You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/updater/windows.ts

232 lines
6.1 KiB
TypeScript

Session v1.0 changes (#802) * correct typo in readme * include log * decrypt file server response, remove debug, handle crypt before _sendToProxy, improve json parsing failure logging * support file uploads on file proxy, fix _sendToProxy calling * bump form-data to 3.0 * initial refactor of feaure flag detection statements in serverRequest() * fix send-message line-height with multiple lines * fix lint * fix position of delete account modal * Profile picture upload, fixes and copy * Various changes suggested by redesign overview * Scrolling button updated and animations to modals * Display subscriber count for open chats * Prevent illegal username and passwords * Delete channel / group merge * Solidification of minor changes w appview injections * hide description field in group panel for now * fix join publicgroups pulls * increase min height respecting ratio * allow space inside a display name but not at start or end * fix height of leftpane overlay view * add back typing indicator and read receipt setting under privacy * Auto-focus new open chat input box * Password lock screen and delete data screen * touchups * Resolving Bilb revisions * Disable link previews as default per Kee on signup * remove date, we have git * add missing semicolon * _sendToProxy pass headers/handle response refactor, lint * fix my yarn conflict/resolve * include IV in server response * Sealed sender support * Support sealed sender for friend requests * fix lint * Remove unused destinationRegistrationId; lint * Update messages.json * pull RSS through file proxy * fix unit tests: remove not used count in scrolldown view and assert svg present * Disable auto-joining default loki open groups * session-id-editable-textarea * fix the textscramble for sessionID on registration * speed up lint, add lint-full/format-full, make sure use lint-full * add skipToken to establishConnection options, smuggle out secureRpcPubKey * get latest version through snode proxy, remove clearfix from ExpiredAlertBanner * expose semver and LokiAppDotNetServerAPI because we can't get ourKey from storage early enough * update note * fix upgrade link, wrap expiredWarning in span for styling, use br to clear the float, trim trailing whitespace * designalify * designalify * designalify user agent * continue designalification * make expired banner legible * remove ugly TLS hack * disable unauthorization rejection when making https requests limited to lokiRpc * Update main.js Aspect ratio amendment * Constants rework * local commit * event listeners * address missing comma for lint * fix header sessions message section * fix profile image size conversation list with pending friend request * textarea centering * refresh files in group in group panel * Looking into keyboard navigation * Remove P2P * cache eslint on `lint` but not `ready` * Cleanup media view formatting * force locale to be EN until our files are updated and translated * Simplification of keyup * Updated all icon references * SASS fixup * fix disabled state of message input on sent friendrequest * trim pubkey when user can enter one to remove whitespaces * remove lZ in path which fixes errors on svg and does not alter rendering * fix text scramble animation on registration * reload app on ctrl-r or f5 from anywhere * add back file which should have not been deleted * fix lint and clean code * fix lint * add .loki to have a self-signed cert * Remove mixpanel * use local shortcut instead of global shortcut otherwise, ctrl+r is only caught bu the last loaded instance * open the conversation when accepting a friend request also, it does what is needed to show the new friend in the friend list * make sure token comms are done over fileProxy, other notes, logging adjustment * leftpane sections titles are Wasa bold * minor refactor * onboarding messageview * linter * fix padding buttons overlay * do not render session-id-editable border when textarea disabled * textarea sessionID SpaceMono font * various touchups * fix font of description to sfprodisplay * reduce triple dots conversation header icon size * reduce size of conversationHeader title font size * fix font for session-search-input * make conversationlistitem title font wasa * fix green and white border under title in leftpane * fix panel-text-divider font-size and family * disable completely borders for profile images * make profile image which where 48pixels big 36 noew, as no more border * Complete conditional message onboarding * cache file deletions * Link preview warning on setting toggle * Messages.json amendments * Join channel generalisation * Localise global vars * remove eslintcache * rm global launchcount * Remove source field from envelope * Session public chat icon * CLosed groups ui initial listprops * Desktop: enable useSnodeProxy feature flag * file proxy needs to be able to talk to snode - disable TLS check for fileProxy - lokiHttpsAgent => snodeHttpsAgent (since we use for two different things now) * enable useSealedSender too per Maxim * lint * lint * window.extension.expiredPromise version * better error checking * use promise version to see if we're expired * fix typo * lint * put back seemingly now required process.env.NODE_TLS_REJECT_UNAUTHORIZED * fix querystring in file-proxy * lint * fix typo * Remove more references to signal.org * make sure TLS is forced on open groups, improve serverRequest error message * Closed groups UI * function params changes * turn off snode proxy logging * include useful info on error * actually validate URL before starting up a bunch of timers * Closed groups overlay integration * move comments from connecting_to_server_dialog_view * use attempt from window object to reduce code duplication * refactor out validServer() * lint * lint caught typo * Rename BACKGROUND_FRIEND_REQUEST to SESSION_REQUEST. Don't trigger friend request logic if a message is aimed at a group. * Linting * Closed group joining completed w/o backend * Fix friend request messages being sent to users you don't have a session in closed groups. Disable typing messages and read receipts in groups. Send out session request messages if you don't have a session with a member in the group. * Remove unneeded boolean condition. * Closed group update message stylgin * constants renaming * Message deletion fix * gruntify * fix grunt error * expose isRss, don't close uncloseable Rss conversation on deleteMessages * remove copyId and block user on RSS feeds * remove options from RSS feed that don't make any sense and don't work * fix grunt error * squelch RSS duplicate messages * extension.expiredStatus(), adjustable timers, improve guards * allowing sending of messages if we're still waiting to hear back * markRandomNodeUnreachable() refactor, notes/logging * improve logging * improve logging * no need to validate empty token, support lokinet/getession file domains, mark broken snodes as bad, improve logging * try to address travis-osx lint complaints * not designed to have a period at the end of titleIsNow * put period back at the end * Catch a stray loki messenger * fix stray loki messenger * loki messenger isnt a thing * lint * Fix open group joining. * guards incase there are no members yet, fixes dialog not showing up * fixed file server holding up message sender init. fix joining closed groups. * Clean * Don't wait for file server to return tokens when establishing home connection. * Disable join public chat prompt Co-authored-by: Audric Ackermann <audric.bilb@gmail.com> Co-authored-by: Ryan Tharp <neuro@interx.net> Co-authored-by: Vince <vincent@loki.network> Co-authored-by: Maxim Shishmarev <msgmaxim@gmail.com>
5 years ago
import { dirname, join } from 'path';
import { spawn as spawnEmitter, SpawnOptions } from 'child_process';
import { readdir as readdirCallback, unlink as unlinkCallback } from 'fs';
import { app, BrowserWindow } from 'electron';
import { get as getFromConfig } from 'config';
import { gt } from 'semver';
import pify from 'pify';
import {
checkForUpdates,
deleteTempDir,
downloadUpdate,
getPrintableError,
LoggerType,
MessagesType,
showCannotUpdateDialog,
showUpdateDialog,
} from './common';
import { hexToBinary, verifySignature } from './signature';
import { markShouldQuit } from '../../app/window_state';
const readdir = pify(readdirCallback);
const unlink = pify(unlinkCallback);
let isChecking = false;
const SECOND = 1000;
const MINUTE = SECOND * 60;
const INTERVAL = MINUTE * 30;
export async function start(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
logger.info('windows/start: starting checks...');
loggerForQuitHandler = logger;
app.once('quit', quitHandler);
setInterval(async () => {
try {
await checkDownloadAndInstall(getMainWindow, messages, logger);
} catch (error) {
logger.error('windows/start: error:', getPrintableError(error));
}
}, INTERVAL);
await deletePreviousInstallers(logger);
await checkDownloadAndInstall(getMainWindow, messages, logger);
}
let fileName: string;
let version: string;
let updateFilePath: string;
let installing: boolean;
let loggerForQuitHandler: LoggerType;
async function checkDownloadAndInstall(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
if (isChecking) {
return;
}
try {
isChecking = true;
logger.info('checkDownloadAndInstall: checking for update...');
const result = await checkForUpdates(logger);
if (!result) {
return;
}
const { fileName: newFileName, version: newVersion } = result;
if (fileName !== newFileName || !version || gt(newVersion, version)) {
deleteCache(updateFilePath, logger);
fileName = newFileName;
version = newVersion;
updateFilePath = await downloadUpdate(fileName, logger);
}
const publicKey = hexToBinary(getFromConfig('updatesPublicKey'));
const verified = verifySignature(updateFilePath, version, publicKey);
if (!verified) {
// Note: We don't delete the cache here, because we don't want to continually
// re-download the broken release. We will download it only once per launch.
throw new Error(
`Downloaded update did not pass signature verification (version: '${version}'; fileName: '${fileName}')`
);
}
logger.info('checkDownloadAndInstall: showing dialog...');
const shouldUpdate = await showUpdateDialog(getMainWindow(), messages);
if (!shouldUpdate) {
return;
}
try {
await verifyAndInstall(updateFilePath, version, logger);
installing = true;
} catch (error) {
logger.info(
'checkDownloadAndInstall: showing general update failure dialog...'
);
await showCannotUpdateDialog(getMainWindow(), messages);
throw error;
}
markShouldQuit();
app.quit();
} catch (error) {
logger.error('checkDownloadAndInstall: error', getPrintableError(error));
} finally {
isChecking = false;
}
}
function quitHandler() {
if (updateFilePath && !installing) {
verifyAndInstall(updateFilePath, version, loggerForQuitHandler).catch(
error => {
loggerForQuitHandler.error(
'quitHandler: error installing:',
getPrintableError(error)
);
}
);
}
}
// Helpers
// This is fixed by out new install mechanisms...
// https://github.com/signalapp/Signal-Desktop/issues/2369
// ...but we should also clean up those old installers.
const IS_EXE = /\.exe$/i;
async function deletePreviousInstallers(logger: LoggerType) {
const userDataPath = app.getPath('userData');
const files: Array<string> = await readdir(userDataPath);
await Promise.all(
files.map(async file => {
const isExe = IS_EXE.test(file);
if (!isExe) {
return;
}
const fullPath = join(userDataPath, file);
try {
await unlink(fullPath);
} catch (error) {
logger.error(`deletePreviousInstallers: couldn't delete file ${file}`);
}
})
);
}
async function verifyAndInstall(
filePath: string,
newVersion: string,
logger: LoggerType
) {
const publicKey = hexToBinary(getFromConfig('updatesPublicKey'));
const verified = verifySignature(updateFilePath, newVersion, publicKey);
if (!verified) {
throw new Error(
`Downloaded update did not pass signature verification (version: '${newVersion}'; fileName: '${fileName}')`
);
}
await install(filePath, logger);
}
async function install(filePath: string, logger: LoggerType): Promise<void> {
logger.info('windows/install: installing package...');
const args = ['--updated'];
const options = {
detached: true,
stdio: 'ignore' as 'ignore', // TypeScript considers this a plain string without help
};
try {
await spawn(filePath, args, options);
} catch (error) {
if (error.code === 'UNKNOWN' || error.code === 'EACCES') {
logger.warn(
'windows/install: Error running installer; Trying again with elevate.exe'
);
await spawn(getElevatePath(), [filePath, ...args], options);
return;
}
throw error;
}
}
function deleteCache(filePath: string | null, logger: LoggerType) {
if (filePath) {
const tempDir = dirname(filePath);
deleteTempDir(tempDir).catch(error => {
logger.error(
'deleteCache: error deleting temporary directory',
getPrintableError(error)
);
});
}
}
function getElevatePath() {
const installPath = app.getAppPath();
return join(installPath, 'resources', 'elevate.exe');
}
async function spawn(
exe: string,
args: Array<string>,
options: SpawnOptions
): Promise<void> {
return new Promise((resolve, reject) => {
const emitter = spawnEmitter(exe, args, options);
emitter.on('error', reject);
emitter.unref();
// tslint:disable-next-line no-string-based-set-timeout
setTimeout(resolve, 200);
});
}