That fuck shit the fascists are using
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}