convert RestModule to kt

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-03-06 14:08:00 +01:00
parent 954648459e
commit a576e5f41c
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
2 changed files with 290 additions and 320 deletions

View file

@ -1,320 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.dagger.modules;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.github.aurae.retrofit2.LoganSquareConverterFactory;
import com.nextcloud.talk.BuildConfig;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.LoggingUtils;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.ssl.KeyManager;
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat;
import com.nextcloud.talk.utils.ssl.TrustManager;
import java.io.IOException;
import java.net.CookieManager;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509KeyManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import dagger.Module;
import dagger.Provides;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Authenticator;
import okhttp3.Cache;
import okhttp3.Credentials;
import okhttp3.Dispatcher;
import okhttp3.Interceptor;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
import okhttp3.internal.tls.OkHostnameVerifier;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
@Module(includes = DatabaseModule.class)
public class RestModule {
private static final String TAG = "RestModule";
private final Context context;
public RestModule(Context context) {
this.context = context;
}
@Singleton
@Provides
NcApi provideNcApi(Retrofit retrofit) {
return retrofit.create(NcApi.class);
}
@Singleton
@Provides
Proxy provideProxy(AppPreferences appPreferences) {
if (!TextUtils.isEmpty(appPreferences.getProxyType()) && !"No proxy".equals(appPreferences.getProxyType())
&& !TextUtils.isEmpty(appPreferences.getProxyHost())) {
GetProxyRunnable getProxyRunnable = new GetProxyRunnable(appPreferences);
Thread getProxyThread = new Thread(getProxyRunnable);
getProxyThread.start();
try {
getProxyThread.join();
return getProxyRunnable.getProxyValue();
} catch (InterruptedException e) {
Log.e(TAG, "Failed to join the thread while getting proxy: " + e.getLocalizedMessage());
return Proxy.NO_PROXY;
}
} else {
return Proxy.NO_PROXY;
}
}
@Singleton
@Provides
Retrofit provideRetrofit(OkHttpClient httpClient) {
Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
.client(httpClient)
.baseUrl("https://nextcloud.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(LoganSquareConverterFactory.create());
return retrofitBuilder.build();
}
@Singleton
@Provides
TrustManager provideTrustManager() {
return new TrustManager();
}
@Singleton
@Provides
KeyManager provideKeyManager(AppPreferences appPreferences, UserManager userManager) {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, null);
X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
return new KeyManager(origKm, userManager, appPreferences);
} catch (KeyStoreException e) {
Log.e(TAG, "KeyStoreException " + e.getLocalizedMessage());
} catch (CertificateException e) {
Log.e(TAG, "CertificateException " + e.getLocalizedMessage());
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException " + e.getLocalizedMessage());
} catch (IOException e) {
Log.e(TAG, "IOException " + e.getLocalizedMessage());
} catch (UnrecoverableKeyException e) {
Log.e(TAG, "UnrecoverableKeyException " + e.getLocalizedMessage());
}
return null;
}
@Singleton
@Provides
SSLSocketFactoryCompat provideSslSocketFactoryCompat(KeyManager keyManager, TrustManager
trustManager) {
return new SSLSocketFactoryCompat(keyManager, trustManager);
}
@Singleton
@Provides
CookieManager provideCookieManager() {
return new CookieManager();
}
@Singleton
@Provides
Cache provideCache() {
int cacheSize = 128 * 1024 * 1024; // 128 MB
return new Cache(NextcloudTalkApplication.Companion.getSharedApplication().getCacheDir(), cacheSize);
}
@Singleton
@Provides
Dispatcher provideDispatcher() {
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequestsPerHost(100);
dispatcher.setMaxRequests(100);
return dispatcher;
}
@Singleton
@Provides
OkHttpClient provideHttpClient(Proxy proxy, AppPreferences appPreferences,
TrustManager trustManager,
SSLSocketFactoryCompat sslSocketFactoryCompat, Cache cache,
CookieManager cookieManager, Dispatcher dispatcher) {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.retryOnConnectionFailure(true);
httpClient.connectTimeout(45, TimeUnit.SECONDS);
httpClient.readTimeout(45, TimeUnit.SECONDS);
httpClient.writeTimeout(45, TimeUnit.SECONDS);
httpClient.cookieJar(new JavaNetCookieJar(cookieManager));
httpClient.cache(cache);
// Trust own CA and all self-signed certs
httpClient.sslSocketFactory(sslSocketFactoryCompat, trustManager);
httpClient.retryOnConnectionFailure(true);
httpClient.hostnameVerifier(trustManager.getHostnameVerifier(OkHostnameVerifier.INSTANCE));
httpClient.dispatcher(dispatcher);
if (!Proxy.NO_PROXY.equals(proxy)) {
httpClient.proxy(proxy);
if (appPreferences.getProxyCredentials() &&
!TextUtils.isEmpty(appPreferences.getProxyUsername()) &&
!TextUtils.isEmpty(appPreferences.getProxyPassword())) {
httpClient.proxyAuthenticator(new HttpAuthenticator(
Credentials.basic(
appPreferences.getProxyUsername(),
appPreferences.getProxyPassword(),
StandardCharsets.UTF_8),
"Proxy-Authorization"));
}
}
httpClient.addInterceptor(new HeadersInterceptor());
if (BuildConfig.DEBUG && !context.getResources().getBoolean(R.bool.nc_is_debug)) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
loggingInterceptor.redactHeader("Authorization");
loggingInterceptor.redactHeader("Proxy-Authorization");
httpClient.addInterceptor(loggingInterceptor);
} else if (context.getResources().getBoolean(R.bool.nc_is_debug)) {
HttpLoggingInterceptor.Logger fileLogger =
s -> LoggingUtils.INSTANCE.writeLogEntryToFile(context, s);
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(fileLogger);
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
loggingInterceptor.redactHeader("Authorization");
loggingInterceptor.redactHeader("Proxy-Authorization");
httpClient.addInterceptor(loggingInterceptor);
}
return httpClient.build();
}
public static class HeadersInterceptor implements Interceptor {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", ApiUtils.getUserAgent())
.header("Accept", "application/json")
.header("OCS-APIRequest", "true")
.header("ngrok-skip-browser-warning", "true")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
public static class HttpAuthenticator implements Authenticator {
private String credentials;
private String authenticatorType;
public HttpAuthenticator(@NonNull String credentials, @NonNull String authenticatorType) {
this.credentials = credentials;
this.authenticatorType = authenticatorType;
}
@Nullable
@Override
public Request authenticate(@Nullable Route route, @NonNull Response response) {
if (response.request().header(authenticatorType) != null) {
return null;
}
Response countedResponse = response;
int attemptsCount = 0;
while ((countedResponse = countedResponse.priorResponse()) != null) {
attemptsCount++;
if (attemptsCount == 3) {
return null;
}
}
return response.request().newBuilder()
.header(authenticatorType, credentials)
.build();
}
}
private class GetProxyRunnable implements Runnable {
private volatile Proxy proxy;
private AppPreferences appPreferences;
GetProxyRunnable(AppPreferences appPreferences) {
this.appPreferences = appPreferences;
}
@Override
public void run() {
if (Proxy.Type.valueOf(appPreferences.getProxyType()) == Proxy.Type.SOCKS) {
proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()),
InetSocketAddress.createUnresolved(appPreferences.getProxyHost(), Integer.parseInt(
appPreferences.getProxyPort())));
} else {
proxy = new Proxy(Proxy.Type.valueOf(appPreferences.getProxyType()),
new InetSocketAddress(appPreferences.getProxyHost(),
Integer.parseInt(appPreferences.getProxyPort())));
}
}
Proxy getProxyValue() {
return proxy;
}
}
}

