From 72019387939d60c9a7e9155637b5755fa7561bbf Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Fri, 22 Nov 2024 10:22:40 -0500 Subject: [PATCH] Add a utility method for getting alternate forms of (Benin) phone numbers --- .../textsecuregcm/util/Util.java | 43 +++++++++++++++++++ .../textsecuregcm/util/UtilTest.java | 42 ++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/util/UtilTest.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java b/service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java index 35e0a545f..c50fab2a0 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java @@ -101,6 +101,49 @@ public class Util { } } + /** + * Returns a list of equivalent phone numbers to the given phone number. This is useful in cases where a numbering + * authority has changed the numbering format for a region or in cases where multiple formats of a number may be valid + * in different circumstances. Numbers are considered equivalent if a call/message sent to each number will generally + * arrive at the same device. + * + * @apiNote This method is intended to support number format transitions in cases where we do not already have + * multiple accounts registered with different forms of the same number. As a result, this method does not cover all + * possible cases of equivalent formats, but instead focuses on the cases where we can and choose to prevent multiple + * accounts from using different formats of the same number. + * + * @param number the e164-formatted phone number for which to find equivalent forms + * + * @return a list of phone numbers equivalent to the given phone number, including the given number + */ + public static List getAlternateForms(final String number) { + try { + final PhoneNumber phoneNumber = PHONE_NUMBER_UTIL.parse(number, null); + + // Benin is changing phone number formats from +229 XXXXXXXX to +229 01XXXXXXXX starting on November 30, 2024 + if ("BJ".equals(PHONE_NUMBER_UTIL.getRegionCodeForNumber(phoneNumber))) { + final String nationalSignificantNumber = PHONE_NUMBER_UTIL.getNationalSignificantNumber(phoneNumber); + final String alternateE164; + + if (nationalSignificantNumber.length() == 10) { + // This is a new-format number; we can get the old-format version by stripping the leading "01" from the + // national number + alternateE164 = "+229" + StringUtils.removeStart(nationalSignificantNumber, "01"); + } else { + // This is an old-format number; we can get the new-format version by adding a "01" prefix to the national + // number + alternateE164 = "+22901" + nationalSignificantNumber; + } + + return List.of(number, alternateE164); + } + + return List.of(number); + } catch (final NumberParseException e) { + return List.of(number); + } + } + public static byte[] truncate(byte[] element, int length) { byte[] result = new byte[length]; System.arraycopy(element, 0, result, 0, result.length); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/util/UtilTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/util/UtilTest.java new file mode 100644 index 000000000..f25c128ab --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/util/UtilTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.util; + +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class UtilTest { + + @ParameterizedTest + @MethodSource + void getAlternateForms(final String phoneNumber, final List expectedAlternateForms) { + assertEquals(expectedAlternateForms, Util.getAlternateForms(phoneNumber)); + } + + static List getAlternateForms() { + final String usE164 = PhoneNumberUtil.getInstance().format( + PhoneNumberUtil.getInstance().getExampleNumber("US"), PhoneNumberUtil.PhoneNumberFormat.E164); + + // libphonenumber 8.13.50 and on generate new-format numbers for Benin + final String newFormatBeninE164 = PhoneNumberUtil.getInstance() + .format(PhoneNumberUtil.getInstance().getExampleNumber("BJ"), PhoneNumberUtil.PhoneNumberFormat.E164); + + final String oldFormatBeninE164 = newFormatBeninE164.replaceFirst("01", ""); + + return List.of( + Arguments.of(usE164, List.of(usE164)), + Arguments.of(newFormatBeninE164, List.of(newFormatBeninE164, oldFormatBeninE164)), + Arguments.of(oldFormatBeninE164, List.of(oldFormatBeninE164, newFormatBeninE164)) + ); + } +}