Allow insecure connections

This commit is contained in:
Stefan Schüller 2020-11-22 13:54:54 +01:00
parent 6218191dd4
commit 86cfac15d5
18 changed files with 180 additions and 19 deletions

View File

@ -93,6 +93,7 @@ android {
implementation "com.google.android.exoplayer:exoplayer-hls:$libVersions.exoplayer" implementation "com.google.android.exoplayer:exoplayer-hls:$libVersions.exoplayer"
implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$libVersions.exoplayer" implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$libVersions.exoplayer"
implementation "com.google.android.exoplayer:extension-mediasession:$libVersions.exoplayer" implementation "com.google.android.exoplayer:extension-mediasession:$libVersions.exoplayer"
implementation "com.google.android.exoplayer:extension-okhttp:$libVersions.exoplayer"
// date formatter // date formatter
implementation 'org.ocpsoft.prettytime:prettytime:4.0.4.Final' implementation 'org.ocpsoft.prettytime:prettytime:4.0.4.Final'

View File

@ -97,7 +97,7 @@ public class AccountActivity extends CommonActivity {
apiBaseURL = APIUrlHelper.getUrlWithVersion(this); apiBaseURL = APIUrlHelper.getUrlWithVersion(this);
userService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetUserService.class); userService = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetUserService.class);
recyclerViewVideos = findViewById(R.id.account_video_recyclerView); recyclerViewVideos = findViewById(R.id.account_video_recyclerView);
recyclerViewChannels = findViewById(R.id.account_channel_recyclerView); recyclerViewChannels = findViewById(R.id.account_channel_recyclerView);
@ -227,7 +227,7 @@ public class AccountActivity extends CommonActivity {
isLoadingVideos = false; isLoadingVideos = false;
GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetVideoDataService.class);
Call<VideoList> call; Call<VideoList> call;
call = service.getAccountVideosData(displayNameAndHost, videosStart, videosCount, videosSort); call = service.getAccountVideosData(displayNameAndHost, videosStart, videosCount, videosSort);

View File

@ -119,7 +119,7 @@ public class MeActivity extends CommonActivity {
String apiBaseURL = APIUrlHelper.getUrlWithVersion(this); String apiBaseURL = APIUrlHelper.getUrlWithVersion(this);
String baseURL = APIUrlHelper.getUrl(this); String baseURL = APIUrlHelper.getUrl(this);
GetUserService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetUserService.class); GetUserService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetUserService.class);
Call<Me> call = service.getMe(); Call<Me> call = service.getMe();

View File

@ -148,7 +148,7 @@ public class SearchServerActivity extends CommonActivity {
GetServerListDataService service = RetrofitInstance.getRetrofitInstance( GetServerListDataService service = RetrofitInstance.getRetrofitInstance(
APIUrlHelper.getServerIndexUrl(SearchServerActivity.this) APIUrlHelper.getServerIndexUrl(SearchServerActivity.this)
).create(GetServerListDataService.class); , APIUrlHelper.useInsecureConnection(this)).create(GetServerListDataService.class);
if ( !searchtext.equals( lastSearchtext ) ) if ( !searchtext.equals( lastSearchtext ) )
{ {

View File

@ -17,14 +17,18 @@
*/ */
package net.schueller.peertube.activity; package net.schueller.peertube.activity;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.preference.PreferenceManager;
import android.util.Log;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import net.schueller.peertube.BuildConfig; import net.schueller.peertube.BuildConfig;
import net.schueller.peertube.R; import net.schueller.peertube.R;
@ -68,6 +72,43 @@ public class SettingsActivity extends CommonActivity {
assert pref != null; assert pref != null;
pref.setSummary(Long.toString(BuildConfig.BUILD_TIME)); pref.setSummary(Long.toString(BuildConfig.BUILD_TIME));
// double check disabling SSL
final SwitchPreference insecure = (SwitchPreference) findPreference("pref_accept_insecure");
if (insecure != null) {
insecure.setOnPreferenceChangeListener((preference, newValue) -> {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sharedPref.edit();
boolean currentValue = sharedPref.getBoolean("pref_accept_insecure", false);
if (newValue instanceof Boolean && ((Boolean) newValue) != currentValue) {
final boolean enable = (Boolean) newValue;
Log.v("pref", "enable: " + enable);
Log.v("pref", "currentValue: " + currentValue);
if (enable) {
new Builder(preference.getContext())
.setTitle(R.string.pref_insecure_confirm_title)
.setMessage(R.string.pref_insecure_confirm_message)
.setIcon(R.drawable.ic_info_black_24dp)
.setNegativeButton(R.string.pref_insecure_confirm_no, (dialog, whichButton) -> {
// do nothing
})
.setPositiveButton(R.string.pref_insecure_confirm_yes, (dialog, whichButton) -> {
// OK has been pressed => force the new value and update the checkbox display
editor.putBoolean("pref_accept_insecure", true);
editor.apply();
insecure.setChecked(true);
}).create().show();
// by default ignore the pref change, which can only be validated when OK is pressed
return false;
}
}
return true;
});
}
} }
} }
} }

View File

@ -325,13 +325,13 @@ public class VideoListActivity extends CommonActivity {
Set<String> languages = sharedPref.getStringSet(getString(R.string.pref_video_language_key), null); Set<String> languages = sharedPref.getStringSet(getString(R.string.pref_video_language_key), null);
String apiBaseURL = APIUrlHelper.getUrlWithVersion(this); String apiBaseURL = APIUrlHelper.getUrlWithVersion(this);
GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetVideoDataService.class);
Call<VideoList> call; Call<VideoList> call;
if (!searchQuery.equals("")) { if (!searchQuery.equals("")) {
call = service.searchVideosData(start, count, sort, nsfw, searchQuery, filter, languages); call = service.searchVideosData(start, count, sort, nsfw, searchQuery, filter, languages);
} else if (subscriptions) { } else if (subscriptions) {
GetUserService userService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetUserService.class); GetUserService userService = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(this)).create(GetUserService.class);
call = userService.getVideosSubscripions(start, count, sort); call = userService.getVideosSubscripions(start, count, sort);
} else { } else {
call = service.getVideosData(start, count, sort, nsfw, filter, languages); call = service.getVideosData(start, count, sort, nsfw, filter, languages);

View File

@ -100,7 +100,7 @@ public class VideoMetaDataFragment extends Fragment {
Activity activity = getActivity(); Activity activity = getActivity();
String apiBaseURL = APIUrlHelper.getUrlWithVersion(context); String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
GetVideoDataService videoDataService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); GetVideoDataService videoDataService = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(GetVideoDataService.class);
// Thumbs up // Thumbs up
Button thumbsUpButton = activity.findViewById(R.id.video_thumbs_up); Button thumbsUpButton = activity.findViewById(R.id.video_thumbs_up);
@ -326,7 +326,7 @@ public class VideoMetaDataFragment extends Fragment {
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), "{\"rating\":\"" + ratePayload + "\"}"); RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json"), "{\"rating\":\"" + ratePayload + "\"}");
String apiBaseURL = APIUrlHelper.getUrlWithVersion(getContext()); String apiBaseURL = APIUrlHelper.getUrlWithVersion(getContext());
GetVideoDataService videoDataService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); GetVideoDataService videoDataService = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(getContext())).create(GetVideoDataService.class);
Call<ResponseBody> call = videoDataService.rateVideo(video.getId(), body); Call<ResponseBody> call = videoDataService.rateVideo(video.getId(), body);

View File

@ -184,7 +184,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
// get video details from api // get video details from api
String apiBaseURL = APIUrlHelper.getUrlWithVersion(context); String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetVideoDataService.class); GetVideoDataService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(GetVideoDataService.class);
Call<Video> call = service.getVideoData(mVideoUuid); Call<Video> call = service.getVideoData(mVideoUuid);

View File

@ -43,6 +43,11 @@ public class APIUrlHelper{
return APIUrlHelper.getUrl(context) + "/api/v1/"; return APIUrlHelper.getUrl(context) + "/api/v1/";
} }
public static Boolean useInsecureConnection(Context context) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
return sharedPref.getBoolean(context.getString(R.string.pref_accept_insecure), false);
}
public static String getShareUrl(Context context, String videoUuid) { public static String getShareUrl(Context context, String videoUuid) {
return APIUrlHelper.getUrl(context) + "/videos/watch/" + videoUuid; return APIUrlHelper.getUrl(context) + "/videos/watch/" + videoUuid;
} }

View File