View file

@ -0,0 +1,290 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.dagger.modules
import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.github.aurae.retrofit2.LoganSquareConverterFactory
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils.userAgent
import com.nextcloud.talk.utils.LoggingUtils.writeLogEntryToFile
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.nextcloud.talk.utils.ssl.KeyManager
import com.nextcloud.talk.utils.ssl.SSLSocketFactoryCompat
import com.nextcloud.talk.utils.ssl.TrustManager
import dagger.Module
import dagger.Provides
import io.reactivex.schedulers.Schedulers
import okhttp3.Authenticator
import okhttp3.Cache
import okhttp3.Credentials.basic
import okhttp3.Dispatcher
import okhttp3.Interceptor
import okhttp3.Interceptor.Chain
import okhttp3.JavaNetCookieJar
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.Route
import okhttp3.internal.tls.OkHostnameVerifier
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import java.io.IOException
import java.net.CookieManager
import java.net.InetSocketAddress
import java.net.Proxy
import java.nio.charset.StandardCharsets
import java.security.KeyStore
import java.security.KeyStoreException
import java.security.NoSuchAlgorithmException
import java.security.UnrecoverableKeyException
import java.security.cert.CertificateException
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.X509KeyManager
import kotlin.concurrent.Volatile
@Module(includes = [DatabaseModule::class])
class RestModule(private val context: Context) {
@Singleton
@Provides
fun provideNcApi(retrofit: Retrofit): NcApi {
return retrofit.create(NcApi::class.java)
}
@Singleton
@Provides
fun provideProxy(appPreferences: AppPreferences): Proxy? {
return if (!TextUtils.isEmpty(appPreferences.getProxyType()) && "No proxy" != appPreferences.getProxyType() &&
!TextUtils.isEmpty(appPreferences.getProxyHost())
) {
val getProxyRunnable = GetProxyRunnable(appPreferences)
val getProxyThread = Thread(getProxyRunnable)
getProxyThread.start()
try {
getProxyThread.join()
getProxyRunnable.proxyValue
} catch (e: InterruptedException) {
Log.e(TAG, "Failed to join the thread while getting proxy: " + e.localizedMessage)
Proxy.NO_PROXY
}
} else {
Proxy.NO_PROXY
}
}
@Singleton
@Provides
fun provideRetrofit(httpClient: OkHttpClient?): Retrofit {
val retrofitBuilder = Retrofit.Builder()
.client(httpClient!!)
.baseUrl("https://nextcloud.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(LoganSquareConverterFactory.create())
return retrofitBuilder.build()
}
@Singleton
@Provides
fun provideTrustManager(): TrustManager {
return TrustManager()
}
@Singleton
@Provides
fun provideKeyManager(appPreferences: AppPreferences?, userManager: UserManager?): KeyManager? {
val keyStore: KeyStore?
try {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, null)
val origKm = kmf.keyManagers[0] as X509KeyManager
return KeyManager(origKm, userManager, appPreferences)
} catch (e: KeyStoreException) {
Log.e(TAG, "KeyStoreException " + e.localizedMessage)
} catch (e: CertificateException) {
Log.e(TAG, "CertificateException " + e.localizedMessage)
} catch (e: NoSuchAlgorithmException) {
Log.e(TAG, "NoSuchAlgorithmException " + e.localizedMessage)
} catch (e: IOException) {
Log.e(TAG, "IOException " + e.localizedMessage)
} catch (e: UnrecoverableKeyException) {
Log.e(TAG, "UnrecoverableKeyException " + e.localizedMessage)
}
return null
}
@Singleton
@Provides
fun provideSslSocketFactoryCompat(keyManager: KeyManager?, trustManager: TrustManager?): SSLSocketFactoryCompat {
return SSLSocketFactoryCompat(keyManager, trustManager!!)
}
@Singleton
@Provides
fun provideCookieManager(): CookieManager {
return CookieManager()
}
@Singleton
@Provides
fun provideCache(): Cache {
val cacheSize = 128 * 1024 * 1024 // 128 MB
return Cache(sharedApplication!!.cacheDir, cacheSize.toLong())
}
@Singleton
@Provides
fun provideDispatcher(): Dispatcher {
val dispatcher = Dispatcher()
dispatcher.maxRequestsPerHost = 100
dispatcher.maxRequests = 100
return dispatcher
}
@Singleton
@Provides
fun provideHttpClient(
proxy: Proxy?,
appPreferences: AppPreferences,
trustManager: TrustManager,
sslSocketFactoryCompat: SSLSocketFactoryCompat?,
cache: Cache?,
cookieManager: CookieManager?,
dispatcher: Dispatcher?
): OkHttpClient {
val httpClient = OkHttpClient.Builder()
httpClient.retryOnConnectionFailure(true)
httpClient.connectTimeout(45, TimeUnit.SECONDS)
httpClient.readTimeout(45, TimeUnit.SECONDS)
httpClient.writeTimeout(45, TimeUnit.SECONDS)
httpClient.cookieJar(JavaNetCookieJar(cookieManager!!))
httpClient.cache(cache)
// Trust own CA and all self-signed certs
httpClient.sslSocketFactory(sslSocketFactoryCompat!!, trustManager)
httpClient.retryOnConnectionFailure(true)
httpClient.hostnameVerifier(trustManager.getHostnameVerifier(OkHostnameVerifier))
httpClient.dispatcher(dispatcher!!)
if (Proxy.NO_PROXY != proxy) {
httpClient.proxy(proxy)
if (appPreferences.getProxyCredentials() &&
!TextUtils.isEmpty(appPreferences.getProxyUsername()) &&
!TextUtils.isEmpty(appPreferences.getProxyPassword())
) {
httpClient.proxyAuthenticator(
HttpAuthenticator(
basic(
appPreferences.getProxyUsername(),
appPreferences.getProxyPassword(),
StandardCharsets.UTF_8
),
"Proxy-Authorization"
)
)
}
}
httpClient.addInterceptor(HeadersInterceptor())
if (BuildConfig.DEBUG && !context.resources.getBoolean(R.bool.nc_is_debug)) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
loggingInterceptor.redactHeader("Authorization")
loggingInterceptor.redactHeader("Proxy-Authorization")
httpClient.addInterceptor(loggingInterceptor)
} else if (context.resources.getBoolean(R.bool.nc_is_debug)) {
val fileLogger = HttpLoggingInterceptor.Logger { s: String? -> writeLogEntryToFile(context, s!!) }
val loggingInterceptor = HttpLoggingInterceptor(fileLogger)
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
loggingInterceptor.redactHeader("Authorization")
loggingInterceptor.redactHeader("Proxy-Authorization")
httpClient.addInterceptor(loggingInterceptor)
}
return httpClient.build()
}
class HeadersInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Chain): Response {
val original: Request = chain.request()
val request = original.newBuilder()
.header("User-Agent", userAgent)
.header("Accept", "application/json")
.header("OCS-APIRequest", "true")
.header("ngrok-skip-browser-warning", "true")
.method(original.method, original.body)
.build()
return chain.proceed(request)
}
}
class HttpAuthenticator(private val credentials: String, private val authenticatorType: String) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.request.header(authenticatorType) != null) {
return null
}
var countedResponse = response
var attemptsCount = 0
while (countedResponse.priorResponse.also { countedResponse = it!! } != null) {
attemptsCount++
if (attemptsCount == 3) {
return null
}
}
return response.request.newBuilder()
.header(authenticatorType, credentials)
.build()
}
}
private inner class GetProxyRunnable(private val appPreferences: AppPreferences) : Runnable {
@Volatile
var proxyValue: Proxy? = null
private set
override fun run() {
proxyValue = if (Proxy.Type.valueOf(appPreferences.getProxyType()) == Proxy.Type.SOCKS) {
Proxy(
Proxy.Type.valueOf(appPreferences.getProxyType()),
InetSocketAddress.createUnresolved(
appPreferences.getProxyHost(),
appPreferences.getProxyPort().toInt()
)
)
} else {
Proxy(
Proxy.Type.valueOf(appPreferences.getProxyType()),
InetSocketAddress(appPreferences.getProxyHost(), appPreferences.getProxyPort().toInt())
)
}
}
}
companion object {
private const val TAG = "RestModule"
}
}