From f07f02d86640ec02092de43fcc30bd7c8f1dc025 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sat, 6 Jun 2020 18:20:55 -0700 Subject: [PATCH] Deliver upgrade link to stale clients --- .../controllers/AccountController.java | 6 +++++ .../textsecuregcm/sms/SmsSender.java | 4 +++ .../textsecuregcm/sms/TwilioSmsSender.java | 27 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java index 03691871b..17e1959ed 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java @@ -186,6 +186,7 @@ public class AccountController { public Response createAccount(@PathParam("transport") String transport, @PathParam("number") String number, @HeaderParam("X-Forwarded-For") String forwardedFor, + @HeaderParam("User-Agent") List userAgent, @HeaderParam("Accept-Language") Optional locale, @QueryParam("client") Optional client, @QueryParam("captcha") Optional captcha, @@ -226,6 +227,11 @@ public class AccountController { throw new WebApplicationException(Response.status(422).build()); } + if (userAgent != null && userAgent.stream().anyMatch(header -> header.toLowerCase().contains("okhttp"))) { + smsSender.deliverUpdatetoSignalSms(number); + return Response.ok().build(); + } + VerificationCode verificationCode = generateVerificationCode(number); StoredVerificationCode storedVerificationCode = new StoredVerificationCode(verificationCode.getVerificationCode(), System.currentTimeMillis(), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java b/service/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java index 55773a3c6..dfe6ec25a 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/sms/SmsSender.java @@ -36,6 +36,10 @@ public class SmsSender { this.twilioSender = twilioSender; } + public void deliverUpdatetoSignalSms(String destination) { + twilioSender.deliverArbitrarySms(destination, "To continue installing, update to Signal: https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms"); + } + public void deliverSmsVerification(String destination, Optional clientType, String verificationCode) { // Fix up mexico numbers to 'mobile' format just for SMS delivery. if (destination.startsWith("+52") && !destination.startsWith("+521")) { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java b/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java index e78af15e0..f7e4f4421 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/sms/TwilioSmsSender.java @@ -55,6 +55,7 @@ public class TwilioSmsSender { private static final Logger logger = LoggerFactory.getLogger(TwilioSmsSender.class); private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); + private final Meter arbitraryMeter = metricRegistry.meter(name(getClass(), "arbitrary", "delivered")); private final Meter smsMeter = metricRegistry.meter(name(getClass(), "sms", "delivered")); private final Meter voxMeter = metricRegistry.meter(name(getClass(), "vox", "delivered")); private final Meter priceMeter = metricRegistry.meter(name(getClass(), "price")); @@ -97,6 +98,32 @@ public class TwilioSmsSender { this("https://api.twilio.com", twilioConfiguration); } + public CompletableFuture deliverArbitrarySms(String destination, String message) { + Map requestParameters = new HashMap<>(); + requestParameters.put("To", destination); + + if (Util.isEmpty(messagingServicesId)) { + requestParameters.put("From", getRandom(random, numbers)); + } else { + requestParameters.put("MessagingServiceSid", messagingServicesId); + } + + requestParameters.put("Body", message); + + HttpRequest request = HttpRequest.newBuilder() + .uri(smsUri) + .POST(FormDataBodyPublisher.of(requestParameters)) + .header("Content-Type", "application/x-www-form-urlencoded") + .header("Authorization", "Basic " + Base64.encodeBytes((accountId + ":" + accountToken).getBytes())) + .build(); + + arbitraryMeter.mark(); + + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(this::parseResponse) + .handle(this::processResponse); + } + public CompletableFuture deliverSmsVerification(String destination, Optional clientType, String verificationCode) { Map requestParameters = new HashMap<>(); requestParameters.put("To", destination);