Conversations/src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java

278 lines
11 KiB
Java
Raw Normal View History

2018-10-19 20:43:38 +02:00
package eu.siacs.conversations.services;
2018-10-19 23:29:17 +02:00
import android.content.SharedPreferences;
2018-10-22 20:08:05 +02:00
import android.os.SystemClock;
import android.preference.PreferenceManager;
2018-10-19 23:29:17 +02:00
import android.util.Log;
2018-10-22 20:08:05 +02:00
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
2018-10-20 16:37:59 +02:00
import java.security.SecureRandom;
2018-10-19 23:29:17 +02:00
import java.util.Collections;
import java.util.Locale;
2018-10-19 23:29:17 +02:00
import java.util.Set;
import java.util.UUID;
2018-10-19 23:29:17 +02:00
import java.util.WeakHashMap;
2018-10-21 16:27:56 +02:00
import java.util.concurrent.atomic.AtomicBoolean;
2018-10-19 23:29:17 +02:00
import javax.net.ssl.SSLHandshakeException;
2018-10-19 23:29:17 +02:00
import eu.siacs.conversations.Config;
2018-10-22 20:08:05 +02:00
import eu.siacs.conversations.crypto.sasl.Plain;
2018-10-19 23:29:17 +02:00
import eu.siacs.conversations.entities.Account;
2018-10-22 20:08:05 +02:00
import eu.siacs.conversations.utils.AccountUtils;
2018-10-20 16:37:59 +02:00
import eu.siacs.conversations.utils.CryptoHelper;
2018-10-19 23:29:17 +02:00
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import io.michaelrocks.libphonenumber.android.Phonenumber;
import rocks.xmpp.addr.Jid;
2018-10-19 20:43:38 +02:00
public class QuickConversationsService {
2018-10-22 20:08:05 +02:00
public static final int API_ERROR_OTHER = -1;
public static final int API_ERROR_UNKNOWN_HOST = -2;
public static final int API_ERROR_CONNECT = -3;
public static final int API_ERROR_SSL_HANDSHAKE = -4;
public static final int API_ERROR_AIRPLANE_MODE = -5;
2018-10-22 20:08:05 +02:00
private static final String BASE_URL = "http://venus.fritz.box:4567";
2018-10-22 20:08:05 +02:00
private static final String INSTALLATION_ID = "eu.siacs.conversations.installation-id";
2018-10-19 20:43:38 +02:00
private final XmppConnectionService service;
2018-10-19 23:29:17 +02:00
private final Set<OnVerificationRequested> mOnVerificationRequested = Collections.newSetFromMap(new WeakHashMap<>());
2018-10-21 16:27:56 +02:00
private final Set<OnVerification> mOnVerification = Collections.newSetFromMap(new WeakHashMap<>());
private final AtomicBoolean mVerificationInProgress = new AtomicBoolean(false);
2018-10-22 20:08:05 +02:00
private final AtomicBoolean mVerificationRequestInProgress = new AtomicBoolean(false);
2018-10-19 23:29:17 +02:00
2018-10-19 20:43:38 +02:00
QuickConversationsService(XmppConnectionService xmppConnectionService) {
this.service = xmppConnectionService;
}
2018-10-19 23:29:17 +02:00
public void addOnVerificationRequestedListener(OnVerificationRequested onVerificationRequested) {
synchronized (mOnVerificationRequested) {
mOnVerificationRequested.add(onVerificationRequested);
}
}
public void removeOnVerificationRequestedListener(OnVerificationRequested onVerificationRequested) {
synchronized (mOnVerificationRequested) {
mOnVerificationRequested.remove(onVerificationRequested);
}
}
2018-10-21 16:27:56 +02:00
public void addOnVerificationListener(OnVerification onVerification) {
synchronized (mOnVerification) {
mOnVerification.add(onVerification);
}
}
public void removeOnVerificationListener(OnVerification onVerification) {
synchronized (mOnVerification) {
mOnVerification.remove(onVerification);
}
}
2018-10-19 23:29:17 +02:00
public void requestVerification(Phonenumber.PhoneNumber phoneNumber) {
2018-10-22 20:08:05 +02:00
final String e164 = PhoneNumberUtilWrapper.normalize(service, phoneNumber);
if (mVerificationRequestInProgress.compareAndSet(false, true)) {
new Thread(() -> {
try {
Thread.sleep(5000);
final URL url = new URL(BASE_URL + "/authentication/" + e164);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
setHeader(connection);
2018-10-22 20:08:05 +02:00
final int code = connection.getResponseCode();
if (code == 200) {
createAccountAndWait(phoneNumber, 0L);
} else if (code == 429) {
createAccountAndWait(phoneNumber, retryAfter(connection));
} else {
synchronized (mOnVerificationRequested) {
for (OnVerificationRequested onVerificationRequested : mOnVerificationRequested) {
onVerificationRequested.onVerificationRequestFailed(code);
}
}
}
} catch (Exception e) {
final int code = getApiErrorCode(e);
synchronized (mOnVerificationRequested) {
for (OnVerificationRequested onVerificationRequested : mOnVerificationRequested) {
onVerificationRequested.onVerificationRequestFailed(code);
}
}
} finally {
mVerificationRequestInProgress.set(false);
}
}).start();
}
}
private void createAccountAndWait(Phonenumber.PhoneNumber phoneNumber, final long timestamp) {
2018-10-19 23:29:17 +02:00
String local = PhoneNumberUtilWrapper.normalize(service, phoneNumber);
2018-10-22 20:08:05 +02:00
Log.d(Config.LOGTAG, "requesting verification for " + PhoneNumberUtilWrapper.normalize(service, phoneNumber));
Jid jid = Jid.of(local, Config.QUICKSY_DOMAIN, null);
2018-10-22 20:08:05 +02:00
Account account = AccountUtils.getFirst(service);
if (account == null || !account.getJid().asBareJid().equals(jid.asBareJid())) {
if (account != null) {
service.deleteAccount(account);
}
account = new Account(jid, CryptoHelper.createPassword(new SecureRandom()));
account.setOption(Account.OPTION_DISABLED, true);
account.setOption(Account.OPTION_UNVERIFIED, true);
service.createAccount(account);
}
2018-10-19 23:29:17 +02:00
synchronized (mOnVerificationRequested) {
2018-10-22 20:08:05 +02:00
for (OnVerificationRequested onVerificationRequested : mOnVerificationRequested) {
if (timestamp <= 0) {
onVerificationRequested.onVerificationRequested();
} else {
onVerificationRequested.onVerificationRequestedRetryAt(timestamp);
}
2018-10-19 23:29:17 +02:00
}
}
}
public void verify(final Account account, String pin) {
2018-10-21 16:27:56 +02:00
if (mVerificationInProgress.compareAndSet(false, true)) {
new Thread(() -> {
try {
2018-10-22 20:08:05 +02:00
2018-10-21 16:27:56 +02:00
Thread.sleep(5000);
2018-10-22 20:08:05 +02:00
final URL url = new URL(BASE_URL + "/password");
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000);
connection.setRequestMethod("POST");
connection.setRequestProperty("Authorization", Plain.getMessage(account.getUsername(), pin));
setHeader(connection);
2018-10-22 20:08:05 +02:00
final OutputStream os = connection.getOutputStream();
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(account.getPassword());
writer.flush();
writer.close();
os.close();
connection.connect();
final int code = connection.getResponseCode();
if (code == 200) {
account.setOption(Account.OPTION_UNVERIFIED, false);
account.setOption(Account.OPTION_DISABLED, false);
service.updateAccount(account);
2018-10-22 20:08:05 +02:00
synchronized (mOnVerification) {
for (OnVerification onVerification : mOnVerification) {
onVerification.onVerificationSucceeded();
}
}
} else if (code == 429) {
final long retryAfter = retryAfter(connection);
synchronized (mOnVerification) {
for (OnVerification onVerification : mOnVerification) {
onVerification.onVerificationRetryAt(retryAfter);
}
}
} else {
synchronized (mOnVerification) {
for (OnVerification onVerification : mOnVerification) {
onVerification.onVerificationFailed(code);
}
}
}
} catch (Exception e) {
final int code = getApiErrorCode(e);
2018-10-21 16:27:56 +02:00
synchronized (mOnVerification) {
for (OnVerification onVerification : mOnVerification) {
2018-10-22 20:08:05 +02:00
onVerification.onVerificationFailed(code);
2018-10-21 16:27:56 +02:00
}
}
} finally {
mVerificationInProgress.set(false);
}
}).start();
}
}
private void setHeader(HttpURLConnection connection) {
connection.setRequestProperty("User-Agent", service.getIqGenerator().getUserAgent());
connection.setRequestProperty("Installation-Id", getInstallationId());
connection.setRequestProperty("Accept-Language", Locale.getDefault().getLanguage());
}
private String getInstallationId() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(service);
String id = preferences.getString(INSTALLATION_ID, null);
if (id != null) {
return id;
} else {
id = UUID.randomUUID().toString();
preferences.edit().putString(INSTALLATION_ID, id).apply();
return id;
}
}
private int getApiErrorCode(Exception e) {
if (!service.hasInternetConnection()) {
return API_ERROR_AIRPLANE_MODE;
} else if (e instanceof UnknownHostException) {
2018-10-22 20:08:05 +02:00
return API_ERROR_UNKNOWN_HOST;
} else if (e instanceof ConnectException) {
return API_ERROR_CONNECT;
} else if (e instanceof SSLHandshakeException) {
return API_ERROR_SSL_HANDSHAKE;
2018-10-22 20:08:05 +02:00
} else {
Log.d(Config.LOGTAG, e.getClass().getName());
2018-10-22 20:08:05 +02:00
return API_ERROR_OTHER;
}
}
private static long retryAfter(HttpURLConnection connection) {
try {
return SystemClock.elapsedRealtime() + (Long.parseLong(connection.getHeaderField("Retry-After")) * 1000L);
} catch (Exception e) {
return 0;
}
}
2018-10-21 16:27:56 +02:00
public boolean isVerifying() {
return mVerificationInProgress.get();
}
2018-10-22 20:08:05 +02:00
public boolean isRequestingVerification() {
return mVerificationRequestInProgress.get();
}
2018-10-24 13:54:57 +02:00
public static boolean isQuicksy() {
return true;
}
2018-10-19 23:29:17 +02:00
public interface OnVerificationRequested {
void onVerificationRequestFailed(int code);
2018-10-22 20:08:05 +02:00
2018-10-19 23:29:17 +02:00
void onVerificationRequested();
2018-10-22 20:08:05 +02:00
void onVerificationRequestedRetryAt(long timestamp);
2018-10-19 23:29:17 +02:00
}
2018-10-21 16:27:56 +02:00
public interface OnVerification {
2018-10-22 20:08:05 +02:00
void onVerificationFailed(int code);
2018-10-19 23:29:17 +02:00
void onVerificationSucceeded();
2018-10-22 20:08:05 +02:00
void onVerificationRetryAt(long timestamp);
2018-10-19 23:29:17 +02:00
}
2018-10-19 20:43:38 +02:00
}