Use libsignal-client for parsing crash reports
This commit is contained in:
parent
d7f0978c6d
commit
9ad6d5b66b
18 changed files with 86 additions and 241 deletions
|
@ -1,17 +1,34 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { app, clipboard, crashReporter, ipcMain as ipc } from 'electron';
|
import { app, crashReporter, ipcMain as ipc } from 'electron';
|
||||||
import { realpath, readdir, readFile, unlink } from 'fs-extra';
|
import { realpath, readdir, readFile, unlink, stat } from 'fs-extra';
|
||||||
import { basename, join } from 'path';
|
import { basename, join } from 'path';
|
||||||
|
import { toJSONString as dumpToJSONString } from '@signalapp/libsignal-client/dist/Minidump';
|
||||||
|
import z from 'zod';
|
||||||
|
|
||||||
import type { LoggerType } from '../ts/types/Logging';
|
import type { LoggerType } from '../ts/types/Logging';
|
||||||
import * as Errors from '../ts/types/errors';
|
import * as Errors from '../ts/types/errors';
|
||||||
import { isAlpha } from '../ts/util/version';
|
import { isAlpha } from '../ts/util/version';
|
||||||
import { upload as uploadDebugLog } from '../ts/logging/uploadDebugLog';
|
|
||||||
import { SignalService as Proto } from '../ts/protobuf';
|
|
||||||
import OS from '../ts/util/os/osMain';
|
import OS from '../ts/util/os/osMain';
|
||||||
|
|
||||||
|
const dumpSchema = z
|
||||||
|
.object({
|
||||||
|
crashing_thread: z
|
||||||
|
.object({
|
||||||
|
frames: z
|
||||||
|
.object({
|
||||||
|
registers: z.unknown(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.passthrough();
|
||||||
|
|
||||||
async function getPendingDumps(): Promise<ReadonlyArray<string>> {
|
async function getPendingDumps(): Promise<ReadonlyArray<string>> {
|
||||||
const crashDumpsPath = await realpath(app.getPath('crashDumps'));
|
const crashDumpsPath = await realpath(app.getPath('crashDumps'));
|
||||||
let pendingDir: string;
|
let pendingDir: string;
|
||||||
|
@ -46,7 +63,11 @@ async function eraseDumps(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setup(getLogger: () => LoggerType, forceEnable = false): void {
|
export function setup(
|
||||||
|
getLogger: () => LoggerType,
|
||||||
|
showDebugLogWindow: () => Promise<void>,
|
||||||
|
forceEnable = false
|
||||||
|
): void {
|
||||||
const isEnabled = isAlpha(app.getVersion()) || forceEnable;
|
const isEnabled = isAlpha(app.getVersion()) || forceEnable;
|
||||||
|
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
|
@ -68,7 +89,7 @@ export function setup(getLogger: () => LoggerType, forceEnable = false): void {
|
||||||
return pendingDumps.length;
|
return pendingDumps.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.handle('crash-reports:upload', async () => {
|
ipc.handle('crash-reports:write-to-log', async () => {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,17 +100,26 @@ export function setup(getLogger: () => LoggerType, forceEnable = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = getLogger();
|
const logger = getLogger();
|
||||||
logger.warn(`crashReports: uploading ${pendingDumps.length} dumps`);
|
logger.warn(`crashReports: logging ${pendingDumps.length} dumps`);
|
||||||
|
|
||||||
const maybeDumps = await Promise.all(
|
await Promise.all(
|
||||||
pendingDumps.map(async fullPath => {
|
pendingDumps.map(async fullPath => {
|
||||||
try {
|
try {
|
||||||
return {
|
const content = await readFile(fullPath);
|
||||||
filename: basename(fullPath),
|
const { mtime } = await stat(fullPath);
|
||||||
content: await readFile(fullPath),
|
|
||||||
};
|
const dump = dumpSchema.parse(JSON.parse(dumpToJSONString(content)));
|
||||||
} catch (error) {
|
for (const frame of dump.crashing_thread?.frames ?? []) {
|
||||||
|
delete frame.registers;
|
||||||
|
}
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
`crashReports: dump=${basename(fullPath)} ` +
|
||||||
|
`mtime=${JSON.stringify(mtime)}`,
|
||||||
|
JSON.stringify(dump, null, 2)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
`crashReports: failed to read crash report ${fullPath} due to error`,
|
`crashReports: failed to read crash report ${fullPath} due to error`,
|
||||||
Errors.toLogFormat(error)
|
Errors.toLogFormat(error)
|
||||||
);
|
);
|
||||||
|
@ -98,30 +128,8 @@ export function setup(getLogger: () => LoggerType, forceEnable = false): void {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = Proto.CrashReportList.encode({
|
await eraseDumps(logger, pendingDumps);
|
||||||
reports: maybeDumps.filter(
|
await showDebugLogWindow();
|
||||||
(dump): dump is { filename: string; content: Buffer } => {
|
|
||||||
return dump !== undefined;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}).finish();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = await uploadDebugLog({
|
|
||||||
content,
|
|
||||||
appVersion: app.getVersion(),
|
|
||||||
logger,
|
|
||||||
extension: 'dmp',
|
|
||||||
contentType: 'application/octet-stream',
|
|
||||||
compress: false,
|
|
||||||
prefix: 'desktop-crash-',
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(`crashReports: upload complete, ${url}`);
|
|
||||||
clipboard.writeText(url);
|
|
||||||
} finally {
|
|
||||||
await eraseDumps(logger, pendingDumps);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.handle('crash-reports:erase', async () => {
|
ipc.handle('crash-reports:erase', async () => {
|
||||||
|
|
|
@ -211,7 +211,7 @@ const FORCE_ENABLE_CRASH_REPORTS = process.argv.some(
|
||||||
|
|
||||||
const CLI_LANG = cliOptions.lang as string | undefined;
|
const CLI_LANG = cliOptions.lang as string | undefined;
|
||||||
|
|
||||||
setupCrashReports(getLogger, FORCE_ENABLE_CRASH_REPORTS);
|
setupCrashReports(getLogger, showDebugLogWindow, FORCE_ENABLE_CRASH_REPORTS);
|
||||||
|
|
||||||
let sendDummyKeystroke: undefined | (() => void);
|
let sendDummyKeystroke: undefined | (() => void);
|
||||||
if (OS.isWindows()) {
|
if (OS.isWindows()) {
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
"@react-aria/utils": "3.16.0",
|
"@react-aria/utils": "3.16.0",
|
||||||
"@react-spring/web": "9.5.5",
|
"@react-spring/web": "9.5.5",
|
||||||
"@signalapp/better-sqlite3": "8.6.0",
|
"@signalapp/better-sqlite3": "8.6.0",
|
||||||
"@signalapp/libsignal-client": "0.39.1",
|
"@signalapp/libsignal-client": "0.40.0",
|
||||||
"@signalapp/ringrtc": "2.37.1",
|
"@signalapp/ringrtc": "2.37.1",
|
||||||
"@signalapp/windows-dummy-keystroke": "1.0.0",
|
"@signalapp/windows-dummy-keystroke": "1.0.0",
|
||||||
"@types/fabric": "4.5.3",
|
"@types/fabric": "4.5.3",
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
package signalservice;
|
|
||||||
|
|
||||||
message CrashReport {
|
|
||||||
string filename = 1;
|
|
||||||
bytes content = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CrashReportList {
|
|
||||||
repeated CrashReport reports = 1;
|
|
||||||
}
|
|
|
@ -1356,7 +1356,9 @@ 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 serviceIdMap = await getServiceIdsForE164s(server, [e164]);
|
const { entries: serviceIdMap } = await getServiceIdsForE164s(server, [
|
||||||
|
e164,
|
||||||
|
]);
|
||||||
|
|
||||||
const pni = serviceIdMap.get(e164)?.pni;
|
const pni = serviceIdMap.get(e164)?.pni;
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ export function Basic(): JSX.Element {
|
||||||
<CrashReportDialog
|
<CrashReportDialog
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
isPending={isPending}
|
isPending={isPending}
|
||||||
uploadCrashReports={async () => {
|
writeCrashReportsToLog={async () => {
|
||||||
setIsPending(true);
|
setIsPending(true);
|
||||||
action('uploadCrashReports')();
|
action('writeCrashReportsToLog')();
|
||||||
await sleep(5000);
|
await sleep(5000);
|
||||||
setIsPending(false);
|
setIsPending(false);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Modal } from './Modal';
|
||||||
import { Spinner } from './Spinner';
|
import { Spinner } from './Spinner';
|
||||||
|
|
||||||
type PropsActionsType = {
|
type PropsActionsType = {
|
||||||
uploadCrashReports: () => void;
|
writeCrashReportsToLog: () => void;
|
||||||
eraseCrashReports: () => void;
|
eraseCrashReports: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export type PropsType = {
|
||||||
} & PropsActionsType;
|
} & PropsActionsType;
|
||||||
|
|
||||||
export function CrashReportDialog(props: Readonly<PropsType>): JSX.Element {
|
export function CrashReportDialog(props: Readonly<PropsType>): JSX.Element {
|
||||||
const { i18n, isPending, uploadCrashReports, eraseCrashReports } = props;
|
const { i18n, isPending, writeCrashReportsToLog, eraseCrashReports } = props;
|
||||||
|
|
||||||
const onEraseClick = (event: React.MouseEvent) => {
|
const onEraseClick = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -30,7 +30,7 @@ export function CrashReportDialog(props: Readonly<PropsType>): JSX.Element {
|
||||||
const onSubmitClick = (event: React.MouseEvent) => {
|
const onSubmitClick = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
uploadCrashReports();
|
writeCrashReportsToLog();
|
||||||
};
|
};
|
||||||
|
|
||||||
const footer = (
|
const footer = (
|
||||||
|
|
|
@ -240,7 +240,7 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
|
||||||
<CrashReportDialog
|
<CrashReportDialog
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
isPending={false}
|
isPending={false}
|
||||||
uploadCrashReports={action('uploadCrashReports')}
|
writeCrashReportsToLog={action('writeCrashReportsToLog')}
|
||||||
eraseCrashReports={action('eraseCrashReports')}
|
eraseCrashReports={action('eraseCrashReports')}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import fs from 'fs/promises';
|
|
||||||
import path from 'path';
|
|
||||||
import http from 'http';
|
|
||||||
import https from 'https';
|
|
||||||
import { tmpdir } from 'os';
|
|
||||||
import { execFile as rawExecFile } from 'child_process';
|
|
||||||
import { promisify } from 'util';
|
|
||||||
|
|
||||||
import { strictAssert } from '../util/assert';
|
|
||||||
import { wrapEventEmitterOnce } from '../util/wrapEventEmitterOnce';
|
|
||||||
import { SignalService as Proto } from '../protobuf';
|
|
||||||
|
|
||||||
const execFile = promisify(rawExecFile);
|
|
||||||
|
|
||||||
const TARGET_URL = 'https://symbols.electronjs.org';
|
|
||||||
const CLI_OPTIONS = [];
|
|
||||||
const CLI_ARGS = [];
|
|
||||||
|
|
||||||
for (const arg of process.argv.slice(2)) {
|
|
||||||
if (arg.startsWith('--')) {
|
|
||||||
CLI_OPTIONS.push(arg.slice(2));
|
|
||||||
} else {
|
|
||||||
CLI_ARGS.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [OUTPUT_DIR, ...CRASH_REPORTS] = CLI_ARGS;
|
|
||||||
|
|
||||||
main(OUTPUT_DIR, CRASH_REPORTS, CLI_OPTIONS).catch(error => {
|
|
||||||
console.error(error.stack);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function main(
|
|
||||||
outDir: string,
|
|
||||||
fileNames: ReadonlyArray<string>,
|
|
||||||
options: ReadonlyArray<string>
|
|
||||||
): Promise<void> {
|
|
||||||
await fs.mkdir(outDir, { recursive: true });
|
|
||||||
|
|
||||||
const substitutions = new Map<string, Buffer>();
|
|
||||||
await Promise.all(
|
|
||||||
options.map(async option => {
|
|
||||||
const match = option.match(/^sub:(.*)=(.*)$/);
|
|
||||||
if (!match) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
substitutions.set(match[1], await fs.readFile(match[2]));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const proxyServer = http
|
|
||||||
.createServer(async ({ method, url = '/' }, res) => {
|
|
||||||
console.log(`Proxy server got request ${method} ${url}`);
|
|
||||||
if (method !== 'GET') {
|
|
||||||
throw new Error('Unsupported');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [name, buffer] of substitutions) {
|
|
||||||
if (url.includes(name)) {
|
|
||||||
console.log(`Providing substitution for ${url}`);
|
|
||||||
res.end(buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
const patchedURL = url.replace(/signal-desktop-[^\/.]+/g, 'electron');
|
|
||||||
|
|
||||||
https.get(`${TARGET_URL}${patchedURL}`, remoteRes => {
|
|
||||||
res.writeHead(remoteRes.statusCode ?? 500, remoteRes.headers);
|
|
||||||
|
|
||||||
remoteRes.pipe(res);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.unref();
|
|
||||||
|
|
||||||
proxyServer.listen(0);
|
|
||||||
|
|
||||||
await wrapEventEmitterOnce(proxyServer, 'listening');
|
|
||||||
const addr = proxyServer.address();
|
|
||||||
strictAssert(
|
|
||||||
typeof addr === 'object' && addr != null,
|
|
||||||
'Address has to be an object'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { port: proxyPort } = addr;
|
|
||||||
|
|
||||||
console.log(`Proxy server listening on ${proxyPort}`);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
fileNames.map(fileName => symbolicate(outDir, fileName, proxyPort))
|
|
||||||
);
|
|
||||||
|
|
||||||
proxyServer.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function symbolicate(
|
|
||||||
outDir: string,
|
|
||||||
fileName: string,
|
|
||||||
proxyPort: number
|
|
||||||
): Promise<void> {
|
|
||||||
const tmpDir = await fs.mkdtemp(path.join(tmpdir(), 'signal-crashe'));
|
|
||||||
await fs.mkdir(tmpDir, { recursive: true });
|
|
||||||
|
|
||||||
const encoded = await fs.readFile(fileName);
|
|
||||||
let reports: ReadonlyArray<Proto.ICrashReport>;
|
|
||||||
if (fileName.endsWith('.raw')) {
|
|
||||||
reports = [
|
|
||||||
{
|
|
||||||
filename: 'report.dmp',
|
|
||||||
content: encoded,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
({ reports } = Proto.CrashReportList.decode(encoded));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name: prefix } = path.parse(fileName);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
reports.map(async ({ filename: reportName, content }) => {
|
|
||||||
if (!reportName || !content) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { base, name, ext } = path.parse(reportName);
|
|
||||||
if (ext !== '.dmp') {
|
|
||||||
console.log(`Ignoring ${reportName}, wrong extension`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dumpFile = path.join(tmpDir, `${prefix}-${base}`);
|
|
||||||
console.log(`Extracting to ${dumpFile}`);
|
|
||||||
await fs.writeFile(dumpFile, content);
|
|
||||||
|
|
||||||
const outFile = path.join(outDir, `${prefix}-${name}.txt`);
|
|
||||||
|
|
||||||
await execFile('minidump-stackwalk', [
|
|
||||||
'--symbols-url',
|
|
||||||
`http://127.0.0.1:${proxyPort}`,
|
|
||||||
'--output-file',
|
|
||||||
outFile,
|
|
||||||
dumpFile,
|
|
||||||
]);
|
|
||||||
console.log(`Symbolicating ${dumpFile} to ${outFile}`);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -93,7 +93,7 @@ function checkForAccount(
|
||||||
|
|
||||||
log.info(`checkForAccount: looking ${phoneNumber} up on server`);
|
log.info(`checkForAccount: looking ${phoneNumber} up on server`);
|
||||||
try {
|
try {
|
||||||
const serviceIdLookup = await getServiceIdsForE164s(server, [
|
const { entries: serviceIdLookup } = await getServiceIdsForE164s(server, [
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
]);
|
]);
|
||||||
const maybePair = serviceIdLookup.get(phoneNumber);
|
const maybePair = serviceIdLookup.get(phoneNumber);
|
||||||
|
|
|
@ -22,7 +22,7 @@ export type CrashReportsStateType = ReadonlyDeep<{
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
const SET_COUNT = 'crashReports/SET_COUNT';
|
const SET_COUNT = 'crashReports/SET_COUNT';
|
||||||
const UPLOAD = 'crashReports/UPLOAD';
|
const WRITE_TO_LOG = 'crashReports/WRITE_TO_LOG';
|
||||||
const ERASE = 'crashReports/ERASE';
|
const ERASE = 'crashReports/ERASE';
|
||||||
|
|
||||||
type SetCrashReportCountActionType = ReadonlyDeep<{
|
type SetCrashReportCountActionType = ReadonlyDeep<{
|
||||||
|
@ -32,7 +32,7 @@ type SetCrashReportCountActionType = ReadonlyDeep<{
|
||||||
|
|
||||||
type CrashReportsActionType = ReadonlyDeep<
|
type CrashReportsActionType = ReadonlyDeep<
|
||||||
| SetCrashReportCountActionType
|
| SetCrashReportCountActionType
|
||||||
| PromiseAction<typeof UPLOAD>
|
| PromiseAction<typeof WRITE_TO_LOG>
|
||||||
| PromiseAction<typeof ERASE>
|
| PromiseAction<typeof ERASE>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ type CrashReportsActionType = ReadonlyDeep<
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
setCrashReportCount,
|
setCrashReportCount,
|
||||||
uploadCrashReports,
|
writeCrashReportsToLog,
|
||||||
eraseCrashReports,
|
eraseCrashReports,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,23 +48,22 @@ function setCrashReportCount(count: number): SetCrashReportCountActionType {
|
||||||
return { type: SET_COUNT, payload: count };
|
return { type: SET_COUNT, payload: count };
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadCrashReports(): ThunkAction<
|
function writeCrashReportsToLog(): ThunkAction<
|
||||||
void,
|
void,
|
||||||
RootStateType,
|
RootStateType,
|
||||||
unknown,
|
unknown,
|
||||||
PromiseAction<typeof UPLOAD> | ShowToastActionType
|
PromiseAction<typeof WRITE_TO_LOG> | ShowToastActionType
|
||||||
> {
|
> {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
try {
|
||||||
await window.IPC.crashReports.upload();
|
await window.IPC.crashReports.writeToLog();
|
||||||
dispatch(showToast({ toastType: ToastType.LinkCopied }));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(showToast({ toastType: ToastType.DebugLogError }));
|
dispatch(showToast({ toastType: ToastType.DebugLogError }));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispatch({ type: UPLOAD, payload: run() });
|
dispatch({ type: WRITE_TO_LOG, payload: run() });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
action.type === `${UPLOAD}_PENDING` ||
|
action.type === `${WRITE_TO_LOG}_PENDING` ||
|
||||||
action.type === `${ERASE}_PENDING`
|
action.type === `${ERASE}_PENDING`
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
|
@ -118,7 +117,7 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
action.type === `${UPLOAD}_FULFILLED` ||
|
action.type === `${WRITE_TO_LOG}_FULFILLED` ||
|
||||||
action.type === `${ERASE}_FULFILLED`
|
action.type === `${ERASE}_FULFILLED`
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
|
@ -129,13 +128,13 @@ export function reducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
action.type === (`${UPLOAD}_REJECTED` as const) ||
|
action.type === (`${WRITE_TO_LOG}_REJECTED` as const) ||
|
||||||
action.type === (`${ERASE}_REJECTED` as const)
|
action.type === (`${ERASE}_REJECTED` as const)
|
||||||
) {
|
) {
|
||||||
const { error } = action;
|
const { error } = action;
|
||||||
|
|
||||||
log.error(
|
log.error(
|
||||||
`Failed to upload crash report due to error ${Errors.toLogFormat(error)}`
|
`Failed to write crash report due to error ${Errors.toLogFormat(error)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -153,7 +153,9 @@ describe('updateConversationsWithUuidLookup', () => {
|
||||||
|
|
||||||
sinonSandbox.stub(window.Signal.Data, 'updateConversation');
|
sinonSandbox.stub(window.Signal.Data, 'updateConversation');
|
||||||
|
|
||||||
fakeCdsLookup = sinonSandbox.stub().resolves(new Map());
|
fakeCdsLookup = sinonSandbox.stub().resolves({
|
||||||
|
entries: new Map(),
|
||||||
|
});
|
||||||
fakeCheckAccountExistence = sinonSandbox.stub().resolves(false);
|
fakeCheckAccountExistence = sinonSandbox.stub().resolves(false);
|
||||||
fakeServer = {
|
fakeServer = {
|
||||||
cdsLookup: fakeCdsLookup,
|
cdsLookup: fakeCdsLookup,
|
||||||
|
@ -198,12 +200,12 @@ describe('updateConversationsWithUuidLookup', () => {
|
||||||
const aci1 = generateAci();
|
const aci1 = generateAci();
|
||||||
const aci2 = generateAci();
|
const aci2 = generateAci();
|
||||||
|
|
||||||
fakeCdsLookup.resolves(
|
fakeCdsLookup.resolves({
|
||||||
new Map([
|
entries: new Map([
|
||||||
['+13215559876', { aci: aci1, pni: undefined }],
|
['+13215559876', { aci: aci1, pni: undefined }],
|
||||||
['+16545559876', { aci: aci2, pni: undefined }],
|
['+16545559876', { aci: aci2, pni: undefined }],
|
||||||
])
|
]),
|
||||||
);
|
});
|
||||||
|
|
||||||
await updateConversationsWithUuidLookup({
|
await updateConversationsWithUuidLookup({
|
||||||
conversationController: new FakeConversationController([
|
conversationController: new FakeConversationController([
|
||||||
|
|
|
@ -122,7 +122,7 @@ export abstract class CDSSocketBase<
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('CDSSocket.request(): done');
|
log.info('CDSSocket.request(): done');
|
||||||
return resultMap;
|
return { debugPermitsUsed: 0, entries: resultMap };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abstract methods
|
// Abstract methods
|
||||||
|
|
|
@ -27,7 +27,7 @@ export async function updateConversationsWithUuidLookup({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverLookup = await getServiceIdsForE164s(server, e164s);
|
const { entries: serverLookup } = await getServiceIdsForE164s(server, e164s);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
conversations.map(async conversation => {
|
conversations.map(async conversation => {
|
||||||
|
|
|
@ -69,7 +69,9 @@ export async function lookupConversationWithoutServiceId(
|
||||||
try {
|
try {
|
||||||
let conversationId: string | undefined;
|
let conversationId: string | undefined;
|
||||||
if (options.type === 'e164') {
|
if (options.type === 'e164') {
|
||||||
const serverLookup = await getServiceIdsForE164s(server, [options.e164]);
|
const { entries: serverLookup } = await getServiceIdsForE164s(server, [
|
||||||
|
options.e164,
|
||||||
|
]);
|
||||||
|
|
||||||
const maybePair = serverLookup.get(options.e164);
|
const maybePair = serverLookup.get(options.e164);
|
||||||
|
|
||||||
|
|
2
ts/window.d.ts
vendored
2
ts/window.d.ts
vendored
|
@ -61,7 +61,7 @@ export type IPCType = {
|
||||||
closeAbout: () => void;
|
closeAbout: () => void;
|
||||||
crashReports: {
|
crashReports: {
|
||||||
getCount: () => Promise<number>;
|
getCount: () => Promise<number>;
|
||||||
upload: () => Promise<void>;
|
writeToLog: () => Promise<void>;
|
||||||
erase: () => Promise<void>;
|
erase: () => Promise<void>;
|
||||||
};
|
};
|
||||||
drawAttention: () => void;
|
drawAttention: () => void;
|
||||||
|
|
|
@ -78,7 +78,7 @@ const IPC: IPCType = {
|
||||||
closeAbout: () => ipc.send('close-about'),
|
closeAbout: () => ipc.send('close-about'),
|
||||||
crashReports: {
|
crashReports: {
|
||||||
getCount: () => ipc.invoke('crash-reports:get-count'),
|
getCount: () => ipc.invoke('crash-reports:get-count'),
|
||||||
upload: () => ipc.invoke('crash-reports:upload'),
|
writeToLog: () => ipc.invoke('crash-reports:write-to-log'),
|
||||||
erase: () => ipc.invoke('crash-reports:erase'),
|
erase: () => ipc.invoke('crash-reports:erase'),
|
||||||
},
|
},
|
||||||
drawAttention: () => {
|
drawAttention: () => {
|
||||||
|
|
|
@ -3955,10 +3955,10 @@
|
||||||
bindings "^1.5.0"
|
bindings "^1.5.0"
|
||||||
tar "^6.1.0"
|
tar "^6.1.0"
|
||||||
|
|
||||||
"@signalapp/libsignal-client@0.39.1":
|
"@signalapp/libsignal-client@0.40.0":
|
||||||
version "0.39.1"
|
version "0.40.0"
|
||||||
resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.39.1.tgz#15b41f15c516ae3eecf8a098a9c9c7aac00444d7"
|
resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.40.0.tgz#2997f44b69d4d73aa474550243998b628d0d3a1a"
|
||||||
integrity sha512-Drna/0rQTa/jB475KssoBA86Da/DLdJYDznkbiFG2YD/OeWEKoDpi64bp+BIpnc2o16GnVhGLFzNvMfVkI41eQ==
|
integrity sha512-RrVe46KDHSfspvz+rmqkP2KMrlctWb6voejEFzhxSB8yXbnmEQuwEu7bqJj+uYsLhv8nKpIfC1qniNQc0SDPeA==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build "^4.2.3"
|
node-gyp-build "^4.2.3"
|
||||||
type-fest "^3.5.0"
|
type-fest "^3.5.0"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue