From 999d3a23a0b0311d38710b742dff456a2103675a Mon Sep 17 00:00:00 2001 From: Sujit Kumar <60378235+therealsujitk@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:02:28 +0530 Subject: [PATCH] Fetch authorised user agent from about.json (Fix #109) --- .../vtopchennai/activities/LoginActivity.java | 2 +- .../vtopchennai/activities/MainActivity.java | 2 +- .../helpers/SettingsRepository.java | 4 +- .../vtopchennai/services/VTOPService.java | 67 +++++++++++++++++-- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java b/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java index 90962c6..86f3ab1 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java @@ -110,7 +110,7 @@ public void onComplete() { /* Check for updates */ - SettingsRepository.checkForUpdates() + SettingsRepository.fetchAboutJson(true) .subscribe(new Observer() { @Override public void onSubscribe(@NonNull Disposable d) { diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java b/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java index 8370ebe..c7ec1b9 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java @@ -361,7 +361,7 @@ public void onComplete() { Check for updates */ Context context = this; - SettingsRepository.checkForUpdates() + SettingsRepository.fetchAboutJson(true) .subscribe(new Observer() { @Override public void onSubscribe(@NonNull Disposable d) { diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java b/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java index 2add381..893fc2c 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java @@ -203,11 +203,11 @@ public static boolean hasFileWritePermission(Context context) { return hasPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE); } - public static Observable checkForUpdates() { + public static Observable fetchAboutJson(boolean useVersion) { return Observable.fromCallable(() -> { try { StringBuilder sb = new StringBuilder(); - URL url = new URL(SettingsRepository.APP_ABOUT_URL + "?v=" + BuildConfig.VERSION_NAME); + URL url = new URL(SettingsRepository.APP_ABOUT_URL + (useVersion ? "?v=" + BuildConfig.VERSION_NAME : "")); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); InputStream in = httpURLConnection.getInputStream(); InputStreamReader reader = new InputStreamReader(in); diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/services/VTOPService.java b/app/src/main/java/tk/therealsuji/vtopchennai/services/VTOPService.java index d626439..44ff306 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/services/VTOPService.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/services/VTOPService.java @@ -49,6 +49,7 @@ import io.reactivex.rxjava3.core.CompletableObserver; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import tk.therealsuji.vtopchennai.R; @@ -95,6 +96,7 @@ public class VTOPService extends Service { Map theorySlots, labSlots, projectSlots; Map semesters; String username, password, semesterID; + CompositeDisposable compositeDisposable; public void clearCallback() { this.callback = null; @@ -119,10 +121,11 @@ public void onCreate() { this.notification.setOngoing(true); this.notification.setProgress(0, 0, true); - this.createWebView(); - this.appDatabase = AppDatabase.getInstance(getApplicationContext()); this.sharedPreferences = SettingsRepository.getSharedPreferences(getApplicationContext()); + this.compositeDisposable = new CompositeDisposable(); + + this.createWebView(); } @Nullable @@ -156,17 +159,23 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } + @Override + public void onDestroy() { + this.compositeDisposable.dispose(); + super.onDestroy(); + } + /** * Function to create a fresh WebView */ @SuppressLint("SetJavaScriptEnabled") private void createWebView() { + String authorisedUserAgent = this.sharedPreferences.getString("authorisedUserAgent", null); + this.webView = new WebView(getApplicationContext()); this.webView.addJavascriptInterface(this, "Android"); this.webView.getSettings().setJavaScriptEnabled(true); - // VTOP doesn't support using an old user agent (#105), if you face issues such as "You are not Authorised" or "This browser is not supported", - // try updating the string below with the latest user agent - https://www.whatismybrowser.com/guides/the-latest-user-agent/android - this.webView.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.119 Mobile Safari/537.36"); + this.webView.getSettings().setUserAgentString(authorisedUserAgent); this.webView.setBackgroundColor(Color.TRANSPARENT); this.webView.setHorizontalScrollBarEnabled(false); this.webView.setVerticalScrollBarEnabled(false); @@ -234,6 +243,39 @@ public void onPageFinished(WebView view, String url) { }); } + /** + * VTOP randomly blocks user agents (I'm guessing to prevent people from using this app). + * If a user agent is blocked, a new authorised one is fetched from my server and stored in shared preferences. + */ + private void updateUserAgent() { + SettingsRepository.fetchAboutJson(false) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + compositeDisposable.add(d); + } + + @Override + public void onNext(@NonNull JSONObject about) { + try { + String authorisedUserAgent = about.getString("authorisedUserAgent"); + sharedPreferences.edit().putString("authorisedUserAgent", authorisedUserAgent).apply(); + webView.getSettings().setUserAgentString(authorisedUserAgent); + } catch (Exception ignored) { + } + } + + @Override + public void onError(@NonNull Throwable e) { + } + + @Override + public void onComplete() { + reloadPage("/login", true); + } + }); + } + /** * Function to brutally destroy the WebView */ @@ -598,7 +640,10 @@ private void getSemesters() { " data : data," + " async: false," + " success: function(res) {" + - " if(res.toLowerCase().includes('time table')) {" + + " if (res.toLowerCase().includes('not authorized')) {" + + " response.error_code = 1;" + + " response.error_message = 'Unauthorised user agent';" + + " } else if (res.toLowerCase().includes('time table')) {" + " var doc = new DOMParser().parseFromString(res, 'text/html');" + " var options = doc.getElementById('semesterSubId').getElementsByTagName('option');" + " var semesters = [];" + @@ -620,6 +665,15 @@ private void getSemesters() { "})();", responseString -> { try { JSONObject response = new JSONObject(responseString); + + if (response.has("error_code")) { + if (response.getInt("error_code") == 1) { + Toast.makeText(getApplicationContext(), "Error " + 107 + ". Unauthorised user agent, attempting to update. Report a bug if this issue prevails.", Toast.LENGTH_SHORT).show(); + updateUserAgent(); + return; + } + } + JSONArray semesterArray = response.getJSONArray("semesters"); this.semesters = new HashMap<>(); @@ -2542,6 +2596,7 @@ private enum PageState {LANDING, LOGIN, HOME} * Error 104 Error getting captcha type * Error 105 Error getting default captcha image * Error 106 Error while attempting to sign in + * Error 107 Unauthorised user agent used * * Error 201 Error fetching list of semesters *