mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Authentication with batoto done
This commit is contained in:
		| @@ -5,22 +5,31 @@ import com.squareup.okhttp.CacheControl; | ||||
| import com.squareup.okhttp.Headers; | ||||
| import com.squareup.okhttp.OkHttpClient; | ||||
| import com.squareup.okhttp.Request; | ||||
| import com.squareup.okhttp.RequestBody; | ||||
| import com.squareup.okhttp.Response; | ||||
|  | ||||
| import java.net.CookieManager; | ||||
| import java.net.CookiePolicy; | ||||
| import java.net.CookieStore; | ||||
|  | ||||
| import rx.Observable; | ||||
|  | ||||
| public final class NetworkHelper { | ||||
|  | ||||
|     private OkHttpClient mClient; | ||||
|     private CookieManager cookieManager; | ||||
|  | ||||
|     public final CacheControl NULL_CACHE_CONTROL = new CacheControl.Builder().noCache().build(); | ||||
|     public final Headers NULL_HEADERS = new Headers.Builder().build(); | ||||
|  | ||||
|     public NetworkHelper() { | ||||
|         mClient = new OkHttpClient(); | ||||
|         cookieManager = new CookieManager(); | ||||
|         cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); | ||||
|         mClient.setCookieHandler(cookieManager); | ||||
|     } | ||||
|  | ||||
|     public Observable<Response> getResponse(final String url, final CacheControl cacheControl, final Headers headers) { | ||||
|     public Observable<Response> getResponse(final String url, final Headers headers, final CacheControl cacheControl) { | ||||
|         return Observable.create(subscriber -> { | ||||
|             try { | ||||
|                 if (!subscriber.isUnsubscribed()) { | ||||
| @@ -49,10 +58,32 @@ public final class NetworkHelper { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public Observable<String> getStringResponse(final String url, final CacheControl cacheControl, final Headers headers) { | ||||
|     public Observable<String> getStringResponse(final String url, final Headers headers, final CacheControl cacheControl) { | ||||
|  | ||||
|         return getResponse(url, cacheControl, headers) | ||||
|         return getResponse(url, headers, cacheControl) | ||||
|                 .flatMap(this::mapResponseToString); | ||||
|     } | ||||
|  | ||||
|     public Observable<Response> postData(final String url, final RequestBody formBody, final Headers headers) { | ||||
|         return Observable.create(subscriber -> { | ||||
|             try { | ||||
|                 if (!subscriber.isUnsubscribed()) { | ||||
|                     Request request = new Request.Builder() | ||||
|                             .url(url) | ||||
|                             .post(formBody) | ||||
|                             .headers(headers != null ? headers : NULL_HEADERS) | ||||
|                             .build(); | ||||
|                     subscriber.onNext(mClient.newCall(request).execute()); | ||||
|                 } | ||||
|                 subscriber.onCompleted(); | ||||
|             } catch (Throwable e) { | ||||
|                 subscriber.onError(e); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public CookieStore getCookies() { | ||||
|         return cookieManager.getCookieStore(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package eu.kanade.mangafeed.data.helpers; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| @@ -17,11 +19,11 @@ public class SourceManager { | ||||
|     private HashMap<Integer, Source> mSourcesMap; | ||||
|     private NetworkHelper mNetworkHelper; | ||||
|     private CacheManager mCacheManager; | ||||
|     private Context context; | ||||
|  | ||||
|     public SourceManager(NetworkHelper networkHelper, CacheManager cacheManager) { | ||||
|     public SourceManager(Context context) { | ||||
|         mSourcesMap = new HashMap<>(); | ||||
|         mNetworkHelper = networkHelper; | ||||
|         mCacheManager = cacheManager; | ||||
|         this.context = context; | ||||
|  | ||||
|         initializeSources(); | ||||
|     } | ||||
| @@ -36,9 +38,9 @@ public class SourceManager { | ||||
|     private Source createSource(int sourceKey) { | ||||
|         switch (sourceKey) { | ||||
|             case BATOTO: | ||||
|                 return new Batoto(mNetworkHelper, mCacheManager); | ||||
|                 return new Batoto(context); | ||||
|             case MANGAHERE: | ||||
|                 return new MangaHere(mNetworkHelper, mCacheManager); | ||||
|                 return new MangaHere(context); | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import eu.kanade.mangafeed.presenter.MangaDetailPresenter; | ||||
| import eu.kanade.mangafeed.presenter.MangaInfoPresenter; | ||||
| import eu.kanade.mangafeed.presenter.ReaderPresenter; | ||||
| import eu.kanade.mangafeed.presenter.SourcePresenter; | ||||
| import eu.kanade.mangafeed.sources.base.Source; | ||||
| import eu.kanade.mangafeed.ui.activity.ReaderActivity; | ||||
| import eu.kanade.mangafeed.ui.fragment.SettingsAccountsFragment; | ||||
|  | ||||
| @@ -37,6 +38,8 @@ public interface AppComponent { | ||||
|     void inject(ReaderActivity readerActivity); | ||||
|     void inject(SettingsAccountsFragment settingsAccountsFragment); | ||||
|  | ||||
|     void inject(Source source); | ||||
|  | ||||
|     Application application(); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -55,8 +55,8 @@ public class DataModule { | ||||
|  | ||||
|     @Provides | ||||
|     @Singleton | ||||
|     SourceManager provideSourceManager(NetworkHelper networkHelper, CacheManager cacheManager) { | ||||
|         return new SourceManager(networkHelper, cacheManager); | ||||
|     SourceManager provideSourceManager(Application app) { | ||||
|         return new SourceManager(app); | ||||
|     } | ||||
|  | ||||
|     @Provides | ||||
|   | ||||
| @@ -2,13 +2,16 @@ package eu.kanade.mangafeed.presenter; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.helpers.PreferencesHelper; | ||||
| import eu.kanade.mangafeed.data.helpers.SourceManager; | ||||
| import eu.kanade.mangafeed.sources.base.Source; | ||||
| import eu.kanade.mangafeed.ui.fragment.SourceFragment; | ||||
|  | ||||
|  | ||||
| public class SourcePresenter extends BasePresenter<SourceFragment> { | ||||
|  | ||||
|     @Inject SourceManager sourceManager; | ||||
|     @Inject PreferencesHelper prefs; | ||||
|  | ||||
|     @Override | ||||
|     protected void onTakeView(SourceFragment view) { | ||||
| @@ -17,4 +20,11 @@ public class SourcePresenter extends BasePresenter<SourceFragment> { | ||||
|         view.setItems(sourceManager.getSources()); | ||||
|     } | ||||
|  | ||||
|     public boolean isValidSource(Source source) { | ||||
|         if (!source.isLoginRequired() || source.isLogged()) | ||||
|             return true; | ||||
|  | ||||
|         return !(prefs.getSourceUsername(source).equals("") | ||||
|                 || prefs.getSourcePassword(source).equals("")); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,19 @@ | ||||
| package eu.kanade.mangafeed.sources; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.squareup.okhttp.FormEncodingBuilder; | ||||
| import com.squareup.okhttp.Headers; | ||||
| import com.squareup.okhttp.Response; | ||||
|  | ||||
| import org.jsoup.Jsoup; | ||||
| import org.jsoup.nodes.Document; | ||||
| import org.jsoup.nodes.Element; | ||||
| import org.jsoup.select.Elements; | ||||
|  | ||||
| import java.net.HttpCookie; | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.text.ParseException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| @@ -14,8 +21,6 @@ import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.caches.CacheManager; | ||||
| import eu.kanade.mangafeed.data.helpers.NetworkHelper; | ||||
| import eu.kanade.mangafeed.data.helpers.SourceManager; | ||||
| import eu.kanade.mangafeed.data.models.Chapter; | ||||
| import eu.kanade.mangafeed.data.models.Manga; | ||||
| @@ -25,15 +30,17 @@ import rx.Observable; | ||||
| public class Batoto extends Source { | ||||
|  | ||||
|     public static final String NAME = "Batoto (EN)"; | ||||
|     public static final String BASE_URL = "www.bato.to"; | ||||
|     public static final String BASE_URL = "http://bato.to"; | ||||
|     public static final String INITIAL_UPDATE_URL = | ||||
|             "http://bato.to/search_ajax?order_cond=views&order=desc&p="; | ||||
|     public static final String INITIAL_SEARCH_URL = "http://bato.to/search_ajax?"; | ||||
|     public static final String INITIAL_PAGE_URL = "http://bato.to/areader?"; | ||||
|     public static final String LOGIN_URL = | ||||
|             "https://bato.to/forums/index.php?app=core&module=global§ion=login"; | ||||
|  | ||||
|  | ||||
|     public Batoto(NetworkHelper networkService, CacheManager cacheManager) { | ||||
|         super(networkService, cacheManager); | ||||
|     public Batoto(Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -336,5 +343,61 @@ public class Batoto extends Source { | ||||
|         return imageElement.attr("src"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Observable<Boolean> login(String username, String password) { | ||||
|         return mNetworkService.getStringResponse(LOGIN_URL, mRequestHeaders, null) | ||||
|                 .flatMap(response -> doLogin(response, username, password)) | ||||
|                 .map(this::isAuthenticationSuccessful); | ||||
|     } | ||||
|  | ||||
|     private Observable<Response> doLogin(String response, String username, String password) { | ||||
|         Document doc = Jsoup.parse(response); | ||||
|         Element form = doc.select("#login").first(); | ||||
|         String postUrl = form.attr("action"); | ||||
|  | ||||
|         FormEncodingBuilder formBody = new FormEncodingBuilder(); | ||||
|         Element authKey = form.select("input[name=auth_key").first(); | ||||
|  | ||||
|         formBody.add(authKey.attr("name"), authKey.attr("value")); | ||||
|         formBody.add("ips_username", username); | ||||
|         formBody.add("ips_password", password); | ||||
|         formBody.add("invisible", "1"); | ||||
|         formBody.add("rememberMe", "1"); | ||||
|  | ||||
|         return mNetworkService.postData(postUrl, formBody.build(), mRequestHeaders); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected boolean isAuthenticationSuccessful(Response response) { | ||||
|         return response.priorResponse() != null && response.priorResponse().code() == 302; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isLogged() { | ||||
|         try { | ||||
|             for ( HttpCookie cookie : mNetworkService.getCookies().get(new URI(BASE_URL)) ) { | ||||
|                 if (cookie.getName().equals("pass_hash")) | ||||
|                     return true; | ||||
|             } | ||||
|  | ||||
|         } catch (URISyntaxException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Observable<List<Chapter>> pullChaptersFromNetwork(String mangaUrl) { | ||||
|         Observable<List<Chapter>> observable; | ||||
|         if (!isLogged()) { | ||||
|             observable = login(prefs.getSourceUsername(this), prefs.getSourcePassword(this)) | ||||
|                     .flatMap(result -> super.pullChaptersFromNetwork(mangaUrl)); | ||||
|         } | ||||
|         else { | ||||
|             observable = super.pullChaptersFromNetwork(mangaUrl); | ||||
|         } | ||||
|         return observable; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package eu.kanade.mangafeed.sources; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import org.jsoup.Jsoup; | ||||
| import org.jsoup.nodes.Document; | ||||
| import org.jsoup.nodes.Element; | ||||
| @@ -13,8 +15,6 @@ import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.caches.CacheManager; | ||||
| import eu.kanade.mangafeed.data.helpers.NetworkHelper; | ||||
| import eu.kanade.mangafeed.data.helpers.SourceManager; | ||||
| import eu.kanade.mangafeed.data.models.Chapter; | ||||
| import eu.kanade.mangafeed.data.models.Manga; | ||||
| @@ -29,8 +29,8 @@ public class MangaHere extends Source { | ||||
|     private static final String INITIAL_UPDATE_URL = "http://www.mangahere.co/latest/"; | ||||
|     private static final String INITIAL_SEARCH_URL = "http://www.mangahere.co/search.php?"; | ||||
|  | ||||
|     public MangaHere(NetworkHelper networkService, CacheManager cacheManager) { | ||||
|         super(networkService, cacheManager); | ||||
|     public MangaHere(Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| package eu.kanade.mangafeed.sources.base; | ||||
|  | ||||
| import com.squareup.okhttp.Headers; | ||||
| import com.squareup.okhttp.Response; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.models.Chapter; | ||||
| import eu.kanade.mangafeed.data.models.Manga; | ||||
| import rx.Observable; | ||||
|  | ||||
| public abstract class BaseSource { | ||||
|  | ||||
| @@ -43,6 +45,19 @@ public abstract class BaseSource { | ||||
|     protected abstract String parseHtmlToImageUrl(String unparsedHtml); | ||||
|  | ||||
|  | ||||
|     // Login related methods, shouldn't be overriden if the source doesn't require it | ||||
|     public Observable<Boolean> login(String username, String password) { | ||||
|         throw new UnsupportedOperationException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     public boolean isLogged() { | ||||
|         throw new UnsupportedOperationException("Not implemented"); | ||||
|     } | ||||
|  | ||||
|     protected boolean isAuthenticationSuccessful(Response response) { | ||||
|         throw new UnsupportedOperationException("Not implemented"); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     // Default fields, they can be overriden by sources' implementation | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,19 @@ | ||||
| package eu.kanade.mangafeed.sources.base; | ||||
|  | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.squareup.okhttp.Headers; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
|  | ||||
| import eu.kanade.mangafeed.App; | ||||
| import eu.kanade.mangafeed.data.caches.CacheManager; | ||||
| import eu.kanade.mangafeed.data.helpers.NetworkHelper; | ||||
| import eu.kanade.mangafeed.data.helpers.PreferencesHelper; | ||||
| import eu.kanade.mangafeed.data.models.Chapter; | ||||
| import eu.kanade.mangafeed.data.models.Manga; | ||||
| import eu.kanade.mangafeed.data.models.Page; | ||||
| @@ -16,13 +22,13 @@ import rx.schedulers.Schedulers; | ||||
|  | ||||
| public abstract class Source extends BaseSource { | ||||
|  | ||||
|     protected NetworkHelper mNetworkService; | ||||
|     protected CacheManager mCacheManager; | ||||
|     @Inject protected NetworkHelper mNetworkService; | ||||
|     @Inject protected CacheManager mCacheManager; | ||||
|     @Inject protected PreferencesHelper prefs; | ||||
|     protected Headers mRequestHeaders; | ||||
|  | ||||
|     public Source(NetworkHelper networkService, CacheManager cacheManager) { | ||||
|         mNetworkService = networkService; | ||||
|         mCacheManager = cacheManager; | ||||
|     public Source(Context context) { | ||||
|         App.get(context).getComponent().inject(this); | ||||
|         mRequestHeaders = headersBuilder().build(); | ||||
|     } | ||||
|  | ||||
| @@ -30,28 +36,28 @@ public abstract class Source extends BaseSource { | ||||
|     public Observable<List<Manga>> pullPopularMangasFromNetwork(int page) { | ||||
|         String url = getUrlFromPageNumber(page); | ||||
|         return mNetworkService | ||||
|                 .getStringResponse(url, mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                 .getStringResponse(url, mRequestHeaders, null) | ||||
|                 .flatMap(response -> Observable.just(parsePopularMangasFromHtml(response))); | ||||
|     } | ||||
|  | ||||
|     // Get mangas from the source with a query | ||||
|     public Observable<List<Manga>> searchMangasFromNetwork(String query, int page) { | ||||
|         return mNetworkService | ||||
|                 .getStringResponse(getSearchUrl(query, page), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                 .getStringResponse(getSearchUrl(query, page), mRequestHeaders, null) | ||||
|                 .flatMap(response -> Observable.just(parseSearchFromHtml(response))); | ||||
|     } | ||||
|  | ||||
|     // Get manga details from the source | ||||
|     public Observable<Manga> pullMangaFromNetwork(final String mangaUrl) { | ||||
|         return mNetworkService | ||||
|                 .getStringResponse(overrideMangaUrl(mangaUrl), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                 .getStringResponse(overrideMangaUrl(mangaUrl), mRequestHeaders, null) | ||||
|                 .flatMap(unparsedHtml -> Observable.just(parseHtmlToManga(mangaUrl, unparsedHtml))); | ||||
|     } | ||||
|  | ||||
|     // Get chapter list of a manga from the source | ||||
|     public Observable<List<Chapter>> pullChaptersFromNetwork(String mangaUrl) { | ||||
|         return mNetworkService | ||||
|                 .getStringResponse(mangaUrl, mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                 .getStringResponse(mangaUrl, mRequestHeaders, null) | ||||
|                 .flatMap(unparsedHtml -> | ||||
|                         Observable.just(parseHtmlToChapters(unparsedHtml))); | ||||
|     } | ||||
| @@ -60,7 +66,7 @@ public abstract class Source extends BaseSource { | ||||
|         return mCacheManager.getPageUrlsFromDiskCache(chapterUrl) | ||||
|                 .onErrorResumeNext(throwable -> { | ||||
|                     return mNetworkService | ||||
|                             .getStringResponse(overrideChapterPageUrl(chapterUrl), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                             .getStringResponse(overrideChapterPageUrl(chapterUrl), mRequestHeaders, null) | ||||
|                             .flatMap(unparsedHtml -> { | ||||
|                                 List<String> pageUrls = parseHtmlToPageUrls(unparsedHtml); | ||||
|                                 return Observable.just(getFirstImageFromPageUrls(pageUrls, unparsedHtml)); | ||||
| @@ -82,7 +88,7 @@ public abstract class Source extends BaseSource { | ||||
|  | ||||
|     private Observable<Page> getImageUrlFromPage(final Page page) { | ||||
|         return mNetworkService | ||||
|                 .getStringResponse(overrideRemainingPagesUrl(page.getUrl()), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders) | ||||
|                 .getStringResponse(overrideRemainingPagesUrl(page.getUrl()), mRequestHeaders, null) | ||||
|                 .flatMap(unparsedHtml -> Observable.just(parseHtmlToImageUrl(unparsedHtml))) | ||||
|                 .flatMap(imageUrl -> { | ||||
|                     page.setImageUrl(imageUrl); | ||||
|   | ||||
| @@ -25,6 +25,9 @@ import eu.kanade.mangafeed.data.helpers.SourceManager; | ||||
| import eu.kanade.mangafeed.sources.base.Source; | ||||
| import eu.kanade.mangafeed.ui.activity.base.BaseActivity; | ||||
| import rx.Observable; | ||||
| import rx.android.schedulers.AndroidSchedulers; | ||||
| import rx.schedulers.Schedulers; | ||||
| import timber.log.Timber; | ||||
|  | ||||
| public class SettingsAccountsFragment extends PreferenceFragment { | ||||
|  | ||||
| @@ -112,6 +115,14 @@ public class SettingsAccountsFragment extends PreferenceFragment { | ||||
|                     username.getText().toString(), | ||||
|                     password.getText().toString()); | ||||
|  | ||||
|             source.login(username.getText().toString(), password.getText().toString()) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe(result -> { | ||||
|                         Timber.e("Result is " + result); | ||||
|                     }); | ||||
|  | ||||
|  | ||||
|             super.onDialogClosed(true); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -56,8 +56,12 @@ public class SourceFragment extends BaseRxFragment<SourcePresenter> { | ||||
|     public void onSourceClick(int position) { | ||||
|         Source source = adapter.getItem(position); | ||||
|  | ||||
|         CatalogueFragment fragment = CatalogueFragment.newInstance(source.getSourceId()); | ||||
|         activity.setFragment(fragment); | ||||
|         if (getPresenter().isValidSource(source)) { | ||||
|             CatalogueFragment fragment = CatalogueFragment.newInstance(source.getSourceId()); | ||||
|             activity.setFragment(fragment); | ||||
|         } else { | ||||
|             // TODO ask for password | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void createAdapter() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user