kopia lustrzana https://github.com/ryukoposting/Signal-Android
Handle non-normalized phone number responses.
rodzic
2980e547cb
commit
48a81da883
|
@ -1,9 +1,12 @@
|
|||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ImpossiblePhoneNumberException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.LocalRateLimitException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
/**
|
||||
* Process responses from requesting an SMS or Phone code from the server.
|
||||
|
@ -25,6 +28,32 @@ class RequestVerificationCodeResponseProcessor(response: ServiceResponse<Request
|
|||
return error is LocalRateLimitException
|
||||
}
|
||||
|
||||
fun isImpossibleNumber(): Boolean {
|
||||
return error is ImpossiblePhoneNumberException
|
||||
}
|
||||
|
||||
fun isNonNormalizedNumber(): Boolean {
|
||||
return error is NonNormalizedPhoneNumberException
|
||||
}
|
||||
|
||||
/** Should only be called if [isNonNormalizedNumber] */
|
||||
fun getOriginalNumber(): String {
|
||||
if (error !is NonNormalizedPhoneNumberException) {
|
||||
throw IllegalStateException("This can only be called when isNonNormalizedNumber()")
|
||||
}
|
||||
|
||||
return (error as NonNormalizedPhoneNumberException).originalNumber
|
||||
}
|
||||
|
||||
/** Should only be called if [isNonNormalizedNumber] */
|
||||
fun getNormalizedNumber(): String {
|
||||
if (error !is NonNormalizedPhoneNumberException) {
|
||||
throw IllegalStateException("This can only be called when isNonNormalizedNumber()")
|
||||
}
|
||||
|
||||
return (error as NonNormalizedPhoneNumberException).normalizedNumber
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun forLocalRateLimit(): RequestVerificationCodeResponseProcessor {
|
||||
|
|
|
@ -34,6 +34,9 @@ import com.google.android.gms.common.ConnectionResult;
|
|||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
|
@ -45,9 +48,11 @@ import org.thoughtcrime.securesms.registration.VerifyAccountRepository.Mode;
|
|||
import org.thoughtcrime.securesms.registration.util.RegistrationNumberInputController;
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState;
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.PlayServicesUtil;
|
||||
import org.thoughtcrime.securesms.util.SupportEmailUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
|
@ -230,8 +235,15 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
|||
} else if (processor.rateLimit()) {
|
||||
Log.i(TAG, "Unable to request sms code due to rate limit");
|
||||
Toast.makeText(register.getContext(), R.string.RegistrationActivity_rate_limited_to_service, Toast.LENGTH_LONG).show();
|
||||
} else if (processor.isImpossibleNumber()) {
|
||||
Log.w(TAG, "Impossible number", processor.getError());
|
||||
Dialogs.showAlertDialog(requireContext(),
|
||||
getString(R.string.RegistrationActivity_invalid_number),
|
||||
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), viewModel.getNumber().getFullFormattedNumber()));
|
||||
} else if (processor.isNonNormalizedNumber()) {
|
||||
handleNonNormalizedNumberError(processor.getOriginalNumber(), processor.getNormalizedNumber(), mode);
|
||||
} else {
|
||||
Log.w(TAG, "Unable to request sms code", processor.getError());
|
||||
Log.i(TAG, "Unknown error during verification code request", processor.getError());
|
||||
Toast.makeText(register.getContext(), R.string.RegistrationActivity_unable_to_connect_to_service, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
|
@ -273,6 +285,37 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
|||
viewModel.onCountrySelected(null, countryCode);
|
||||
}
|
||||
|
||||
private void handleNonNormalizedNumberError(@NonNull String originalNumber, @NonNull String normalizedNumber, @NonNull Mode mode) {
|
||||
try {
|
||||
Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(normalizedNumber, null);
|
||||
|
||||
new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.RegistrationActivity_non_standard_number_format)
|
||||
.setMessage(getString(R.string.RegistrationActivity_the_number_you_entered_appears_to_be_a_non_standard, originalNumber, normalizedNumber))
|
||||
.setNegativeButton(android.R.string.no, (d, i) -> d.dismiss())
|
||||
.setNeutralButton(R.string.RegistrationActivity_contact_signal_support, (d, i) -> {
|
||||
String subject = getString(R.string.RegistrationActivity_signal_android_phone_number_format);
|
||||
String body = SupportEmailUtil.generateSupportEmailBody(requireContext(), R.string.RegistrationActivity_signal_android_phone_number_format, null, null);
|
||||
|
||||
CommunicationActions.openEmail(requireContext(), SupportEmailUtil.getSupportEmailAddress(requireContext()), subject, body);
|
||||
d.dismiss();
|
||||
})
|
||||
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||
countryCode.setText(String.valueOf(phoneNumber.getCountryCode()));
|
||||
number.setText(String.valueOf(phoneNumber.getNationalNumber()));
|
||||
requestVerificationCode(mode);
|
||||
d.dismiss();
|
||||
})
|
||||
.show();
|
||||
} catch (NumberParseException e) {
|
||||
Log.w(TAG, "Failed to parse number!", e);
|
||||
|
||||
Dialogs.showAlertDialog(requireContext(),
|
||||
getString(R.string.RegistrationActivity_invalid_number),
|
||||
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), viewModel.getNumber().getFullFormattedNumber()));
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePromptForNoPlayServices(@NonNull Context context) {
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.RegistrationActivity_missing_google_play_services)
|
||||
|
|
|
@ -20,24 +20,25 @@ import android.content.Context;
|
|||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class Dialogs {
|
||||
public static void showAlertDialog(Context context, String title, String message) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(title);
|
||||
dialog.setMessage(message);
|
||||
dialog.setIcon(R.drawable.ic_warning);
|
||||
dialog.setPositiveButton(android.R.string.ok, null);
|
||||
dialog.show();
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void showInfoDialog(Context context, String title, String message) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(title);
|
||||
dialog.setMessage(message);
|
||||
dialog.setIcon(R.drawable.ic_info_outline);
|
||||
dialog.setPositiveButton(android.R.string.ok, null);
|
||||
dialog.show();
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setIcon(R.drawable.ic_info_outline)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1560,6 +1560,9 @@
|
|||
<string name="RegistrationActivity_signal_needs_access_to_your_contacts_in_order_to_connect_with_friends">Signal needs access to your contacts in order to connect with friends, exchange messages, and make secure calls</string>
|
||||
<string name="RegistrationActivity_rate_limited_to_service">You\'ve made too many attempts to register this number. Please try again later.</string>
|
||||
<string name="RegistrationActivity_unable_to_connect_to_service">Unable to connect to service. Please check network connection and try again.</string>
|
||||
<string name="RegistrationActivity_non_standard_number_format">Non-standard number format</string>
|
||||
<string name="RegistrationActivity_the_number_you_entered_appears_to_be_a_non_standard">The number you entered (%1$s) appears to be a non-standard format.\n\nDid you mean %2$s?</string>
|
||||
<string name="RegistrationActivity_signal_android_phone_number_format">Signal Android - Phone Number Format</string>
|
||||
<string name="RegistrationActivity_call_requested">Call requested</string>
|
||||
<plurals name="RegistrationActivity_debug_log_hint">
|
||||
<item quantity="one">You are now %d step away from submitting a debug log.</item>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package org.whispersystems.signalservice.api.push.exceptions;
|
||||
|
||||
/**
|
||||
* An exception indicating that the server believes the number provided is 'impossible', meaning it fails the most basic libphonenumber checks.
|
||||
*/
|
||||
public class ImpossiblePhoneNumberException extends NonSuccessfulResponseCodeException {
|
||||
public ImpossiblePhoneNumberException() {
|
||||
super(400);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.whispersystems.signalservice.api.push.exceptions;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Response indicating we gave the server a non-normalized phone number. The expected normalized version of the number is provided.
|
||||
*/
|
||||
public class NonNormalizedPhoneNumberException extends NonSuccessfulResponseCodeException {
|
||||
|
||||
private final String originalNumber;
|
||||
private final String normalizedNumber;
|
||||
|
||||
public static NonNormalizedPhoneNumberException forResponse(@NonNull String responseBody) throws MalformedResponseException {
|
||||
JsonResponse response = JsonUtil.fromJsonResponse(responseBody, JsonResponse.class);
|
||||
return new NonNormalizedPhoneNumberException(response.originalNumber, response.normalizedNumber);
|
||||
}
|
||||
|
||||
public NonNormalizedPhoneNumberException(String originalNumber, String normalizedNumber) {
|
||||
super(400);
|
||||
|
||||
this.originalNumber = originalNumber;
|
||||
this.normalizedNumber = normalizedNumber;
|
||||
}
|
||||
|
||||
public String getOriginalNumber() {
|
||||
return originalNumber;
|
||||
}
|
||||
|
||||
public String getNormalizedNumber() {
|
||||
return normalizedNumber;
|
||||
}
|
||||
|
||||
private static class JsonResponse {
|
||||
@JsonProperty
|
||||
private String originalNumber;
|
||||
|
||||
@JsonProperty
|
||||
private String normalizedNumber;
|
||||
}
|
||||
}
|
|
@ -59,9 +59,11 @@ import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
|
|||
import org.whispersystems.signalservice.api.push.exceptions.ContactManifestMismatchException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ImpossiblePhoneNumberException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NoContentException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
|
||||
|
@ -141,7 +143,6 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
@ -304,14 +305,7 @@ public class PushServiceSocket {
|
|||
path += "&challenge=" + challenge.get();
|
||||
}
|
||||
|
||||
makeServiceRequest(path, "GET", null, NO_HEADERS, new ResponseCodeHandler() {
|
||||
@Override
|
||||
public void handle(int responseCode) throws NonSuccessfulResponseCodeException {
|
||||
if (responseCode == 402) {
|
||||
throw new CaptchaRequiredException();
|
||||
}
|
||||
}
|
||||
});
|
||||
makeServiceRequest(path, "GET", null, NO_HEADERS, new VerificationCodeResponseHandler());
|
||||
}
|
||||
|
||||
public void requestVoiceVerificationCode(Locale locale, Optional<String> captchaToken, Optional<String> challenge) throws IOException {
|
||||
|
@ -324,14 +318,7 @@ public class PushServiceSocket {
|
|||
path += "?challenge=" + challenge.get();
|
||||
}
|
||||
|
||||
makeServiceRequest(path, "GET", null, headers, new ResponseCodeHandler() {
|
||||
@Override
|
||||
public void handle(int responseCode) throws NonSuccessfulResponseCodeException {
|
||||
if (responseCode == 402) {
|
||||
throw new CaptchaRequiredException();
|
||||
}
|
||||
}
|
||||
});
|
||||
makeServiceRequest(path, "GET", null, headers, new VerificationCodeResponseHandler());
|
||||
}
|
||||
|
||||
public UUID getOwnUuid() throws IOException {
|
||||
|
@ -848,13 +835,10 @@ public class PushServiceSocket {
|
|||
}
|
||||
|
||||
public void setUsername(String username) throws IOException {
|
||||
makeServiceRequest(String.format(SET_USERNAME_PATH, username), "PUT", "", NO_HEADERS, new ResponseCodeHandler() {
|
||||
@Override
|
||||
public void handle(int responseCode) throws NonSuccessfulResponseCodeException {
|
||||
switch (responseCode) {
|
||||
case 400: throw new UsernameMalformedException();
|
||||
case 409: throw new UsernameTakenException();
|
||||
}
|
||||
makeServiceRequest(String.format(SET_USERNAME_PATH, username), "PUT", "", NO_HEADERS, (responseCode, body) -> {
|
||||
switch (responseCode) {
|
||||
case 400: throw new UsernameMalformedException();
|
||||
case 409: throw new UsernameTakenException();
|
||||
}
|
||||
}, Optional.<UnidentifiedAccess>absent());
|
||||
}
|
||||
|
@ -932,7 +916,7 @@ public class PushServiceSocket {
|
|||
String.format(SUBSCRIPTION_RECEIPT_CREDENTIALS, subscriptionId),
|
||||
"POST",
|
||||
payload,
|
||||
code -> {
|
||||
(code, body) -> {
|
||||
if (code == 204) throw new NonSuccessfulResponseCodeException(204);
|
||||
});
|
||||
|
||||
|
@ -1636,7 +1620,7 @@ public class PushServiceSocket {
|
|||
{
|
||||
Response response = getServiceConnection(urlFragment, method, body, headers, unidentifiedAccessKey, doNotAddAuthenticationOrUnidentifiedAccessKey);
|
||||
|
||||
responseCodeHandler.handle(response.code());
|
||||
responseCodeHandler.handle(response.code(), response.body());
|
||||
|
||||
return validateServiceResponse(response);
|
||||
}
|
||||
|
@ -1916,7 +1900,7 @@ public class PushServiceSocket {
|
|||
}
|
||||
}
|
||||
|
||||
responseCodeHandler.handle(response.code());
|
||||
responseCodeHandler.handle(response.code(), response.body());
|
||||
|
||||
switch (response.code()) {
|
||||
case 204:
|
||||
|
@ -2231,12 +2215,12 @@ public class PushServiceSocket {
|
|||
}
|
||||
|
||||
private interface ResponseCodeHandler {
|
||||
void handle(int responseCode) throws NonSuccessfulResponseCodeException, PushNetworkException;
|
||||
void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException;
|
||||
}
|
||||
|
||||
private static class EmptyResponseCodeHandler implements ResponseCodeHandler {
|
||||
@Override
|
||||
public void handle(int responseCode) { }
|
||||
public void handle(int responseCode, ResponseBody body) { }
|
||||
}
|
||||
|
||||
public enum ClientSet { ContactDiscovery, KeyBackup }
|
||||
|
@ -2254,20 +2238,20 @@ public class PushServiceSocket {
|
|||
return JsonUtil.fromJson(response, CredentialResponse.class);
|
||||
}
|
||||
|
||||
private static final ResponseCodeHandler GROUPS_V2_PUT_RESPONSE_HANDLER = responseCode -> {
|
||||
private static final ResponseCodeHandler GROUPS_V2_PUT_RESPONSE_HANDLER = (responseCode, body) -> {
|
||||
if (responseCode == 409) throw new GroupExistsException();
|
||||
};;
|
||||
private static final ResponseCodeHandler GROUPS_V2_GET_LOGS_HANDLER = NO_HANDLER;
|
||||
private static final ResponseCodeHandler GROUPS_V2_GET_CURRENT_HANDLER = responseCode -> {
|
||||
private static final ResponseCodeHandler GROUPS_V2_GET_CURRENT_HANDLER = (responseCode, body) -> {
|
||||
switch (responseCode) {
|
||||
case 403: throw new NotInGroupException();
|
||||
case 404: throw new GroupNotFoundException();
|
||||
}
|
||||
};
|
||||
private static final ResponseCodeHandler GROUPS_V2_PATCH_RESPONSE_HANDLER = responseCode -> {
|
||||
private static final ResponseCodeHandler GROUPS_V2_PATCH_RESPONSE_HANDLER = (responseCode, body) -> {
|
||||
if (responseCode == 400) throw new GroupPatchNotAcceptedException();
|
||||
};
|
||||
private static final ResponseCodeHandler GROUPS_V2_GET_JOIN_INFO_HANDLER = responseCode -> {
|
||||
private static final ResponseCodeHandler GROUPS_V2_GET_JOIN_INFO_HANDLER = (responseCode, body) -> {
|
||||
if (responseCode == 403) throw new ForbiddenException();
|
||||
};
|
||||
|
||||
|
@ -2404,6 +2388,34 @@ public class PushServiceSocket {
|
|||
makeServiceRequest(String.format(REPORT_SPAM, e164, serverGuid), "POST", "");
|
||||
}
|
||||
|
||||
private static class VerificationCodeResponseHandler implements ResponseCodeHandler {
|
||||
@Override
|
||||
public void handle(int responseCode, ResponseBody responseBody) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||
switch (responseCode) {
|
||||
case 400:
|
||||
String body;
|
||||
try {
|
||||
body = responseBody != null ? responseBody.string() : "";
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
}
|
||||
|
||||
if (body.isEmpty()) {
|
||||
throw new ImpossiblePhoneNumberException();
|
||||
} else {
|
||||
try {
|
||||
throw NonNormalizedPhoneNumberException.forResponse(body);
|
||||
} catch (MalformedResponseException e) {
|
||||
Log.w(TAG, "Unable to parse 400 response! Assuming a generic 400.");
|
||||
throw new ImpossiblePhoneNumberException();
|
||||
}
|
||||
}
|
||||
case 402:
|
||||
throw new CaptchaRequiredException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GroupHistory {
|
||||
private final GroupChanges groupChanges;
|
||||
private final Optional<ContentRange> contentRange;
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.whispersystems.signalservice.internal.push.exceptions;
|
|||
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
public final class PaymentsRegionException extends NonSuccessfulResponseCodeException {
|
||||
public PaymentsRegionException(int code) {
|
||||
super(code);
|
||||
|
@ -10,7 +12,7 @@ public final class PaymentsRegionException extends NonSuccessfulResponseCodeExce
|
|||
/**
|
||||
* Promotes a 403 to this exception type.
|
||||
*/
|
||||
public static void responseCodeHandler(int responseCode) throws PaymentsRegionException {
|
||||
public static void responseCodeHandler(int responseCode, ResponseBody body) throws PaymentsRegionException {
|
||||
if (responseCode == 403) {
|
||||
throw new PaymentsRegionException(responseCode);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue