That fuck shit the fascists are using
at master 181 lines 6.3 kB view raw
1package org.tm.archive.util; 2 3import androidx.annotation.NonNull; 4import androidx.annotation.Nullable; 5import androidx.annotation.WorkerThread; 6 7import org.conscrypt.ConscryptSignal; 8import org.signal.core.util.concurrent.SignalExecutors; 9import org.signal.core.util.logging.Log; 10import org.tm.archive.dependencies.ApplicationDependencies; 11import org.tm.archive.keyvalue.SignalStore; 12import org.tm.archive.push.AccountManagerFactory; 13import org.whispersystems.signalservice.api.SignalServiceAccountManager; 14import org.whispersystems.signalservice.api.push.SignalServiceAddress; 15import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; 16import org.whispersystems.signalservice.internal.configuration.SignalProxy; 17 18import java.io.IOException; 19import java.util.concurrent.CountDownLatch; 20import java.util.concurrent.TimeUnit; 21import java.util.concurrent.atomic.AtomicBoolean; 22import java.util.regex.Matcher; 23import java.util.regex.Pattern; 24 25import io.reactivex.rxjava3.core.Single; 26import io.reactivex.rxjava3.schedulers.Schedulers; 27 28public final class SignalProxyUtil { 29 30 private static final String TAG = Log.tag(SignalProxyUtil.class); 31 32 private static final String PROXY_LINK_HOST = "signal.tube"; 33 34 private static final Pattern PROXY_LINK_PATTERN = Pattern.compile("^(https|sgnl)://" + PROXY_LINK_HOST + "/#([^:]+).*$"); 35 private static final Pattern HOST_PATTERN = Pattern.compile("^([^:]+).*$"); 36 37 private SignalProxyUtil() {} 38 39 public static void startListeningToWebsocket() { 40 if (SignalStore.proxy().isProxyEnabled() && ApplicationDependencies.getSignalWebSocket().getWebSocketState().firstOrError().blockingGet().isFailure()) { 41 Log.w(TAG, "Proxy is in a failed state. Restarting."); 42 ApplicationDependencies.closeConnections(); 43 } 44 45 ApplicationDependencies.getIncomingMessageObserver(); 46 } 47 48 /** 49 * Handles all things related to enabling a proxy, including saving it and resetting the relevant 50 * network connections. 51 */ 52 public static void enableProxy(@NonNull SignalProxy proxy) { 53 SignalStore.proxy().enableProxy(proxy); 54 ConscryptSignal.setUseEngineSocketByDefault(true); 55 ApplicationDependencies.resetAllNetworkConnections(); 56 startListeningToWebsocket(); 57 } 58 59 /** 60 * Handles all things related to disabling a proxy, including saving the change and resetting the 61 * relevant network connections. 62 */ 63 public static void disableProxy() { 64 SignalStore.proxy().disableProxy(); 65 ConscryptSignal.setUseEngineSocketByDefault(false); 66 ApplicationDependencies.resetAllNetworkConnections(); 67 startListeningToWebsocket(); 68 } 69 70 public static void disableAndClearProxy(){ 71 disableProxy(); 72 SignalStore.proxy().setProxy(null); 73 } 74 75 /** 76 * A blocking call that will wait until the websocket either successfully connects, or fails. 77 * It is assumed that the app state is already configured how you would like it, e.g. you've 78 * already configured a proxy if relevant. 79 * 80 * @return True if the connection is successful within the specified timeout, otherwise false. 81 */ 82 @WorkerThread 83 public static boolean testWebsocketConnection(long timeout) { 84 startListeningToWebsocket(); 85 86 if (SignalStore.account().getE164() == null) { 87 Log.i(TAG, "User is unregistered! Doing simple check."); 88 return testWebsocketConnectionUnregistered(timeout); 89 } 90 91 return ApplicationDependencies.getSignalWebSocket() 92 .getWebSocketState() 93 .subscribeOn(Schedulers.trampoline()) 94 .observeOn(Schedulers.trampoline()) 95 .timeout(timeout, TimeUnit.MILLISECONDS) 96 .skipWhile(state -> state != WebSocketConnectionState.CONNECTED && !state.isFailure()) 97 .firstOrError() 98 .flatMap(state -> Single.just(state == WebSocketConnectionState.CONNECTED)) 99 .onErrorReturn(t -> false) 100 .blockingGet(); 101 } 102 103 /** 104 * If this is a valid proxy deep link, this will return the embedded host. If not, it will return 105 * null. 106 */ 107 public static @Nullable String parseHostFromProxyDeepLink(@Nullable String proxyLink) { 108 if (proxyLink == null) { 109 return null; 110 } 111 112 Matcher matcher = PROXY_LINK_PATTERN.matcher(proxyLink); 113 114 if (matcher.matches()) { 115 return matcher.group(2); 116 } else { 117 return null; 118 } 119 } 120 121 /** 122 * Takes in an address that could be in various formats, and converts it to the format we should 123 * be storing and connecting to. 124 */ 125 public static @NonNull String convertUserEnteredAddressToHost(@NonNull String host) { 126 String parsedHost = SignalProxyUtil.parseHostFromProxyDeepLink(host); 127 if (parsedHost != null) { 128 return parsedHost; 129 } 130 131 Matcher matcher = HOST_PATTERN.matcher(host); 132 133 if (matcher.matches()) { 134 String result = matcher.group(1); 135 return result != null ? result : ""; 136 } else { 137 return host; 138 } 139 } 140 141 public static @NonNull String generateProxyUrl(@NonNull String link) { 142 String host = link; 143 String parsed = parseHostFromProxyDeepLink(link); 144 145 if (parsed != null) { 146 host = parsed; 147 } 148 149 Matcher matcher = HOST_PATTERN.matcher(host); 150 151 if (matcher.matches()) { 152 host = matcher.group(1); 153 } 154 155 return "https://" + PROXY_LINK_HOST + "/#" + host; 156 } 157 158 private static boolean testWebsocketConnectionUnregistered(long timeout) { 159 CountDownLatch latch = new CountDownLatch(1); 160 AtomicBoolean success = new AtomicBoolean(false); 161 SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createUnauthenticated(ApplicationDependencies.getApplication(), "", SignalServiceAddress.DEFAULT_DEVICE_ID, ""); 162 163 SignalExecutors.UNBOUNDED.execute(() -> { 164 try { 165 accountManager.checkNetworkConnection(); 166 success.set(true); 167 latch.countDown(); 168 } catch (IOException e) { 169 latch.countDown(); 170 } 171 }); 172 173 try { 174 latch.await(timeout, TimeUnit.MILLISECONDS); 175 } catch (InterruptedException e) { 176 Log.w(TAG, "Interrupted!", e); 177 } 178 179 return success.get(); 180 } 181}