Send alternate numbers to CDSI
Co-authored-by: yash-signal <yash@signal.org>
This commit is contained in:
parent
cbeb51a68b
commit
8edb054874
7 changed files with 116 additions and 28 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -49,7 +49,7 @@
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.1",
|
||||||
"fs-extra": "11.2.0",
|
"fs-extra": "11.2.0",
|
||||||
"fuse.js": "6.5.3",
|
"fuse.js": "6.5.3",
|
||||||
"google-libphonenumber": "3.2.38",
|
"google-libphonenumber": "^3.2.39",
|
||||||
"got": "11.8.5",
|
"got": "11.8.5",
|
||||||
"heic-convert": "2.1.0",
|
"heic-convert": "2.1.0",
|
||||||
"humanize-duration": "3.27.1",
|
"humanize-duration": "3.27.1",
|
||||||
|
@ -17417,9 +17417,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/google-libphonenumber": {
|
"node_modules/google-libphonenumber": {
|
||||||
"version": "3.2.38",
|
"version": "3.2.39",
|
||||||
"resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.38.tgz",
|
"resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.39.tgz",
|
||||||
"integrity": "sha512-t/K0dsVmA0gMMVLJgcMeB9g1Ar4ANVWfkY+AJGSdfyJ2Ay7Bu8ceLYpUlC6FZSilZgaF1qbkM9tZydGBEBHqAg==",
|
"integrity": "sha512-dpCbkY6ZxHXIHEFDwSir/gPBWkn22e2EixBv47guVs/NE8+qd35f1yu+fxQ8awRnHEXC60uhcPM9mbqmrD6nmw==",
|
||||||
"license": "(MIT AND Apache-2.0)",
|
"license": "(MIT AND Apache-2.0)",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.1",
|
||||||
"fs-extra": "11.2.0",
|
"fs-extra": "11.2.0",
|
||||||
"fuse.js": "6.5.3",
|
"fuse.js": "6.5.3",
|
||||||
"google-libphonenumber": "3.2.38",
|
"google-libphonenumber": "3.2.39",
|
||||||
"got": "11.8.5",
|
"got": "11.8.5",
|
||||||
"heic-convert": "2.1.0",
|
"heic-convert": "2.1.0",
|
||||||
"humanize-duration": "3.27.1",
|
"humanize-duration": "3.27.1",
|
||||||
|
|
|
@ -1402,15 +1402,15 @@ export class ConversationController {
|
||||||
async _forgetE164(e164: string): Promise<void> {
|
async _forgetE164(e164: string): Promise<void> {
|
||||||
const { server } = window.textsecure;
|
const { server } = window.textsecure;
|
||||||
strictAssert(server, 'Server must be initialized');
|
strictAssert(server, 'Server must be initialized');
|
||||||
const { entries: serviceIdMap } = await getServiceIdsForE164s(server, [
|
const { entries: serviceIdMap, transformedE164s } =
|
||||||
e164,
|
await getServiceIdsForE164s(server, [e164]);
|
||||||
]);
|
|
||||||
|
|
||||||
const pni = serviceIdMap.get(e164)?.pni;
|
const e164ToUse = transformedE164s.get(e164) ?? e164;
|
||||||
|
const pni = serviceIdMap.get(e164ToUse)?.pni;
|
||||||
|
|
||||||
log.info(`ConversationController: forgetting e164=${e164} pni=${pni}`);
|
log.info(`ConversationController: forgetting e164=${e164ToUse} pni=${pni}`);
|
||||||
|
|
||||||
const convos = [this.get(e164), this.get(pni)];
|
const convos = [this.get(e164ToUse), this.get(pni)];
|
||||||
|
|
||||||
for (const convo of convos) {
|
for (const convo of convos) {
|
||||||
if (!convo) {
|
if (!convo) {
|
||||||
|
|
|
@ -93,17 +93,17 @@ function checkForAccount(
|
||||||
|
|
||||||
log.info(`checkForAccount: looking ${phoneNumber} up on server`);
|
log.info(`checkForAccount: looking ${phoneNumber} up on server`);
|
||||||
try {
|
try {
|
||||||
const { entries: serviceIdLookup } = await getServiceIdsForE164s(server, [
|
const { entries: serviceIdLookup, transformedE164s } =
|
||||||
phoneNumber,
|
await getServiceIdsForE164s(server, [phoneNumber]);
|
||||||
]);
|
const phoneNumberToUse = transformedE164s.get(phoneNumber) ?? phoneNumber;
|
||||||
const maybePair = serviceIdLookup.get(phoneNumber);
|
const maybePair = serviceIdLookup.get(phoneNumberToUse);
|
||||||
|
|
||||||
if (maybePair) {
|
if (maybePair) {
|
||||||
const { conversation: maybeMerged } =
|
const { conversation: maybeMerged } =
|
||||||
window.ConversationController.maybeMergeContacts({
|
window.ConversationController.maybeMergeContacts({
|
||||||
aci: maybePair.aci,
|
aci: maybePair.aci,
|
||||||
pni: maybePair.pni,
|
pni: maybePair.pni,
|
||||||
e164: phoneNumber,
|
e164: phoneNumberToUse,
|
||||||
reason: 'checkForAccount',
|
reason: 'checkForAccount',
|
||||||
});
|
});
|
||||||
serviceId = maybeMerged.getServiceId();
|
serviceId = maybeMerged.getServiceId();
|
||||||
|
|
|
@ -27,7 +27,8 @@ export async function updateConversationsWithUuidLookup({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { entries: serverLookup } = await getServiceIdsForE164s(server, e164s);
|
const { entries: serverLookup, transformedE164s } =
|
||||||
|
await getServiceIdsForE164s(server, e164s);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
conversations.map(async conversation => {
|
conversations.map(async conversation => {
|
||||||
|
@ -38,13 +39,14 @@ export async function updateConversationsWithUuidLookup({
|
||||||
|
|
||||||
let finalConversation: ConversationModel;
|
let finalConversation: ConversationModel;
|
||||||
|
|
||||||
const pairFromServer = serverLookup.get(e164);
|
const e164ToUse = transformedE164s.get(e164) ?? e164;
|
||||||
|
const pairFromServer = serverLookup.get(e164ToUse);
|
||||||
if (pairFromServer) {
|
if (pairFromServer) {
|
||||||
const { conversation: maybeFinalConversation } =
|
const { conversation: maybeFinalConversation } =
|
||||||
conversationController.maybeMergeContacts({
|
conversationController.maybeMergeContacts({
|
||||||
aci: pairFromServer.aci,
|
aci: pairFromServer.aci,
|
||||||
pni: pairFromServer.pni,
|
pni: pairFromServer.pni,
|
||||||
e164,
|
e164: e164ToUse,
|
||||||
reason: 'updateConversationsWithUuidLookup',
|
reason: 'updateConversationsWithUuidLookup',
|
||||||
});
|
});
|
||||||
assertDev(
|
assertDev(
|
||||||
|
|
|
@ -6,11 +6,73 @@ import type { WebAPIType } from '../textsecure/WebAPI';
|
||||||
import type { AciString } from '../types/ServiceId';
|
import type { AciString } from '../types/ServiceId';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { isDirectConversation, isMe } from './whatTypeOfConversation';
|
import { isDirectConversation, isMe } from './whatTypeOfConversation';
|
||||||
|
import { parseNumber } from './libphonenumberUtil';
|
||||||
|
|
||||||
|
type PhoneNumberTransformation = {
|
||||||
|
oldPattern: RegExp;
|
||||||
|
newPattern: RegExp;
|
||||||
|
oldToNew: (e164: string) => string;
|
||||||
|
newToOld: (e164: string) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PHONE_TRANSFORMATIONS: Record<string, PhoneNumberTransformation> = {
|
||||||
|
'229': {
|
||||||
|
// Benin
|
||||||
|
oldPattern: /^\+229\d{8}$/,
|
||||||
|
newPattern: /^\+22901\d{8}$/,
|
||||||
|
oldToNew: (e164: string) => e164.replace(/^\+229(\d{8})$/, '+22901$1'),
|
||||||
|
newToOld: (e164: string) => e164.replace(/^\+22901(\d{8})$/, '+229$1'),
|
||||||
|
},
|
||||||
|
'52': {
|
||||||
|
// Mexico
|
||||||
|
oldPattern: /^\+521\d{10}$/,
|
||||||
|
newPattern: /^\+52\d{10}$/,
|
||||||
|
oldToNew: (e164: string) => e164.replace(/^\+521(\d{10})$/, '+52$1'),
|
||||||
|
newToOld: (e164: string) => e164.replace(/^\+52(\d{10})$/, '+521$1'),
|
||||||
|
},
|
||||||
|
'54': {
|
||||||
|
// Argentina
|
||||||
|
oldPattern: /^\+54\d{10}$/,
|
||||||
|
newPattern: /^\+549\d{10}$/,
|
||||||
|
oldToNew: (e164: string) => e164.replace(/^\+54(\d{10})$/, '+549$1'),
|
||||||
|
newToOld: (e164: string) => e164.replace(/^\+549(\d{10})$/, '+54$1'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
type ReturnType = CDSResponseType & {
|
||||||
|
// Maps from provided E164 phone numbers to their alternate representations
|
||||||
|
// found in CDSI. If a E164 appears as a key in this map, you should use the
|
||||||
|
// corresponding E164 value for any subsequent operations, as that's
|
||||||
|
// the format stored in CDSI's database.
|
||||||
|
transformedE164s: Map<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
export async function getServiceIdsForE164s(
|
export async function getServiceIdsForE164s(
|
||||||
server: Pick<WebAPIType, 'cdsLookup'>,
|
server: Pick<WebAPIType, 'cdsLookup'>,
|
||||||
e164s: ReadonlyArray<string>
|
e164s: ReadonlyArray<string>
|
||||||
): Promise<CDSResponseType> {
|
): Promise<ReturnType> {
|
||||||
|
const expandedE164s = new Set(e164s);
|
||||||
|
|
||||||
|
const transformationMap = new Map<string, string>();
|
||||||
|
|
||||||
|
for (const e164 of e164s) {
|
||||||
|
const parsedNumber = parseNumber(e164);
|
||||||
|
|
||||||
|
if (parsedNumber.isValidNumber && parsedNumber.countryCode) {
|
||||||
|
const transform = PHONE_TRANSFORMATIONS[parsedNumber.countryCode];
|
||||||
|
if (transform) {
|
||||||
|
if (transform.oldPattern.test(e164)) {
|
||||||
|
const newFormat = transform.oldToNew(e164);
|
||||||
|
expandedE164s.add(newFormat);
|
||||||
|
transformationMap.set(e164, newFormat);
|
||||||
|
} else if (transform.newPattern.test(e164)) {
|
||||||
|
const oldFormat = transform.newToOld(e164);
|
||||||
|
expandedE164s.add(oldFormat);
|
||||||
|
transformationMap.set(e164, oldFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Note: these have no relationship to supplied e164s. We just provide
|
// Note: these have no relationship to supplied e164s. We just provide
|
||||||
// all available information to the server so that it could return as many
|
// all available information to the server so that it could return as many
|
||||||
// ACI+PNI+E164 matches as possible.
|
// ACI+PNI+E164 matches as possible.
|
||||||
|
@ -35,16 +97,40 @@ export async function getServiceIdsForE164s(
|
||||||
acisAndAccessKeys.push({ aci, accessKey });
|
acisAndAccessKeys.push({ aci, accessKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const expandedE164sArray = Array.from(expandedE164s);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`getServiceIdsForE164s(${e164s}): acis=${acisAndAccessKeys.length} ` +
|
`getServiceIdsForE164s(${expandedE164sArray}): acis=${acisAndAccessKeys.length} ` +
|
||||||
`accessKeys=${acisAndAccessKeys.length}`
|
`accessKeys=${acisAndAccessKeys.length}`
|
||||||
);
|
);
|
||||||
return server.cdsLookup({
|
const response = await server.cdsLookup({
|
||||||
e164s,
|
e164s: expandedE164sArray,
|
||||||
acisAndAccessKeys,
|
acisAndAccessKeys,
|
||||||
returnAcisWithoutUaks: false,
|
returnAcisWithoutUaks: false,
|
||||||
useLibsignal: window.Signal.RemoteConfig.isEnabled(
|
useLibsignal: window.Signal.RemoteConfig.isEnabled(
|
||||||
'desktop.cdsiViaLibsignal'
|
'desktop.cdsiViaLibsignal'
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const e164sWithVariantsInCdsi = new Map(
|
||||||
|
Array.from(transformationMap).filter(([providedE164, alternateE164]) => {
|
||||||
|
if (
|
||||||
|
response.entries.has(providedE164) &&
|
||||||
|
response.entries.has(alternateE164)
|
||||||
|
) {
|
||||||
|
log.warn(`both ${providedE164} and ${alternateE164} are in CDSI`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (response.entries.has(alternateE164)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
transformedE164s: e164sWithVariantsInCdsi,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,18 +71,18 @@ export async function lookupConversationWithoutServiceId(
|
||||||
try {
|
try {
|
||||||
let conversationId: string | undefined;
|
let conversationId: string | undefined;
|
||||||
if (options.type === 'e164') {
|
if (options.type === 'e164') {
|
||||||
const { entries: serverLookup } = await getServiceIdsForE164s(server, [
|
const { entries: serverLookup, transformedE164s } =
|
||||||
options.e164,
|
await getServiceIdsForE164s(server, [options.e164]);
|
||||||
]);
|
const e164ToUse = transformedE164s.get(options.e164) ?? options.e164;
|
||||||
|
|
||||||
const maybePair = serverLookup.get(options.e164);
|
const maybePair = serverLookup.get(e164ToUse);
|
||||||
|
|
||||||
if (maybePair) {
|
if (maybePair) {
|
||||||
const { conversation } =
|
const { conversation } =
|
||||||
window.ConversationController.maybeMergeContacts({
|
window.ConversationController.maybeMergeContacts({
|
||||||
aci: maybePair.aci,
|
aci: maybePair.aci,
|
||||||
pni: maybePair.pni,
|
pni: maybePair.pni,
|
||||||
e164: options.e164,
|
e164: e164ToUse,
|
||||||
reason: 'startNewConversationWithoutUuid(e164)',
|
reason: 'startNewConversationWithoutUuid(e164)',
|
||||||
});
|
});
|
||||||
conversationId = conversation?.id;
|
conversationId = conversation?.id;
|
||||||
|
|
Loading…
Reference in a new issue