@ -17,6 +17,11 @@
*/ */
package net.schueller.peertube.network; package net.schueller.peertube.network;
import static net.schueller.peertube.network.UnsafeOkHttpClient.getUnsafeOkHttpClientBuilder;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import net.schueller.peertube.R;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Retrofit; import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory; import retrofit2.converter.gson.GsonConverterFactory;
@ -26,11 +31,17 @@ public class RetrofitInstance {
private static Retrofit retrofit; private static Retrofit retrofit;
private static String baseUrl; private static String baseUrl;
public static Retrofit getRetrofitInstance(String newBaseUrl) { public static Retrofit getRetrofitInstance(String newBaseUrl, boolean insecure) {
if (retrofit == null || !newBaseUrl.equals(baseUrl)) { if (retrofit == null || !newBaseUrl.equals(baseUrl)) {
baseUrl = newBaseUrl; baseUrl = newBaseUrl;
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder(); OkHttpClient.Builder okhttpClientBuilder;
if (!insecure) {
okhttpClientBuilder = new OkHttpClient.Builder();
} else {
okhttpClientBuilder = getUnsafeOkHttpClientBuilder();
}
okhttpClientBuilder.addInterceptor(new AuthorizationInterceptor()); okhttpClientBuilder.addInterceptor(new AuthorizationInterceptor());
okhttpClientBuilder.authenticator(new AccessTokenAuthenticator()); okhttpClientBuilder.authenticator(new AccessTokenAuthenticator());

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 Stefan Schüller <sschueller@techdroid.com>
*
* License: GPL-3.0+
* 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 net.schueller.peertube.network;
import android.annotation.SuppressLint;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
// Take from https://stackoverflow.com/questions/25509296/trusting-all-certificates-with-okhttp/25992879#25992879
public class UnsafeOkHttpClient {
public static Builder getUnsafeOkHttpClientBuilder() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -> true);
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -50,7 +50,7 @@ public class LoginService {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
AuthenticationService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(AuthenticationService.class); AuthenticationService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(AuthenticationService.class);
Call<OauthClient> call = service.getOauthClientLocal(); Call<OauthClient> call = service.getOauthClientLocal();
@ -134,7 +134,7 @@ public class LoginService {
String apiBaseURL = APIUrlHelper.getUrlWithVersion(context); String apiBaseURL = APIUrlHelper.getUrlWithVersion(context);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
AuthenticationService service = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(AuthenticationService.class); AuthenticationService service = RetrofitInstance.getRetrofitInstance(apiBaseURL, APIUrlHelper.useInsecureConnection(context)).create(AuthenticationService.class);
String refreshToken = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_token_refresh), null); String refreshToken = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_token_refresh), null);
String userName = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_username), null); String userName = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_username), null);

View File

@ -51,6 +51,8 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator; import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource;
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerNotificationManager; import com.google.android.exoplayer2.ui.PlayerNotificationManager;
@ -60,13 +62,16 @@ import com.google.android.exoplayer2.util.Util;
import net.schueller.peertube.R; import net.schueller.peertube.R;
import net.schueller.peertube.activity.VideoPlayActivity; import net.schueller.peertube.activity.VideoPlayActivity;
import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.helper.MetaDataHelper; import net.schueller.peertube.helper.MetaDataHelper;
import net.schueller.peertube.model.Video; import net.schueller.peertube.model.Video;
import okhttp3.OkHttpClient;
import static android.media.session.PlaybackState.ACTION_PAUSE; import static android.media.session.PlaybackState.ACTION_PAUSE;
import static android.media.session.PlaybackState.ACTION_PLAY; import static android.media.session.PlaybackState.ACTION_PLAY;
import static com.google.android.exoplayer2.ui.PlayerNotificationManager.ACTION_STOP; import static com.google.android.exoplayer2.ui.PlayerNotificationManager.ACTION_STOP;
import static net.schueller.peertube.activity.VideoListActivity.EXTRA_VIDEOID; import static net.schueller.peertube.activity.VideoListActivity.EXTRA_VIDEOID;
import static net.schueller.peertube.network.UnsafeOkHttpClient.getUnsafeOkHttpClientBuilder;
public class VideoPlayerService extends Service { public class VideoPlayerService extends Service {
@ -209,8 +214,18 @@ public class VideoPlayerService extends Service {
Log.v(TAG, "playVideo..."); Log.v(TAG, "playVideo...");
// Produces DataSource instances through which media data is loaded. // Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(), // DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(),
Util.getUserAgent(getApplicationContext(), "PeerTube"), null); // Util.getUserAgent(getApplicationContext(), "PeerTube"), null);
OkHttpClient.Builder okhttpClientBuilder;
if (!APIUrlHelper.useInsecureConnection(this)) {
okhttpClientBuilder = new OkHttpClient.Builder();
} else {
okhttpClientBuilder = getUnsafeOkHttpClientBuilder();
}
DataSource.Factory dataSourceFactory = new OkHttpDataSourceFactory(okhttpClientBuilder.build(), Util.getUserAgent(getApplicationContext(), "PeerTube"));
// This is the MediaSource representing the media to be played. // This is the MediaSource representing the media to be played.
ExtractorMediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory) ExtractorMediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)

