Better types for WebAPI
This commit is contained in:
parent
c05d23e628
commit
b9d6497cb1
22 changed files with 156 additions and 107 deletions
|
@ -8,6 +8,7 @@ import { render, unstable_batchedUpdates as batchedUpdates } from 'react-dom';
|
||||||
|
|
||||||
import MessageReceiver from './textsecure/MessageReceiver';
|
import MessageReceiver from './textsecure/MessageReceiver';
|
||||||
import { SessionResetsType, ProcessedDataMessage } from './textsecure/Types.d';
|
import { SessionResetsType, ProcessedDataMessage } from './textsecure/Types.d';
|
||||||
|
import { HTTPError } from './textsecure/Errors';
|
||||||
import {
|
import {
|
||||||
MessageAttributesType,
|
MessageAttributesType,
|
||||||
ConversationAttributesType,
|
ConversationAttributesType,
|
||||||
|
@ -1797,7 +1798,7 @@ export async function startApp(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await window.Signal.RemoteConfig.maybeRefreshRemoteConfig(server);
|
await window.Signal.RemoteConfig.maybeRefreshRemoteConfig(server);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error && window._.isNumber(error.code)) {
|
if (error instanceof HTTPError) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`registerForActive: Failed to to refresh remote config. Code: ${error.code}`
|
`registerForActive: Failed to to refresh remote config. Code: ${error.code}`
|
||||||
);
|
);
|
||||||
|
@ -3403,8 +3404,7 @@ export async function startApp(): Promise<void> {
|
||||||
log.error('background onError:', Errors.toLogFormat(error));
|
log.error('background onError:', Errors.toLogFormat(error));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
error &&
|
error instanceof HTTPError &&
|
||||||
error.name === 'HTTPError' &&
|
|
||||||
(error.code === 401 || error.code === 403)
|
(error.code === 401 || error.code === 403)
|
||||||
) {
|
) {
|
||||||
unlinkAndDisconnect(RemoveAllConfiguration.Full);
|
unlinkAndDisconnect(RemoveAllConfiguration.Full);
|
||||||
|
@ -3412,8 +3412,7 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
error &&
|
error instanceof HTTPError &&
|
||||||
error.name === 'HTTPError' &&
|
|
||||||
(error.code === -1 || error.code === 502)
|
(error.code === -1 || error.code === 502)
|
||||||
) {
|
) {
|
||||||
// Failed to connect to server
|
// Failed to connect to server
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { isOlderThan } from './util/timestamp';
|
||||||
import { parseRetryAfter } from './util/parseRetryAfter';
|
import { parseRetryAfter } from './util/parseRetryAfter';
|
||||||
import { getEnvironment, Environment } from './environment';
|
import { getEnvironment, Environment } from './environment';
|
||||||
import { StorageInterface } from './types/Storage.d';
|
import { StorageInterface } from './types/Storage.d';
|
||||||
|
import { HTTPError } from './textsecure/Errors';
|
||||||
import * as log from './logging/log';
|
import * as log from './logging/log';
|
||||||
|
|
||||||
export type ChallengeResponse = {
|
export type ChallengeResponse = {
|
||||||
|
@ -454,8 +455,7 @@ export class ChallengeHandler {
|
||||||
await this.options.sendChallengeResponse(data);
|
await this.options.sendChallengeResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
!(error instanceof Error) ||
|
!(error instanceof HTTPError) ||
|
||||||
error.name !== 'HTTPError' ||
|
|
||||||
error.code !== 413 ||
|
error.code !== 413 ||
|
||||||
!error.responseHeaders
|
!error.responseHeaders
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { action } from '@storybook/addon-actions';
|
||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
|
|
||||||
import { setupI18n } from '../../../util/setupI18n';
|
import { setupI18n } from '../../../util/setupI18n';
|
||||||
|
import { CapabilityError } from '../../../types/errors';
|
||||||
import enMessages from '../../../../_locales/en/messages.json';
|
import enMessages from '../../../../_locales/en/messages.json';
|
||||||
import { ConversationDetails, Props } from './ConversationDetails';
|
import { ConversationDetails, Props } from './ConversationDetails';
|
||||||
import { ConversationType } from '../../../state/ducks/conversations';
|
import { ConversationType } from '../../../state/ducks/conversations';
|
||||||
|
@ -152,9 +153,7 @@ story.add('Group add with missing capabilities', () => (
|
||||||
{...createProps()}
|
{...createProps()}
|
||||||
canEditGroupInfo
|
canEditGroupInfo
|
||||||
addMembers={async () => {
|
addMembers={async () => {
|
||||||
const error = new Error();
|
throw new CapabilityError('stories');
|
||||||
error.code = 'E_NO_CAPABILITY';
|
|
||||||
throw error;
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { getMutedUntilText } from '../../../util/getMutedUntilText';
|
||||||
|
|
||||||
import { LocalizerType } from '../../../types/Util';
|
import { LocalizerType } from '../../../types/Util';
|
||||||
import { MediaItemType } from '../../../types/MediaItem';
|
import { MediaItemType } from '../../../types/MediaItem';
|
||||||
|
import { CapabilityError } from '../../../types/errors';
|
||||||
import { missingCaseError } from '../../../util/missingCaseError';
|
import { missingCaseError } from '../../../util/missingCaseError';
|
||||||
|
|
||||||
import { DisappearingTimerSelect } from '../../DisappearingTimerSelect';
|
import { DisappearingTimerSelect } from '../../DisappearingTimerSelect';
|
||||||
|
@ -224,7 +225,7 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
||||||
setModalState(ModalState.NothingOpen);
|
setModalState(ModalState.NothingOpen);
|
||||||
setAddGroupMembersRequestState(RequestState.Inactive);
|
setAddGroupMembersRequestState(RequestState.Inactive);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'E_NO_CAPABILITY') {
|
if (err instanceof CapabilityError) {
|
||||||
setMembersMissingCapability(true);
|
setMembersMissingCapability(true);
|
||||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import type { LoggerType } from '../../types/Logging';
|
import type { LoggerType } from '../../types/Logging';
|
||||||
import { parseIntWithFallback } from '../../util/parseIntWithFallback';
|
import { parseIntWithFallback } from '../../util/parseIntWithFallback';
|
||||||
|
import { HTTPError } from '../../textsecure/Errors';
|
||||||
import { sleepFor413RetryAfterTimeIfApplicable } from './sleepFor413RetryAfterTimeIfApplicable';
|
import { sleepFor413RetryAfterTimeIfApplicable } from './sleepFor413RetryAfterTimeIfApplicable';
|
||||||
|
|
||||||
export async function handleCommonJobRequestError({
|
export async function handleCommonJobRequestError({
|
||||||
|
@ -14,7 +15,7 @@ export async function handleCommonJobRequestError({
|
||||||
log: LoggerType;
|
log: LoggerType;
|
||||||
timeRemaining: number;
|
timeRemaining: number;
|
||||||
}>): Promise<void> {
|
}>): Promise<void> {
|
||||||
if (!(err instanceof Error)) {
|
if (!(err instanceof HTTPError)) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { LoggerType } from '../../types/Logging';
|
||||||
import { sleep } from '../../util/sleep';
|
import { sleep } from '../../util/sleep';
|
||||||
import { parseRetryAfter } from '../../util/parseRetryAfter';
|
import { parseRetryAfter } from '../../util/parseRetryAfter';
|
||||||
import { isRecord } from '../../util/isRecord';
|
import { isRecord } from '../../util/isRecord';
|
||||||
|
import { HTTPError } from '../../textsecure/Errors';
|
||||||
|
|
||||||
export async function sleepFor413RetryAfterTimeIfApplicable({
|
export async function sleepFor413RetryAfterTimeIfApplicable({
|
||||||
err,
|
err,
|
||||||
|
@ -17,7 +18,7 @@ export async function sleepFor413RetryAfterTimeIfApplicable({
|
||||||
}>): Promise<void> {
|
}>): Promise<void> {
|
||||||
if (
|
if (
|
||||||
timeRemaining <= 0 ||
|
timeRemaining <= 0 ||
|
||||||
!(err instanceof Error) ||
|
!(err instanceof HTTPError) ||
|
||||||
err.code !== 413 ||
|
err.code !== 413 ||
|
||||||
!isRecord(err.responseHeaders)
|
!isRecord(err.responseHeaders)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { getSendOptions } from '../util/getSendOptions';
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
import { handleMessageSend } from '../util/handleMessageSend';
|
import { handleMessageSend } from '../util/handleMessageSend';
|
||||||
import type { CallbackResultType } from '../textsecure/Types.d';
|
import type { CallbackResultType } from '../textsecure/Types.d';
|
||||||
|
import { HTTPError } from '../textsecure/Errors';
|
||||||
import { isSent } from '../messages/MessageSendState';
|
import { isSent } from '../messages/MessageSendState';
|
||||||
import { getLastChallengeError, isOutgoing } from '../state/selectors/message';
|
import { getLastChallengeError, isOutgoing } from '../state/selectors/message';
|
||||||
import { parseIntWithFallback } from '../util/parseIntWithFallback';
|
import { parseIntWithFallback } from '../util/parseIntWithFallback';
|
||||||
|
@ -340,7 +341,7 @@ export class NormalMessageSendJobQueue extends JobQueue<NormalMessageSendJobData
|
||||||
let maybe413Error: undefined | Error;
|
let maybe413Error: undefined | Error;
|
||||||
messageSendErrors.forEach((messageSendError: unknown) => {
|
messageSendErrors.forEach((messageSendError: unknown) => {
|
||||||
formattedMessageSendErrors.push(Errors.toLogFormat(messageSendError));
|
formattedMessageSendErrors.push(Errors.toLogFormat(messageSendError));
|
||||||
if (!(messageSendError instanceof Error)) {
|
if (!(messageSendError instanceof HTTPError)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (parseIntWithFallback(messageSendError.code, -1)) {
|
switch (parseIntWithFallback(messageSendError.code, -1)) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { JobQueue } from './JobQueue';
|
||||||
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
|
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
|
||||||
import { parseIntWithFallback } from '../util/parseIntWithFallback';
|
import { parseIntWithFallback } from '../util/parseIntWithFallback';
|
||||||
import type { WebAPIType } from '../textsecure/WebAPI';
|
import type { WebAPIType } from '../textsecure/WebAPI';
|
||||||
|
import { HTTPError } from '../textsecure/Errors';
|
||||||
|
|
||||||
const RETRY_WAIT_TIME = durations.MINUTE;
|
const RETRY_WAIT_TIME = durations.MINUTE;
|
||||||
const RETRYABLE_4XX_FAILURE_STATUSES = new Set([
|
const RETRYABLE_4XX_FAILURE_STATUSES = new Set([
|
||||||
|
@ -83,7 +84,7 @@ export class ReportSpamJobQueue extends JobQueue<ReportSpamJobData> {
|
||||||
map(serverGuids, serverGuid => server.reportMessage(e164, serverGuid))
|
map(serverGuids, serverGuid => server.reportMessage(e164, serverGuid))
|
||||||
);
|
);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (!(err instanceof Error)) {
|
if (!(err instanceof HTTPError)) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
import { AttachmentType } from '../types/Attachment';
|
import { AttachmentType } from '../types/Attachment';
|
||||||
import { CallMode, CallHistoryDetailsType } from '../types/Calling';
|
import { CallMode, CallHistoryDetailsType } from '../types/Calling';
|
||||||
import * as Stickers from '../types/Stickers';
|
import * as Stickers from '../types/Stickers';
|
||||||
|
import { CapabilityError } from '../types/errors';
|
||||||
import type {
|
import type {
|
||||||
GroupV1InfoType,
|
GroupV1InfoType,
|
||||||
GroupV2InfoType,
|
GroupV2InfoType,
|
||||||
|
@ -1889,11 +1890,9 @@ export class ConversationModel extends window.Backbone
|
||||||
return Boolean(model?.get('capabilities')?.announcementGroup);
|
return Boolean(model?.get('capabilities')?.announcementGroup);
|
||||||
});
|
});
|
||||||
if (!isEveryMemberCapable) {
|
if (!isEveryMemberCapable) {
|
||||||
const error = new Error(
|
throw new CapabilityError(
|
||||||
'addMembersV2: some or all members need to upgrade.'
|
'addMembersV2: some or all members need to upgrade.'
|
||||||
);
|
);
|
||||||
error.code = 'E_NO_CAPABILITY';
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1902,7 +1902,7 @@ export class CallingClass {
|
||||||
throw new Error('getCallSettings: offline!');
|
throw new Error('getCallSettings: offline!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const iceServerJson = await window.textsecure.messaging.server.getIceServers();
|
const iceServer = await window.textsecure.messaging.server.getIceServers();
|
||||||
|
|
||||||
const shouldRelayCalls = window.Events.getAlwaysRelayCalls();
|
const shouldRelayCalls = window.Events.getAlwaysRelayCalls();
|
||||||
|
|
||||||
|
@ -1910,7 +1910,7 @@ export class CallingClass {
|
||||||
const isContactUnknown = !conversation.isFromOrAddedByTrustedContact();
|
const isContactUnknown = !conversation.isFromOrAddedByTrustedContact();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
iceServer: JSON.parse(iceServerJson),
|
iceServer,
|
||||||
hideIp: shouldRelayCalls || isContactUnknown,
|
hideIp: shouldRelayCalls || isContactUnknown,
|
||||||
bandwidthMode: BandwidthMode.Normal,
|
bandwidthMode: BandwidthMode.Normal,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,8 @@ import PQueue from 'p-queue';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
|
||||||
import EventTarget from './EventTarget';
|
import EventTarget from './EventTarget';
|
||||||
import { WebAPIType } from './WebAPI';
|
import type { WebAPIType } from './WebAPI';
|
||||||
|
import { HTTPError } from './Errors';
|
||||||
import { KeyPairType, CompatSignedPreKeyType } from './Types.d';
|
import { KeyPairType, CompatSignedPreKeyType } from './Types.d';
|
||||||
import utils from './Helpers';
|
import utils from './Helpers';
|
||||||
import ProvisioningCipher from './ProvisioningCipher';
|
import ProvisioningCipher from './ProvisioningCipher';
|
||||||
|
@ -29,6 +30,7 @@ import {
|
||||||
generateSignedPreKey,
|
generateSignedPreKey,
|
||||||
generatePreKey,
|
generatePreKey,
|
||||||
} from '../Curve';
|
} from '../Curve';
|
||||||
|
import { UUID } from '../types/UUID';
|
||||||
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
|
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
|
||||||
import { ourProfileKeyService } from '../services/ourProfileKey';
|
import { ourProfileKeyService } from '../services/ourProfileKey';
|
||||||
import { assert, strictAssert } from '../util/assert';
|
import { assert, strictAssert } from '../util/assert';
|
||||||
|
@ -390,8 +392,7 @@ export default class AccountManager extends EventTarget {
|
||||||
log.error('rotateSignedPrekey error:', e && e.stack ? e.stack : e);
|
log.error('rotateSignedPrekey error:', e && e.stack ? e.stack : e);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
e instanceof Error &&
|
e instanceof HTTPError &&
|
||||||
e.name === 'HTTPError' &&
|
|
||||||
e.code &&
|
e.code &&
|
||||||
e.code >= 400 &&
|
e.code >= 400 &&
|
||||||
e.code <= 599
|
e.code <= 599
|
||||||
|
@ -589,7 +590,7 @@ export default class AccountManager extends EventTarget {
|
||||||
|
|
||||||
// update our own identity key, which may have changed
|
// update our own identity key, which may have changed
|
||||||
// if we're relinking after a reinstall on the master device
|
// if we're relinking after a reinstall on the master device
|
||||||
await storage.protocol.saveIdentityWithAttributes(ourUuid, {
|
await storage.protocol.saveIdentityWithAttributes(new UUID(ourUuid), {
|
||||||
publicKey: identityKeyPair.pubKey,
|
publicKey: identityKeyPair.pubKey,
|
||||||
firstUse: true,
|
firstUse: true,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2020-2021 Signal Messenger, LLC
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
|
|
||||||
import { parseRetryAfter } from '../util/parseRetryAfter';
|
import { parseRetryAfter } from '../util/parseRetryAfter';
|
||||||
|
@ -102,14 +101,14 @@ export class OutgoingIdentityKeyError extends ReplayableError {
|
||||||
export class OutgoingMessageError extends ReplayableError {
|
export class OutgoingMessageError extends ReplayableError {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
|
||||||
code?: any;
|
code?: number;
|
||||||
|
|
||||||
// Note: Data to resend message is no longer captured
|
// Note: Data to resend message is no longer captured
|
||||||
constructor(
|
constructor(
|
||||||
incomingIdentifier: string,
|
incomingIdentifier: string,
|
||||||
_m: unknown,
|
_m: unknown,
|
||||||
_t: unknown,
|
_t: unknown,
|
||||||
httpError?: Error
|
httpError?: HTTPError
|
||||||
) {
|
) {
|
||||||
const identifier = incomingIdentifier.split('.')[0];
|
const identifier = incomingIdentifier.split('.')[0];
|
||||||
|
|
||||||
|
@ -128,11 +127,13 @@ export class OutgoingMessageError extends ReplayableError {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SendMessageNetworkError extends ReplayableError {
|
export class SendMessageNetworkError extends ReplayableError {
|
||||||
|
code: number;
|
||||||
|
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
|
||||||
responseHeaders?: HeaderListType | undefined;
|
responseHeaders?: HeaderListType | undefined;
|
||||||
|
|
||||||
constructor(identifier: string, _m: unknown, httpError: Error) {
|
constructor(identifier: string, _m: unknown, httpError: HTTPError) {
|
||||||
super({
|
super({
|
||||||
name: 'SendMessageNetworkError',
|
name: 'SendMessageNetworkError',
|
||||||
message: httpError.message,
|
message: httpError.message,
|
||||||
|
@ -152,13 +153,15 @@ export type SendMessageChallengeData = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SendMessageChallengeError extends ReplayableError {
|
export class SendMessageChallengeError extends ReplayableError {
|
||||||
|
public code: number;
|
||||||
|
|
||||||
public identifier: string;
|
public identifier: string;
|
||||||
|
|
||||||
public readonly data: SendMessageChallengeData | undefined;
|
public readonly data: SendMessageChallengeData | undefined;
|
||||||
|
|
||||||
public readonly retryAfter: number;
|
public readonly retryAfter: number;
|
||||||
|
|
||||||
constructor(identifier: string, httpError: Error) {
|
constructor(identifier: string, httpError: HTTPError) {
|
||||||
super({
|
super({
|
||||||
name: 'SendMessageChallengeError',
|
name: 'SendMessageChallengeError',
|
||||||
message: httpError.message,
|
message: httpError.message,
|
||||||
|
@ -166,7 +169,7 @@ export class SendMessageChallengeError extends ReplayableError {
|
||||||
|
|
||||||
[this.identifier] = identifier.split('.');
|
[this.identifier] = identifier.split('.');
|
||||||
this.code = httpError.code;
|
this.code = httpError.code;
|
||||||
this.data = httpError.response;
|
this.data = httpError.response as SendMessageChallengeData;
|
||||||
|
|
||||||
const headers = httpError.responseHeaders || {};
|
const headers = httpError.responseHeaders || {};
|
||||||
|
|
||||||
|
@ -241,9 +244,9 @@ export class SignedPreKeyRotationError extends ReplayableError {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessageError extends ReplayableError {
|
export class MessageError extends ReplayableError {
|
||||||
code?: any;
|
code: number;
|
||||||
|
|
||||||
constructor(_m: unknown, httpError: Error) {
|
constructor(_m: unknown, httpError: HTTPError) {
|
||||||
super({
|
super({
|
||||||
name: 'MessageError',
|
name: 'MessageError',
|
||||||
message: httpError.message,
|
message: httpError.message,
|
||||||
|
@ -258,9 +261,9 @@ export class MessageError extends ReplayableError {
|
||||||
export class UnregisteredUserError extends Error {
|
export class UnregisteredUserError extends Error {
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
|
||||||
code?: any;
|
code: number;
|
||||||
|
|
||||||
constructor(identifier: string, httpError: Error) {
|
constructor(identifier: string, httpError: HTTPError) {
|
||||||
const { message } = httpError;
|
const { message } = httpError;
|
||||||
|
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -282,3 +285,5 @@ export class UnregisteredUserError extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConnectTimeoutError extends Error {}
|
export class ConnectTimeoutError extends Error {}
|
||||||
|
|
||||||
|
export class WarnOnlyError extends Error {}
|
||||||
|
|
|
@ -63,6 +63,7 @@ import { IncomingWebSocketRequest } from './WebsocketResources';
|
||||||
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
||||||
import type { WebAPIType } from './WebAPI';
|
import type { WebAPIType } from './WebAPI';
|
||||||
import type { Storage } from './Storage';
|
import type { Storage } from './Storage';
|
||||||
|
import { WarnOnlyError } from './Errors';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
import {
|
import {
|
||||||
ProcessedDataMessage,
|
ProcessedDataMessage,
|
||||||
|
@ -922,7 +923,7 @@ export default class MessageReceiver
|
||||||
':',
|
':',
|
||||||
Errors.toLogFormat(error),
|
Errors.toLogFormat(error),
|
||||||
];
|
];
|
||||||
if (error.warn) {
|
if (error instanceof WarnOnlyError) {
|
||||||
log.warn(...args);
|
log.warn(...args);
|
||||||
} else {
|
} else {
|
||||||
log.error(...args);
|
log.error(...args);
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
UnidentifiedSenderMessageContent,
|
UnidentifiedSenderMessageContent,
|
||||||
} from '@signalapp/signal-client';
|
} from '@signalapp/signal-client';
|
||||||
|
|
||||||
import { WebAPIType } from './WebAPI';
|
import type { WebAPIType } from './WebAPI';
|
||||||
import { SendMetadataType, SendOptionsType } from './SendMessage';
|
import { SendMetadataType, SendOptionsType } from './SendMessage';
|
||||||
import {
|
import {
|
||||||
OutgoingIdentityKeyError,
|
OutgoingIdentityKeyError,
|
||||||
|
@ -29,6 +29,7 @@ import {
|
||||||
SendMessageNetworkError,
|
SendMessageNetworkError,
|
||||||
SendMessageChallengeError,
|
SendMessageChallengeError,
|
||||||
UnregisteredUserError,
|
UnregisteredUserError,
|
||||||
|
HTTPError,
|
||||||
} from './Errors';
|
} from './Errors';
|
||||||
import { CallbackResultType, CustomError } from './Types.d';
|
import { CallbackResultType, CustomError } from './Types.d';
|
||||||
import { isValidNumber } from '../types/PhoneNumber';
|
import { isValidNumber } from '../types/PhoneNumber';
|
||||||
|
@ -221,7 +222,7 @@ export default class OutgoingMessage {
|
||||||
): void {
|
): void {
|
||||||
let error = providedError;
|
let error = providedError;
|
||||||
|
|
||||||
if (!error || (error.name === 'HTTPError' && error.code !== 404)) {
|
if (!error || (error instanceof HTTPError && error.code !== 404)) {
|
||||||
if (error && error.code === 428) {
|
if (error && error.code === 428) {
|
||||||
error = new SendMessageChallengeError(identifier, error);
|
error = new SendMessageChallengeError(identifier, error);
|
||||||
} else {
|
} else {
|
||||||
|
@ -313,7 +314,7 @@ export default class OutgoingMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.catch(e => {
|
return promise.catch(e => {
|
||||||
if (e.name === 'HTTPError' && e.code !== 409 && e.code !== 410) {
|
if (e instanceof HTTPError && e.code !== 409 && e.code !== 410) {
|
||||||
// 409 and 410 should bubble and be handled by doSendMessage
|
// 409 and 410 should bubble and be handled by doSendMessage
|
||||||
// 404 should throw UnregisteredUserError
|
// 404 should throw UnregisteredUserError
|
||||||
// 428 should throw SendMessageChallengeError
|
// 428 should throw SendMessageChallengeError
|
||||||
|
@ -517,7 +518,10 @@ export default class OutgoingMessage {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async (error: Error) => {
|
async (error: Error) => {
|
||||||
if (error.code === 401 || error.code === 403) {
|
if (
|
||||||
|
error instanceof HTTPError &&
|
||||||
|
(error.code === 401 || error.code === 403)
|
||||||
|
) {
|
||||||
if (this.failoverIdentifiers.indexOf(identifier) === -1) {
|
if (this.failoverIdentifiers.indexOf(identifier) === -1) {
|
||||||
this.failoverIdentifiers.push(identifier);
|
this.failoverIdentifiers.push(identifier);
|
||||||
}
|
}
|
||||||
|
@ -556,8 +560,7 @@ export default class OutgoingMessage {
|
||||||
})
|
})
|
||||||
.catch(async error => {
|
.catch(async error => {
|
||||||
if (
|
if (
|
||||||
error instanceof Error &&
|
error instanceof HTTPError &&
|
||||||
error.name === 'HTTPError' &&
|
|
||||||
(error.code === 410 || error.code === 409)
|
(error.code === 410 || error.code === 409)
|
||||||
) {
|
) {
|
||||||
if (!recurse) {
|
if (!recurse) {
|
||||||
|
@ -569,15 +572,20 @@ export default class OutgoingMessage {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = error.response as {
|
||||||
|
extraDevices?: Array<number>;
|
||||||
|
staleDevices?: Array<number>;
|
||||||
|
missingDevices?: Array<number>;
|
||||||
|
};
|
||||||
let p: Promise<any> = Promise.resolve();
|
let p: Promise<any> = Promise.resolve();
|
||||||
if (error.code === 409) {
|
if (error.code === 409) {
|
||||||
p = this.removeDeviceIdsForIdentifier(
|
p = this.removeDeviceIdsForIdentifier(
|
||||||
identifier,
|
identifier,
|
||||||
error.response.extraDevices || []
|
response.extraDevices || []
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
p = Promise.all(
|
p = Promise.all(
|
||||||
error.response.staleDevices.map(async (deviceId: number) => {
|
(response.staleDevices || []).map(async (deviceId: number) => {
|
||||||
await window.textsecure.storage.protocol.archiveSession(
|
await window.textsecure.storage.protocol.archiveSession(
|
||||||
new QualifiedAddress(
|
new QualifiedAddress(
|
||||||
ourUuid,
|
ourUuid,
|
||||||
|
@ -591,8 +599,8 @@ export default class OutgoingMessage {
|
||||||
return p.then(async () => {
|
return p.then(async () => {
|
||||||
const resetDevices =
|
const resetDevices =
|
||||||
error.code === 410
|
error.code === 410
|
||||||
? error.response.staleDevices
|
? response.staleDevices
|
||||||
: error.response.missingDevices;
|
: response.missingDevices;
|
||||||
return this.getKeysForIdentifier(identifier, resetDevices).then(
|
return this.getKeysForIdentifier(identifier, resetDevices).then(
|
||||||
// We continue to retry as long as the error code was 409; the assumption is
|
// We continue to retry as long as the error code was 409; the assumption is
|
||||||
// that we'll request new device info and the next request will succeed.
|
// that we'll request new device info and the next request will succeed.
|
||||||
|
@ -678,7 +686,10 @@ export default class OutgoingMessage {
|
||||||
if (!uuid) {
|
if (!uuid) {
|
||||||
throw new UnregisteredUserError(
|
throw new UnregisteredUserError(
|
||||||
identifier,
|
identifier,
|
||||||
new Error('User is not registered')
|
new HTTPError('User is not registered', {
|
||||||
|
code: -1,
|
||||||
|
headers: {},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
identifier = uuid;
|
identifier = uuid;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
|
|
||||||
import { Dictionary } from 'lodash';
|
import { Dictionary } from 'lodash';
|
||||||
|
import Long from 'long';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import {
|
import {
|
||||||
PlaintextContent,
|
PlaintextContent,
|
||||||
|
@ -54,6 +55,7 @@ import {
|
||||||
MessageError,
|
MessageError,
|
||||||
SignedPreKeyRotationError,
|
SignedPreKeyRotationError,
|
||||||
SendMessageProtoError,
|
SendMessageProtoError,
|
||||||
|
HTTPError,
|
||||||
} from './Errors';
|
} from './Errors';
|
||||||
import { BodyRangesType } from '../types/Util';
|
import { BodyRangesType } from '../types/Util';
|
||||||
import {
|
import {
|
||||||
|
@ -526,7 +528,7 @@ export default class MessageSender {
|
||||||
const id = await this.server.putAttachment(result.ciphertext);
|
const id = await this.server.putAttachment(result.ciphertext);
|
||||||
|
|
||||||
const proto = new Proto.AttachmentPointer();
|
const proto = new Proto.AttachmentPointer();
|
||||||
proto.cdnId = id;
|
proto.cdnId = Long.fromString(id);
|
||||||
proto.contentType = attachment.contentType;
|
proto.contentType = attachment.contentType;
|
||||||
proto.key = new FIXMEU8(key);
|
proto.key = new FIXMEU8(key);
|
||||||
proto.size = attachment.size;
|
proto.size = attachment.size;
|
||||||
|
@ -563,7 +565,7 @@ export default class MessageSender {
|
||||||
message.attachmentPointers = attachmentPointers;
|
message.attachmentPointers = attachmentPointers;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error instanceof Error && error.name === 'HTTPError') {
|
if (error instanceof HTTPError) {
|
||||||
throw new MessageError(message, error);
|
throw new MessageError(message, error);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -584,7 +586,7 @@ export default class MessageSender {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
message.preview = preview;
|
message.preview = preview;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error && error.name === 'HTTPError') {
|
if (error instanceof HTTPError) {
|
||||||
throw new MessageError(message, error);
|
throw new MessageError(message, error);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -609,7 +611,7 @@ export default class MessageSender {
|
||||||
attachmentPointer: await this.makeAttachmentPointer(sticker.data),
|
attachmentPointer: await this.makeAttachmentPointer(sticker.data),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error && error.name === 'HTTPError') {
|
if (error instanceof HTTPError) {
|
||||||
throw new MessageError(message, error);
|
throw new MessageError(message, error);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -637,7 +639,7 @@ export default class MessageSender {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
).catch(error => {
|
).catch(error => {
|
||||||
if (error instanceof Error && error.name === 'HTTPError') {
|
if (error instanceof HTTPError) {
|
||||||
throw new MessageError(message, error);
|
throw new MessageError(message, error);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
|
import { HTTPError } from './Errors';
|
||||||
|
|
||||||
export async function handleStatusCode(status: number): Promise<void> {
|
export async function handleStatusCode(status: number): Promise<void> {
|
||||||
if (status === 499) {
|
if (status === 499) {
|
||||||
|
@ -11,7 +12,7 @@ export async function handleStatusCode(status: number): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function translateError(error: Error): Error | undefined {
|
export function translateError(error: HTTPError): HTTPError | undefined {
|
||||||
const { code } = error;
|
const { code } = error;
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
// Happens sometimes when we get no response. Might be nice to get 204 instead.
|
// Happens sometimes when we get no response. Might be nice to get 204 instead.
|
||||||
|
|
|
@ -598,7 +598,7 @@ async function _retryAjax(
|
||||||
const limit = providedLimit || 3;
|
const limit = providedLimit || 3;
|
||||||
|
|
||||||
return _promiseAjax(url, options).catch(async (e: Error) => {
|
return _promiseAjax(url, options).catch(async (e: Error) => {
|
||||||
if (e.name === 'HTTPError' && e.code === -1 && count < limit) {
|
if (e instanceof HTTPError && e.code === -1 && count < limit) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(_retryAjax(url, options, limit, count));
|
resolve(_retryAjax(url, options, limit, count));
|
||||||
|
@ -615,17 +615,6 @@ async function _outerAjax(url: string | null, options: PromiseAjaxOptionsType) {
|
||||||
return _retryAjax(url, options);
|
return _retryAjax(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
|
||||||
// We want to extend `Error`, so we need an interface.
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
interface Error {
|
|
||||||
code?: number | string;
|
|
||||||
response?: any;
|
|
||||||
responseHeaders?: HeaderListType;
|
|
||||||
warn?: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeHTTPError(
|
function makeHTTPError(
|
||||||
message: string,
|
message: string,
|
||||||
providedCode: number,
|
providedCode: number,
|
||||||
|
@ -722,7 +711,7 @@ type InitializeOptionsType = {
|
||||||
version: string;
|
version: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MessageType = any;
|
type MessageType = unknown;
|
||||||
|
|
||||||
type AjaxOptionsType = {
|
type AjaxOptionsType = {
|
||||||
accessKey?: string;
|
accessKey?: string;
|
||||||
|
@ -737,7 +726,7 @@ type AjaxOptionsType = {
|
||||||
password?: string;
|
password?: string;
|
||||||
redactUrl?: RedactUrl;
|
redactUrl?: RedactUrl;
|
||||||
responseType?: 'json' | 'arraybuffer' | 'arraybufferwithdetails';
|
responseType?: 'json' | 'arraybuffer' | 'arraybufferwithdetails';
|
||||||
schema?: any;
|
schema?: unknown;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
unauthenticated?: boolean;
|
unauthenticated?: boolean;
|
||||||
urlParameters?: string;
|
urlParameters?: string;
|
||||||
|
@ -768,7 +757,7 @@ export type CapabilitiesUploadType = {
|
||||||
changeNumber: true;
|
changeNumber: true;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StickerPackManifestType = any;
|
type StickerPackManifestType = ArrayBuffer;
|
||||||
|
|
||||||
export type GroupCredentialType = {
|
export type GroupCredentialType = {
|
||||||
credential: string;
|
credential: string;
|
||||||
|
@ -808,6 +797,20 @@ const uploadAvatarHeadersZod = z
|
||||||
.passthrough();
|
.passthrough();
|
||||||
export type UploadAvatarHeadersType = z.infer<typeof uploadAvatarHeadersZod>;
|
export type UploadAvatarHeadersType = z.infer<typeof uploadAvatarHeadersZod>;
|
||||||
|
|
||||||
|
export type ProfileType = Readonly<{
|
||||||
|
identityKey?: string;
|
||||||
|
name?: string;
|
||||||
|
about?: string;
|
||||||
|
aboutEmoji?: string;
|
||||||
|
avatar?: string;
|
||||||
|
unidentifiedAccess?: string;
|
||||||
|
unrestrictedUnidentifiedAccess?: string;
|
||||||
|
username?: string;
|
||||||
|
uuid?: string;
|
||||||
|
credential?: string;
|
||||||
|
capabilities?: unknown;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type WebAPIType = {
|
export type WebAPIType = {
|
||||||
confirmCode: (
|
confirmCode: (
|
||||||
number: string,
|
number: string,
|
||||||
|
@ -816,14 +819,21 @@ export type WebAPIType = {
|
||||||
registrationId: number,
|
registrationId: number,
|
||||||
deviceName?: string | null,
|
deviceName?: string | null,
|
||||||
options?: { accessKey?: ArrayBuffer; uuid?: string }
|
options?: { accessKey?: ArrayBuffer; uuid?: string }
|
||||||
) => Promise<any>;
|
) => Promise<{ uuid?: string; deviceId: number }>;
|
||||||
createGroup: (
|
createGroup: (
|
||||||
group: Proto.IGroup,
|
group: Proto.IGroup,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getAttachment: (cdnKey: string, cdnNumber?: number) => Promise<any>;
|
getAttachment: (cdnKey: string, cdnNumber?: number) => Promise<ArrayBuffer>;
|
||||||
getAvatar: (path: string) => Promise<any>;
|
getAvatar: (path: string) => Promise<ArrayBuffer>;
|
||||||
getDevices: () => Promise<any>;
|
getDevices: () => Promise<
|
||||||
|
Array<{
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
lastSeen: number;
|
||||||
|
created: number;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
getGroup: (options: GroupCredentialsType) => Promise<Proto.Group>;
|
getGroup: (options: GroupCredentialsType) => Promise<Proto.Group>;
|
||||||
getGroupFromLink: (
|
getGroupFromLink: (
|
||||||
inviteLinkPassword: string,
|
inviteLinkPassword: string,
|
||||||
|
@ -841,7 +851,11 @@ export type WebAPIType = {
|
||||||
startVersion: number,
|
startVersion: number,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<GroupLogResponseType>;
|
) => Promise<GroupLogResponseType>;
|
||||||
getIceServers: () => Promise<any>;
|
getIceServers: () => Promise<{
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
urls: Array<string>;
|
||||||
|
}>;
|
||||||
getKeysForIdentifier: (
|
getKeysForIdentifier: (
|
||||||
identifier: string,
|
identifier: string,
|
||||||
deviceId?: number
|
deviceId?: number
|
||||||
|
@ -858,7 +872,7 @@ export type WebAPIType = {
|
||||||
profileKeyVersion?: string;
|
profileKeyVersion?: string;
|
||||||
profileKeyCredentialRequest?: string;
|
profileKeyCredentialRequest?: string;
|
||||||
}
|
}
|
||||||
) => Promise<any>;
|
) => Promise<ProfileType>;
|
||||||
getProfileUnauth: (
|
getProfileUnauth: (
|
||||||
identifier: string,
|
identifier: string,
|
||||||
options: {
|
options: {
|
||||||
|
@ -866,7 +880,7 @@ export type WebAPIType = {
|
||||||
profileKeyVersion?: string;
|
profileKeyVersion?: string;
|
||||||
profileKeyCredentialRequest?: string;
|
profileKeyCredentialRequest?: string;
|
||||||
}
|
}
|
||||||
) => Promise<any>;
|
) => Promise<ProfileType>;
|
||||||
getProvisioningResource: (
|
getProvisioningResource: (
|
||||||
handler: IRequestHandler
|
handler: IRequestHandler
|
||||||
) => Promise<WebSocketResource>;
|
) => Promise<WebSocketResource>;
|
||||||
|
@ -892,7 +906,13 @@ export type WebAPIType = {
|
||||||
makeProxiedRequest: (
|
makeProxiedRequest: (
|
||||||
targetUrl: string,
|
targetUrl: string,
|
||||||
options?: ProxiedRequestOptionsType
|
options?: ProxiedRequestOptionsType
|
||||||
) => Promise<any>;
|
) => Promise<
|
||||||
|
| ArrayBufferWithDetailsType
|
||||||
|
| {
|
||||||
|
result: ArrayBufferWithDetailsType;
|
||||||
|
totalSize: number;
|
||||||
|
}
|
||||||
|
>;
|
||||||
makeSfuRequest: (
|
makeSfuRequest: (
|
||||||
targetUrl: string,
|
targetUrl: string,
|
||||||
type: HTTPCodeType,
|
type: HTTPCodeType,
|
||||||
|
@ -905,7 +925,7 @@ export type WebAPIType = {
|
||||||
inviteLinkBase64?: string
|
inviteLinkBase64?: string
|
||||||
) => Promise<Proto.IGroupChange>;
|
) => Promise<Proto.IGroupChange>;
|
||||||
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
modifyStorageRecords: MessageSender['modifyStorageRecords'];
|
||||||
putAttachment: (encryptedBin: ArrayBuffer) => Promise<any>;
|
putAttachment: (encryptedBin: ArrayBuffer) => Promise<string>;
|
||||||
putProfile: (
|
putProfile: (
|
||||||
jsonData: ProfileRequestDataType
|
jsonData: ProfileRequestDataType
|
||||||
) => Promise<UploadAvatarHeadersType | undefined>;
|
) => Promise<UploadAvatarHeadersType | undefined>;
|
||||||
|
@ -916,10 +936,10 @@ export type WebAPIType = {
|
||||||
onProgress?: () => void
|
onProgress?: () => void
|
||||||
) => Promise<string>;
|
) => Promise<string>;
|
||||||
registerKeys: (genKeys: KeysType) => Promise<void>;
|
registerKeys: (genKeys: KeysType) => Promise<void>;
|
||||||
registerSupportForUnauthenticatedDelivery: () => Promise<any>;
|
registerSupportForUnauthenticatedDelivery: () => Promise<void>;
|
||||||
reportMessage: (senderE164: string, serverGuid: string) => Promise<void>;
|
reportMessage: (senderE164: string, serverGuid: string) => Promise<void>;
|
||||||
requestVerificationSMS: (number: string) => Promise<any>;
|
requestVerificationSMS: (number: string) => Promise<void>;
|
||||||
requestVerificationVoice: (number: string) => Promise<any>;
|
requestVerificationVoice: (number: string) => Promise<void>;
|
||||||
sendMessages: (
|
sendMessages: (
|
||||||
destination: string,
|
destination: string,
|
||||||
messageArray: Array<MessageType>,
|
messageArray: Array<MessageType>,
|
||||||
|
@ -949,8 +969,11 @@ export type WebAPIType = {
|
||||||
avatarData: Uint8Array,
|
avatarData: Uint8Array,
|
||||||
options: GroupCredentialsType
|
options: GroupCredentialsType
|
||||||
) => Promise<string>;
|
) => Promise<string>;
|
||||||
whoami: () => Promise<any>;
|
whoami: () => Promise<{
|
||||||
sendChallengeResponse: (challengeResponse: ChallengeType) => Promise<any>;
|
uuid?: string;
|
||||||
|
number?: string;
|
||||||
|
}>;
|
||||||
|
sendChallengeResponse: (challengeResponse: ChallengeType) => Promise<void>;
|
||||||
getConfig: () => Promise<
|
getConfig: () => Promise<
|
||||||
Array<{ name: string; enabled: boolean; value: string | null }>
|
Array<{ name: string; enabled: boolean; value: string | null }>
|
||||||
>;
|
>;
|
||||||
|
@ -1188,6 +1211,9 @@ export function initialize({
|
||||||
unauthenticated: param.unauthenticated,
|
unauthenticated: param.unauthenticated,
|
||||||
accessKey: param.accessKey,
|
accessKey: param.accessKey,
|
||||||
}).catch((e: Error) => {
|
}).catch((e: Error) => {
|
||||||
|
if (!(e instanceof HTTPError)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
const translatedError = translateError(e);
|
const translatedError = translateError(e);
|
||||||
if (translatedError) {
|
if (translatedError) {
|
||||||
throw translatedError;
|
throw translatedError;
|
||||||
|
@ -1458,7 +1484,7 @@ export function initialize({
|
||||||
async function getAvatar(path: string) {
|
async function getAvatar(path: string) {
|
||||||
// Using _outerAJAX, since it's not hardcoded to the Signal Server. Unlike our
|
// Using _outerAJAX, since it's not hardcoded to the Signal Server. Unlike our
|
||||||
// attachment CDN, it uses our self-signed certificate, so we pass it in.
|
// attachment CDN, it uses our self-signed certificate, so we pass it in.
|
||||||
return _outerAjax(`${cdnUrlObject['0']}/${path}`, {
|
return (await _outerAjax(`${cdnUrlObject['0']}/${path}`, {
|
||||||
certificateAuthority,
|
certificateAuthority,
|
||||||
contentType: 'application/octet-stream',
|
contentType: 'application/octet-stream',
|
||||||
proxyUrl,
|
proxyUrl,
|
||||||
|
@ -1470,7 +1496,7 @@ export function initialize({
|
||||||
return href.replace(pattern, `[REDACTED]${path.slice(-3)}`);
|
return href.replace(pattern, `[REDACTED]${path.slice(-3)}`);
|
||||||
},
|
},
|
||||||
version,
|
version,
|
||||||
});
|
})) as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reportMessage(
|
async function reportMessage(
|
||||||
|
@ -1571,6 +1597,7 @@ export function initialize({
|
||||||
return _ajax({
|
return _ajax({
|
||||||
call: 'getIceServers',
|
call: 'getIceServers',
|
||||||
httpType: 'GET',
|
httpType: 'GET',
|
||||||
|
responseType: 'json',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1832,7 +1859,7 @@ export function initialize({
|
||||||
if (!isPackIdValid(packId)) {
|
if (!isPackIdValid(packId)) {
|
||||||
throw new Error('getSticker: pack ID was invalid');
|
throw new Error('getSticker: pack ID was invalid');
|
||||||
}
|
}
|
||||||
return _outerAjax(
|
return (await _outerAjax(
|
||||||
`${cdnUrlObject['0']}/stickers/${packId}/full/${stickerId}`,
|
`${cdnUrlObject['0']}/stickers/${packId}/full/${stickerId}`,
|
||||||
{
|
{
|
||||||
certificateAuthority,
|
certificateAuthority,
|
||||||
|
@ -1842,14 +1869,14 @@ export function initialize({
|
||||||
redactUrl: redactStickerUrl,
|
redactUrl: redactStickerUrl,
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
);
|
)) as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getStickerPackManifest(packId: string) {
|
async function getStickerPackManifest(packId: string) {
|
||||||
if (!isPackIdValid(packId)) {
|
if (!isPackIdValid(packId)) {
|
||||||
throw new Error('getStickerPackManifest: pack ID was invalid');
|
throw new Error('getStickerPackManifest: pack ID was invalid');
|
||||||
}
|
}
|
||||||
return _outerAjax(
|
return (await _outerAjax(
|
||||||
`${cdnUrlObject['0']}/stickers/${packId}/manifest.proto`,
|
`${cdnUrlObject['0']}/stickers/${packId}/manifest.proto`,
|
||||||
{
|
{
|
||||||
certificateAuthority,
|
certificateAuthority,
|
||||||
|
@ -1859,7 +1886,7 @@ export function initialize({
|
||||||
redactUrl: redactStickerUrl,
|
redactUrl: redactStickerUrl,
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
);
|
)) as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerAttachmentType = {
|
type ServerAttachmentType = {
|
||||||
|
@ -1989,7 +2016,7 @@ export function initialize({
|
||||||
? cdnUrlObject[cdnNumber] || cdnUrlObject['0']
|
? cdnUrlObject[cdnNumber] || cdnUrlObject['0']
|
||||||
: cdnUrlObject['0'];
|
: cdnUrlObject['0'];
|
||||||
// This is going to the CDN, not the service, so we use _outerAjax
|
// This is going to the CDN, not the service, so we use _outerAjax
|
||||||
return _outerAjax(`${cdnUrl}/attachments/${cdnKey}`, {
|
return (await _outerAjax(`${cdnUrl}/attachments/${cdnKey}`, {
|
||||||
certificateAuthority,
|
certificateAuthority,
|
||||||
proxyUrl,
|
proxyUrl,
|
||||||
responseType: 'arraybuffer',
|
responseType: 'arraybuffer',
|
||||||
|
@ -1997,7 +2024,7 @@ export function initialize({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
redactUrl: _createRedactor(cdnKey),
|
redactUrl: _createRedactor(cdnKey),
|
||||||
version,
|
version,
|
||||||
});
|
})) as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function putAttachment(encryptedBin: ArrayBuffer) {
|
async function putAttachment(encryptedBin: ArrayBuffer) {
|
||||||
|
@ -2066,7 +2093,7 @@ export function initialize({
|
||||||
headers.Range = `bytes=${start}-${end}`;
|
headers.Range = `bytes=${start}-${end}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await _outerAjax(targetUrl, {
|
const result = (await _outerAjax(targetUrl, {
|
||||||
responseType: returnArrayBuffer ? 'arraybufferwithdetails' : undefined,
|
responseType: returnArrayBuffer ? 'arraybufferwithdetails' : undefined,
|
||||||
proxyUrl: contentProxyUrl,
|
proxyUrl: contentProxyUrl,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
|
@ -2074,7 +2101,7 @@ export function initialize({
|
||||||
redactUrl: () => '[REDACTED_URL]',
|
redactUrl: () => '[REDACTED_URL]',
|
||||||
headers,
|
headers,
|
||||||
version,
|
version,
|
||||||
});
|
})) as ArrayBufferWithDetailsType;
|
||||||
|
|
||||||
if (!returnArrayBuffer) {
|
if (!returnArrayBuffer) {
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
} from '@signalapp/signal-client';
|
} from '@signalapp/signal-client';
|
||||||
|
|
||||||
import { UnregisteredUserError } from './Errors';
|
import { UnregisteredUserError, HTTPError } from './Errors';
|
||||||
import { Sessions, IdentityKeys } from '../LibSignalStores';
|
import { Sessions, IdentityKeys } from '../LibSignalStores';
|
||||||
import { Address } from '../types/Address';
|
import { Address } from '../types/Address';
|
||||||
import { QualifiedAddress } from '../types/QualifiedAddress';
|
import { QualifiedAddress } from '../types/QualifiedAddress';
|
||||||
|
@ -35,7 +35,7 @@ export async function getKeysForIdentifier(
|
||||||
accessKeyFailed,
|
accessKeyFailed,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'HTTPError' && error.code === 404) {
|
if (error instanceof HTTPError && error.code === 404) {
|
||||||
const theirUuid = UUID.lookup(identifier);
|
const theirUuid = UUID.lookup(identifier);
|
||||||
|
|
||||||
if (theirUuid) {
|
if (theirUuid) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
ProcessedReaction,
|
ProcessedReaction,
|
||||||
ProcessedDelete,
|
ProcessedDelete,
|
||||||
} from './Types.d';
|
} from './Types.d';
|
||||||
|
import { WarnOnlyError } from './Errors';
|
||||||
|
|
||||||
// TODO: remove once we move away from ArrayBuffers
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
const FIXMEU8 = Uint8Array;
|
const FIXMEU8 = Uint8Array;
|
||||||
|
@ -335,11 +336,9 @@ export async function processDataMessage(
|
||||||
// Cleaned up in `processGroupContext`
|
// Cleaned up in `processGroupContext`
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const err = new Error(
|
throw new WarnOnlyError(
|
||||||
`Unknown group message type: ${result.group.type}`
|
`Unknown group message type: ${result.group.type}`
|
||||||
);
|
);
|
||||||
err.warn = true;
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,5 @@ export function toLogFormat(error: unknown): string {
|
||||||
|
|
||||||
return String(error);
|
return String(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CapabilityError extends Error {}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
GroupSendOptionsType,
|
GroupSendOptionsType,
|
||||||
SendOptionsType,
|
SendOptionsType,
|
||||||
} from '../textsecure/SendMessage';
|
} from '../textsecure/SendMessage';
|
||||||
|
import { HTTPError } from '../textsecure/Errors';
|
||||||
import { IdentityKeys, SenderKeys, Sessions } from '../LibSignalStores';
|
import { IdentityKeys, SenderKeys, Sessions } from '../LibSignalStores';
|
||||||
import { ConversationModel } from '../models/conversations';
|
import { ConversationModel } from '../models/conversations';
|
||||||
import { DeviceType, CallbackResultType } from '../textsecure/Types.d';
|
import { DeviceType, CallbackResultType } from '../textsecure/Types.d';
|
||||||
|
@ -696,7 +697,7 @@ function isIdentifierRegistered(identifier: string) {
|
||||||
return !isUnregistered;
|
return !isUnregistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle409Response(logId: string, error: Error) {
|
async function handle409Response(logId: string, error: HTTPError) {
|
||||||
const parsed = multiRecipient409ResponseSchema.safeParse(error.response);
|
const parsed = multiRecipient409ResponseSchema.safeParse(error.response);
|
||||||
if (parsed.success) {
|
if (parsed.success) {
|
||||||
await _waitForAll({
|
await _waitForAll({
|
||||||
|
@ -734,7 +735,7 @@ async function handle409Response(logId: string, error: Error) {
|
||||||
|
|
||||||
async function handle410Response(
|
async function handle410Response(
|
||||||
conversation: ConversationModel,
|
conversation: ConversationModel,
|
||||||
error: Error
|
error: HTTPError
|
||||||
) {
|
) {
|
||||||
const logId = conversation.idForLogging();
|
const logId = conversation.idForLogging();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||||
|
import { HTTPError } from '../textsecure/Errors';
|
||||||
|
|
||||||
window.Whisper = window.Whisper || {};
|
window.Whisper = window.Whisper || {};
|
||||||
const { Whisper } = window;
|
const { Whisper } = window;
|
||||||
|
@ -47,19 +48,19 @@ Whisper.InstallView = Whisper.View.extend({
|
||||||
|
|
||||||
if (this.error) {
|
if (this.error) {
|
||||||
if (
|
if (
|
||||||
this.error.name === 'HTTPError' &&
|
this.error instanceof HTTPError &&
|
||||||
this.error.code === TOO_MANY_DEVICES
|
this.error.code === TOO_MANY_DEVICES
|
||||||
) {
|
) {
|
||||||
errorMessage = window.i18n('installTooManyDevices');
|
errorMessage = window.i18n('installTooManyDevices');
|
||||||
} else if (
|
} else if (
|
||||||
this.error.name === 'HTTPError' &&
|
this.error instanceof HTTPError &&
|
||||||
this.error.code === TOO_OLD
|
this.error.code === TOO_OLD
|
||||||
) {
|
) {
|
||||||
errorMessage = window.i18n('installTooOld');
|
errorMessage = window.i18n('installTooOld');
|
||||||
errorButton = window.i18n('upgrade');
|
errorButton = window.i18n('upgrade');
|
||||||
errorSecondButton = window.i18n('quit');
|
errorSecondButton = window.i18n('quit');
|
||||||
} else if (
|
} else if (
|
||||||
this.error.name === 'HTTPError' &&
|
this.error instanceof HTTPError &&
|
||||||
this.error.code === CONNECTION_ERROR
|
this.error.code === CONNECTION_ERROR
|
||||||
) {
|
) {
|
||||||
errorMessage = window.i18n('installConnectionFailed');
|
errorMessage = window.i18n('installConnectionFailed');
|
||||||
|
@ -102,11 +103,7 @@ Whisper.InstallView = Whisper.View.extend({
|
||||||
window.shutdown();
|
window.shutdown();
|
||||||
},
|
},
|
||||||
async connect() {
|
async connect() {
|
||||||
if (
|
if (this.error instanceof HTTPError && this.error.code === TOO_OLD) {
|
||||||
this.error &&
|
|
||||||
this.error.name === 'HTTPError' &&
|
|
||||||
this.error.code === TOO_OLD
|
|
||||||
) {
|
|
||||||
openLinkInWebBrowser('https://signal.org/download');
|
openLinkInWebBrowser('https://signal.org/download');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +139,7 @@ Whisper.InstallView = Whisper.View.extend({
|
||||||
if (error.message === 'websocket closed') {
|
if (error.message === 'websocket closed') {
|
||||||
this.trigger('disconnected');
|
this.trigger('disconnected');
|
||||||
} else if (
|
} else if (
|
||||||
error.name !== 'HTTPError' ||
|
!(error instanceof HTTPError) ||
|
||||||
(error.code !== CONNECTION_ERROR && error.code !== TOO_MANY_DEVICES)
|
(error.code !== CONNECTION_ERROR && error.code !== TOO_MANY_DEVICES)
|
||||||
) {
|
) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
Loading…
Add table
Reference in a new issue