From 20fd8816131502518b83c34f86002e5f8bf591d9 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 25 Feb 2014 16:59:56 -0800 Subject: [PATCH] Display error code from server when already registered elsewhere. --- .../push/ExpectationFailedException.java | 6 +++++ .../textsecure/push/PushServiceSocket.java | 6 ++++- res/values/strings.xml | 3 ++- .../RegistrationProgressActivity.java | 24 +++++++++++++++---- .../service/RegistrationService.java | 14 +++++++---- 5 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 library/src/org/whispersystems/textsecure/push/ExpectationFailedException.java diff --git a/library/src/org/whispersystems/textsecure/push/ExpectationFailedException.java b/library/src/org/whispersystems/textsecure/push/ExpectationFailedException.java new file mode 100644 index 0000000000..defc66d20d --- /dev/null +++ b/library/src/org/whispersystems/textsecure/push/ExpectationFailedException.java @@ -0,0 +1,6 @@ +package org.whispersystems.textsecure.push; + +import java.io.IOException; + +public class ExpectationFailedException extends IOException { +} diff --git a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java index 0289773a69..360266d0c7 100644 --- a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java +++ b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java @@ -207,7 +207,7 @@ public class PushServiceSocket { public List retrieveDirectory(Set contactTokens) { try { - ContactTokenList contactTokenList = new ContactTokenList(new LinkedList(contactTokens)); + ContactTokenList contactTokenList = new ContactTokenList(new LinkedList(contactTokens)); String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", new Gson().toJson(contactTokenList)); ContactTokenDetailsList activeTokens = new Gson().fromJson(response, ContactTokenDetailsList.class); @@ -334,6 +334,10 @@ public class PushServiceSocket { throw new StaleDevicesException(new Gson().fromJson(response, StaleDevices.class)); } + if (connection.getResponseCode() == 417) { + throw new ExpectationFailedException(); + } + if (connection.getResponseCode() != 200 && connection.getResponseCode() != 204) { throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage()); } diff --git a/res/values/strings.xml b/res/values/strings.xml index 57ab9e9f7b..b04e606f76 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -268,6 +268,8 @@ Too Many Requests! You\'ve already recently requested a voice call. You can request another in 20 minutes. Verifying voice code... + Registration conflict + This number is already registered on a different TextSecure server (CyanogenMod?). You must unregistering there before registering here. Registration Complete @@ -771,7 +773,6 @@ Verified - diff --git a/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java b/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java index 341fdfd2a8..4a18d669ee 100644 --- a/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java +++ b/src/org/thoughtcrime/securesms/RegistrationProgressActivity.java @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.service.RegistrationService; import org.thoughtcrime.securesms.util.ActionBarUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.textsecure.crypto.MasterSecret; +import org.whispersystems.textsecure.push.ExpectationFailedException; import org.whispersystems.textsecure.push.PushServiceSocket; import org.whispersystems.textsecure.push.RateLimitException; import org.whispersystems.textsecure.util.PhoneNumberFormatter; @@ -314,6 +315,12 @@ public class RegistrationProgressActivity extends SherlockActivity { PhoneNumberFormatter.formatNumberInternational(state.number))); } + private void handleMultiRegistrationError(RegistrationState state) { + handleVerificationTimeout(state); + Util.showAlertDialog(this, getString(R.string.RegistrationProgressActivity_registration_conflict), + getString(R.string.RegistrationProgressActivity_this_number_is_already_registered_on_a_different)); + } + private void handleVerificationComplete() { if (visible) { Toast.makeText(this, @@ -403,6 +410,7 @@ public class RegistrationProgressActivity extends SherlockActivity { case RegistrationState.STATE_COMPLETE: handleVerificationComplete(); break; case RegistrationState.STATE_GCM_TIMEOUT: handleGcmTimeout(state); break; case RegistrationState.STATE_NETWORK_ERROR: handleConnectivityError(state); break; + case RegistrationState.STATE_MULTI_REGISTERED: handleMultiRegistrationError(state); break; case RegistrationState.STATE_VOICE_REQUESTED: handleVerificationRequestedVoice(state); break; } } @@ -429,10 +437,11 @@ public class RegistrationProgressActivity extends SherlockActivity { private class VerifyClickListener implements View.OnClickListener { - private static final int SUCCESS = 0; - private static final int NETWORK_ERROR = 1; - private static final int RATE_LIMIT_ERROR = 2; - private static final int VERIFICATION_ERROR = 3; + private static final int SUCCESS = 0; + private static final int NETWORK_ERROR = 1; + private static final int RATE_LIMIT_ERROR = 2; + private static final int VERIFICATION_ERROR = 3; + private static final int MULTI_REGISTRATION_ERROR = 4; private final String e164number; private final String password; @@ -495,6 +504,10 @@ public class RegistrationProgressActivity extends SherlockActivity { Util.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_too_many_attempts), getString(R.string.RegistrationProgressActivity_youve_submitted_an_incorrect_verification_code_too_many_times)); break; + case MULTI_REGISTRATION_ERROR: + Util.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_registration_conflict), + getString(R.string.RegistrationProgressActivity_this_number_is_already_registered_on_a_different)); + break; } } @@ -505,6 +518,9 @@ public class RegistrationProgressActivity extends SherlockActivity { int registrationId = TextSecurePreferences.getLocalRegistrationId(context); socket.verifyAccount(code, signalingKey, true, registrationId); return SUCCESS; + } catch (ExpectationFailedException e) { + Log.w("RegistrationProgressActivity", e); + return MULTI_REGISTRATION_ERROR; } catch (RateLimitException e) { Log.w("RegistrationProgressActivity", e); return RATE_LIMIT_ERROR; diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java index 3c26a4df78..e56def7f34 100644 --- a/src/org/thoughtcrime/securesms/service/RegistrationService.java +++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java @@ -23,13 +23,13 @@ import org.whispersystems.textsecure.crypto.IdentityKey; import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.PreKeyUtil; import org.whispersystems.textsecure.crypto.ecc.Curve; +import org.whispersystems.textsecure.push.ExpectationFailedException; import org.whispersystems.textsecure.push.PushServiceSocket; import org.whispersystems.textsecure.storage.PreKeyRecord; import org.whispersystems.textsecure.util.Util; import java.io.IOException; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -89,9 +89,9 @@ public class RegistrationService extends Service { executor.execute(new Runnable() { @Override public void run() { - if (intent.getAction().equals(REGISTER_NUMBER_ACTION)) handleSmsRegistrationIntent(intent); - else if (intent.getAction().equals(VOICE_REQUESTED_ACTION)) handleVoiceRequestedIntent(intent); - else if (intent.getAction().equals(VOICE_REGISTER_ACTION)) handleVoiceRegistrationIntent(intent); + if (REGISTER_NUMBER_ACTION.equals(intent.getAction())) handleSmsRegistrationIntent(intent); + else if (VOICE_REQUESTED_ACTION.equals(intent.getAction())) handleVoiceRequestedIntent(intent); + else if (VOICE_REGISTER_ACTION.equals(intent.getAction())) handleVoiceRegistrationIntent(intent); } }); } @@ -256,6 +256,10 @@ public class RegistrationService extends Service { setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number)); broadcastComplete(true); + } catch (ExpectationFailedException efe) { + Log.w("RegistrationService", efe); + setState(new RegistrationState(RegistrationState.STATE_MULTI_REGISTERED, number)); + broadcastComplete(false); } catch (UnsupportedOperationException uoe) { Log.w("RegistrationService", uoe); setState(new RegistrationState(RegistrationState.STATE_GCM_UNSUPPORTED, number)); @@ -434,6 +438,8 @@ public class RegistrationService extends Service { public static final int STATE_VOICE_REQUESTED = 12; public static final int STATE_GENERATING_KEYS = 13; + public static final int STATE_MULTI_REGISTERED = 14; + public final int state; public final String number; public final String password;