View File

@ -15,6 +15,8 @@
<string name="pref_background_behavior_key" translatable="false">pref_background_behavior</string> <string name="pref_background_behavior_key" translatable="false">pref_background_behavior</string>
<string name="pref_torrent_player_key" translatable="false">pref_torrent_player</string> <string name="pref_torrent_player_key" translatable="false">pref_torrent_player</string>
<string name="pref_accept_insecure" translatable="false">pref_accept_insecure</string>
<!-- defaults --> <!-- defaults -->
<string name="pref_default_api_base_url" formatted="false" translatable="false">https://troll.tv</string> <string name="pref_default_api_base_url" formatted="false" translatable="false">https://troll.tv</string>
<string name="app_default_theme" translatable="false">AppTheme.BLUE</string> <string name="app_default_theme" translatable="false">AppTheme.BLUE</string>

View File

@ -67,6 +67,8 @@
<string name="pref_background_behavior_summary">How a playing video responds when going to background</string> <string name="pref_background_behavior_summary">How a playing video responds when going to background</string>
<string name="clear_search_history">Clear Search History</string> <string name="clear_search_history">Clear Search History</string>
<string name="clear_search_history_prompt">Do you want to permanently delete the search history?</string> <string name="clear_search_history_prompt">Do you want to permanently delete the search history?</string>
<string name="pref_description_accept_insecure">Ignore insecure connections. Use this only if you know the server you are connecting to. Requires App Restart.</string>
<string name="pref_title_accept_insecure">Disable SSL Certificate Check</string>
<!-- languages --> <!-- languages -->
<string name="ab">Abkhazian</string> <string name="ab">Abkhazian</string>
<string name="aa">Afar</string> <string name="aa">Afar</string>
@ -353,4 +355,9 @@
<string name="pref_title_buildtime">Build Time</string> <string name="pref_title_buildtime">Build Time</string>
<string name="authentication_token_refresh_failed">Could not refresh token</string> <string name="authentication_token_refresh_failed">Could not refresh token</string>
<string name="authentication_token_refresh_success">Token refreshed</string> <string name="authentication_token_refresh_success">Token refreshed</string>
<string name="settings_activity_advanced_category_title">Advanced</string>
<string name="pref_insecure_confirm_title">Warning!</string>
<string name="pref_insecure_confirm_no">No</string>
<string name="pref_insecure_confirm_yes">Yes</string>
<string name="pref_insecure_confirm_message">You are about the disable all SSL Certification validation in Thorium. Disabling this can be very dangerous if the peertube server is not under your control, because a man-in-the-middle attack could direct traffic to another server without your knowledge. An attacker could record passwords and other personal data.</string>
</resources> </resources>

View File

@ -77,6 +77,17 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/settings_activity_advanced_category_title" app:iconSpaceReserved="false">
<SwitchPreference
app:defaultValue="false"
app:key="@string/pref_accept_insecure"
app:summary="@string/pref_description_accept_insecure"
app:title="@string/pref_title_accept_insecure"
app:iconSpaceReserved="false"/>
</PreferenceCategory>
<PreferenceCategory app:title="@string/settings_activity_about_category_title" app:iconSpaceReserved="false"> <PreferenceCategory app:title="@string/settings_activity_about_category_title" app:iconSpaceReserved="false">
<Preference <Preference

View File

@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.android.tools.build:gradle:4.1.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View File

@ -1,6 +1,6 @@
#Fri Jun 12 19:07:10 CEST 2020 #Sun Nov 22 12:39:32 CET 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip