2021-01-27 19:42:58 +02:00
'use strict' ;
2017-11-03 02:09:34 +02:00
const fs = require ( 'fs-extra' ) ;
2020-11-05 18:58:23 +02:00
const shim = require ( './shim' ) . default ;
2022-04-26 14:22:43 +02:00
const GeolocationNode = require ( './geolocation-node' ) . default ;
2022-07-10 16:26:24 +02:00
const { FileApiDriverLocal } = require ( './file-api-driver-local' ) ;
2020-11-05 18:58:23 +02:00
const { setLocale , defaultLocale , closestSupportedLocale } = require ( './locale' ) ;
const FsDriverNode = require ( './fs-driver-node' ) . default ;
const mimeUtils = require ( './mime-utils.js' ) . mime ;
2021-01-22 19:41:11 +02:00
const Note = require ( './models/Note' ) . default ;
const Resource = require ( './models/Resource' ) . default ;
2020-11-05 18:58:23 +02:00
const { _ } = require ( './locale' ) ;
2020-02-27 02:14:40 +02:00
const http = require ( 'http' ) ;
const https = require ( 'https' ) ;
2022-07-10 15:54:31 +02:00
const { HttpProxyAgent , HttpsProxyAgent } = require ( 'hpagent' ) ;
2020-03-10 01:24:57 +02:00
const toRelative = require ( 'relative' ) ;
2020-10-09 19:35:46 +02:00
const timers = require ( 'timers' ) ;
2020-10-28 17:47:36 +02:00
const zlib = require ( 'zlib' ) ;
2021-11-17 14:54:34 +02:00
const dgram = require ( 'dgram' ) ;
2022-02-07 19:23:20 +02:00
const { basename , fileExtension , safeFileExtension } = require ( './path-utils' ) ;
2020-10-28 17:47:36 +02:00
2022-07-10 15:54:31 +02:00
const proxySettings = { } ;
2020-10-28 17:47:36 +02:00
function fileExists ( filePath ) {
try {
return fs . statSync ( filePath ) . isFile ( ) ;
} catch ( err ) {
return false ;
}
}
2022-07-10 15:54:31 +02:00
function isUrlHttps ( url ) {
return url . startsWith ( 'https' ) ;
}
function resolveProxyUrl ( proxyUrl ) {
return (
proxyUrl ||
process . env [ 'http_proxy' ] ||
process . env [ 'https_proxy' ] ||
process . env [ 'HTTP_PROXY' ] ||
process . env [ 'HTTPS_PROXY' ]
) ;
}
2021-01-27 19:42:58 +02:00
// https://github.com/sindresorhus/callsites/blob/main/index.js
function callsites ( ) {
const _prepareStackTrace = Error . prepareStackTrace ;
Error . prepareStackTrace = ( _any , stack ) => stack ;
const stack = new Error ( ) . stack . slice ( 1 ) ;
Error . prepareStackTrace = _prepareStackTrace ;
return stack ;
}
2020-10-28 17:47:36 +02:00
const gunzipFile = function ( source , destination ) {
if ( ! fileExists ( source ) ) {
throw new Error ( ` No such file: ${ source } ` ) ;
}
return new Promise ( ( resolve , reject ) => {
// prepare streams
const src = fs . createReadStream ( source ) ;
const dest = fs . createWriteStream ( destination ) ;
// extract the archive
src . pipe ( zlib . createGunzip ( ) ) . pipe ( dest ) ;
// callback on extract completion
dest . on ( 'close' , function ( ) {
resolve ( ) ;
} ) ;
src . on ( 'error' , ( ) => {
reject ( ) ;
} ) ;
dest . on ( 'error' , ( ) => {
reject ( ) ;
} ) ;
} ) ;
} ;
2017-07-24 20:01:40 +02:00
2022-07-10 15:54:31 +02:00
function setupProxySettings ( options ) {
proxySettings . maxConcurrentConnections = options . maxConcurrentConnections ;
proxySettings . proxyTimeout = options . proxyTimeout ;
proxySettings . proxyEnabled = options . proxyEnabled ;
proxySettings . proxyUrl = options . proxyUrl ;
}
2021-10-01 20:35:27 +02:00
function shimInit ( options = null ) {
options = {
sharp : null ,
keytar : null ,
React : null ,
appVersion : null ,
electronBridge : null ,
nodeSqlite : null ,
... options ,
} ;
const sharp = options . sharp ;
const keytar = ( shim . isWindows ( ) || shim . isMac ( ) ) && ! shim . isPortable ( ) ? options . keytar : null ;
const appVersion = options . appVersion ;
2022-07-10 15:54:31 +02:00
2021-10-01 20:35:27 +02:00
shim . setNodeSqlite ( options . nodeSqlite ) ;
2020-11-05 18:58:23 +02:00
2019-07-29 15:43:53 +02:00
shim . fsDriver = ( ) => {
throw new Error ( 'Not implemented' ) ;
} ;
2017-07-24 20:01:40 +02:00
shim . FileApiDriverLocal = FileApiDriverLocal ;
2017-07-10 20:09:58 +02:00
shim . Geolocation = GeolocationNode ;
2017-07-24 20:01:40 +02:00
shim . FormData = require ( 'form-data' ) ;
2020-11-05 18:58:23 +02:00
shim . sjclModule = require ( './vendor/sjcl.js' ) ;
2021-10-01 20:35:27 +02:00
shim . electronBridge _ = options . electronBridge ;
2017-10-15 13:13:09 +02:00
2018-01-21 19:01:37 +02:00
shim . fsDriver = ( ) => {
if ( ! shim . fsDriver _ ) shim . fsDriver _ = new FsDriverNode ( ) ;
return shim . fsDriver _ ;
2019-07-29 15:43:53 +02:00
} ;
2018-01-21 19:01:37 +02:00
2021-11-17 14:54:34 +02:00
shim . dgram = ( ) => {
return dgram ;
} ;
2021-10-01 20:35:27 +02:00
if ( options . React ) {
2020-11-19 18:38:44 +02:00
shim . react = ( ) => {
2021-10-01 20:35:27 +02:00
return options . React ;
2020-11-19 18:38:44 +02:00
} ;
}
2021-10-01 20:35:27 +02:00
shim . electronBridge = ( ) => {
return shim . electronBridge _ ;
} ;
2020-05-21 10:14:33 +02:00
shim . randomBytes = async count => {
2017-12-12 19:51:07 +02:00
const buffer = require ( 'crypto' ) . randomBytes ( count ) ;
return Array . from ( buffer ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-12-12 19:51:07 +02:00
2019-07-29 15:43:53 +02:00
shim . detectAndSetLocale = function ( Setting ) {
2017-11-04 14:23:46 +02:00
let locale = process . env . LANG ;
if ( ! locale ) locale = defaultLocale ( ) ;
locale = locale . split ( '.' ) ;
locale = locale [ 0 ] ;
locale = closestSupportedLocale ( locale ) ;
Setting . setValue ( 'locale' , locale ) ;
setLocale ( locale ) ;
return locale ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-04 14:23:46 +02:00
2018-05-10 11:45:44 +02:00
shim . writeImageToFile = async function ( nativeImage , mime , targetPath ) {
2019-07-29 15:43:53 +02:00
if ( shim . isElectron ( ) ) {
// For Electron
2018-05-25 09:51:54 +02:00
let buffer = null ;
2018-05-10 11:45:44 +02:00
2018-05-25 09:51:54 +02:00
mime = mime . toLowerCase ( ) ;
2018-05-10 11:45:44 +02:00
2018-05-25 09:51:54 +02:00
if ( mime === 'image/png' ) {
buffer = nativeImage . toPNG ( ) ;
} else if ( mime === 'image/jpg' || mime === 'image/jpeg' ) {
buffer = nativeImage . toJPEG ( 90 ) ;
}
2018-05-10 11:45:44 +02:00
2019-09-19 23:51:18 +02:00
if ( ! buffer ) throw new Error ( ` Cannot resize image because mime type " ${ mime } " is not supported: ${ targetPath } ` ) ;
2018-05-10 11:45:44 +02:00
2018-05-25 09:51:54 +02:00
await shim . fsDriver ( ) . writeFile ( targetPath , buffer , 'buffer' ) ;
} else {
throw new Error ( 'Node support not implemented' ) ;
}
2019-07-29 15:43:53 +02:00
} ;
2018-05-10 11:45:44 +02:00
2020-03-31 23:40:38 +02:00
shim . showMessageBox = ( message , options = null ) => {
if ( shim . isElectron ( ) ) {
2021-10-01 20:35:27 +02:00
return shim . electronBridge ( ) . showMessageBox ( message , options ) ;
2020-03-31 23:40:38 +02:00
} else {
throw new Error ( 'Not implemented' ) ;
}
} ;
const handleResizeImage _ = async function ( filePath , targetPath , mime , resizeLargeImages ) {
2019-05-12 12:38:33 +02:00
const maxDim = Resource . IMAGE _MAX _DIMENSION ;
2019-07-29 15:43:53 +02:00
if ( shim . isElectron ( ) ) {
// For Electron
2022-02-11 21:53:42 +02:00
const nativeImage = require ( 'electron' ) . nativeImage ;
2018-04-22 21:10:43 +02:00
let image = nativeImage . createFromPath ( filePath ) ;
2019-09-19 23:51:18 +02:00
if ( image . isEmpty ( ) ) throw new Error ( ` Image is invalid or does not exist: ${ filePath } ` ) ;
2018-04-22 21:10:43 +02:00
const size = image . getSize ( ) ;
2020-03-31 23:40:38 +02:00
let mustResize = size . width > maxDim || size . height > maxDim ;
if ( mustResize && resizeLargeImages === 'ask' ) {
const answer = shim . showMessageBox ( _ ( 'You are about to attach a large image (%dx%d pixels). Would you like to resize it down to %d pixels before attaching it?' , size . width , size . height , maxDim ) , {
buttons : [ _ ( 'Yes' ) , _ ( 'No' ) , _ ( 'Cancel' ) ] ,
} ) ;
if ( answer === 2 ) return false ;
mustResize = answer === 0 ;
}
if ( ! mustResize ) {
2021-08-05 16:08:57 +02:00
await shim . fsDriver ( ) . copy ( filePath , targetPath ) ;
2020-03-31 23:40:38 +02:00
return true ;
2018-04-22 21:10:43 +02:00
}
const options = { } ;
if ( size . width > size . height ) {
options . width = maxDim ;
} else {
options . height = maxDim ;
}
image = image . resize ( options ) ;
2018-05-10 11:45:44 +02:00
await shim . writeImageToFile ( image , mime , targetPath ) ;
2019-07-29 15:43:53 +02:00
} else {
// For the CLI tool
2019-05-12 12:38:33 +02:00
const image = sharp ( filePath ) ;
const md = await image . metadata ( ) ;
if ( md . width <= maxDim && md . height <= maxDim ) {
shim . fsDriver ( ) . copy ( filePath , targetPath ) ;
2020-03-31 23:40:38 +02:00
return true ;
2019-05-12 12:38:33 +02:00
}
2018-04-22 21:10:43 +02:00
return new Promise ( ( resolve , reject ) => {
2019-07-29 15:43:53 +02:00
image
. resize ( Resource . IMAGE _MAX _DIMENSION , Resource . IMAGE _MAX _DIMENSION , {
fit : 'inside' ,
withoutEnlargement : true ,
} )
. toFile ( targetPath , ( err , info ) => {
if ( err ) {
reject ( err ) ;
} else {
resolve ( info ) ;
}
} ) ;
2017-11-11 00:18:00 +02:00
} ) ;
2018-04-22 21:10:43 +02:00
}
2020-03-31 23:40:38 +02:00
return true ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-11 00:18:00 +02:00
2022-03-28 17:35:41 +02:00
// This is a bit of an ugly method that's used to both create a new resource
// from a file, and update one. To update a resource, pass the
// destinationResourceId option. This method is indirectly tested in
// Api.test.ts.
2020-03-31 23:40:38 +02:00
shim . createResourceFromPath = async function ( filePath , defaultProps = null , options = null ) {
options = Object . assign ( {
2020-05-30 14:25:05 +02:00
resizeLargeImages : 'always' , // 'always', 'ask' or 'never'
2020-07-24 02:45:15 +02:00
userSideValidation : false ,
2022-03-28 17:35:41 +02:00
destinationResourceId : '' ,
2020-03-31 23:40:38 +02:00
} , options ) ;
2018-05-23 15:25:59 +02:00
const readChunk = require ( 'read-chunk' ) ;
const imageType = require ( 'image-type' ) ;
2022-04-11 18:01:01 +02:00
const isUpdate = ! ! options . destinationResourceId ;
2020-11-05 18:58:23 +02:00
const uuid = require ( './uuid' ) . default ;
2017-11-11 00:18:00 +02:00
if ( ! ( await fs . pathExists ( filePath ) ) ) throw new Error ( _ ( 'Cannot access %s' , filePath ) ) ;
2019-02-03 20:58:44 +02:00
defaultProps = defaultProps ? defaultProps : { } ;
2022-03-28 17:35:41 +02:00
let resourceId = defaultProps . id ? defaultProps . id : uuid . create ( ) ;
2022-04-11 18:01:01 +02:00
if ( isUpdate ) resourceId = options . destinationResourceId ;
2019-02-03 20:58:44 +02:00
2022-04-11 18:01:01 +02:00
let resource = isUpdate ? { } : Resource . new ( ) ;
2019-02-03 20:58:44 +02:00
resource . id = resourceId ;
2022-04-11 18:01:01 +02:00
// When this is an update we auto-update the mime type, in case the
// content type has changed, but we keep the title. It is still possible
// to modify the title on update using defaultProps.
2019-10-08 21:36:33 +02:00
resource . mime = mimeUtils . fromFilename ( filePath ) ;
2022-04-11 18:01:01 +02:00
if ( ! isUpdate ) resource . title = basename ( filePath ) ;
2017-12-02 01:15:49 +02:00
2018-05-23 15:25:59 +02:00
let fileExt = safeFileExtension ( fileExtension ( filePath ) ) ;
if ( ! resource . mime ) {
const buffer = await readChunk ( filePath , 0 , 64 ) ;
const detectedType = imageType ( buffer ) ;
if ( detectedType ) {
fileExt = detectedType . ext ;
resource . mime = detectedType . mime ;
} else {
resource . mime = 'application/octet-stream' ;
}
}
resource . file _extension = fileExt ;
2017-11-11 00:18:00 +02:00
2020-03-14 01:46:14 +02:00
const targetPath = Resource . fullPath ( resource ) ;
2017-11-11 00:18:00 +02:00
2020-05-30 14:25:05 +02:00
if ( options . resizeLargeImages !== 'never' && [ 'image/jpeg' , 'image/jpg' , 'image/png' ] . includes ( resource . mime ) ) {
2020-03-31 23:40:38 +02:00
const ok = await handleResizeImage _ ( filePath , targetPath , resource . mime , options . resizeLargeImages ) ;
if ( ! ok ) return null ;
2017-11-11 00:18:00 +02:00
} else {
await fs . copy ( filePath , targetPath , { overwrite : true } ) ;
}
2020-07-24 02:45:15 +02:00
// While a whole object can be passed as defaultProps, we only just
// support the title and ID (used above). Any other prop should be
// derived from the provided file.
if ( 'title' in defaultProps ) resource . title = defaultProps . title ;
2019-02-03 20:58:44 +02:00
2019-05-12 02:15:52 +02:00
const itDoes = await shim . fsDriver ( ) . waitTillExists ( targetPath ) ;
2019-09-19 23:51:18 +02:00
if ( ! itDoes ) throw new Error ( ` Resource file was not created: ${ targetPath } ` ) ;
2019-05-12 02:15:52 +02:00
2019-05-11 18:55:40 +02:00
const fileStat = await shim . fsDriver ( ) . stat ( targetPath ) ;
resource . size = fileStat . size ;
2021-01-23 17:51:19 +02:00
const saveOptions = { isNew : true } ;
2020-07-24 02:45:15 +02:00
if ( options . userSideValidation ) saveOptions . userSideValidation = true ;
2022-03-28 17:35:41 +02:00
2022-04-11 18:01:01 +02:00
if ( isUpdate ) {
2022-03-28 17:35:41 +02:00
saveOptions . isNew = false ;
const tempPath = ` ${ targetPath } .tmp ` ;
await shim . fsDriver ( ) . move ( targetPath , tempPath ) ;
resource = await Resource . save ( resource , saveOptions ) ;
await Resource . updateResourceBlobContent ( resource . id , tempPath ) ;
await shim . fsDriver ( ) . remove ( tempPath ) ;
return resource ;
} else {
return Resource . save ( resource , saveOptions ) ;
}
2019-07-29 15:43:53 +02:00
} ;
2018-05-23 13:14:38 +02:00
2020-05-02 17:41:07 +02:00
shim . attachFileToNoteBody = async function ( noteBody , filePath , position = null , options = null ) {
2020-03-31 23:40:38 +02:00
options = Object . assign ( { } , {
createFileURL : false ,
} , options ) ;
2019-07-29 12:16:47 +02:00
const { basename } = require ( 'path' ) ;
2020-11-05 18:58:23 +02:00
const { escapeTitleText } = require ( './markdownUtils' ) . default ;
const { toFileProtocolPath } = require ( './path-utils' ) ;
2019-07-29 12:16:47 +02:00
2020-03-31 23:40:38 +02:00
let resource = null ;
if ( ! options . createFileURL ) {
resource = await shim . createResourceFromPath ( filePath , null , options ) ;
2020-04-03 00:01:14 +02:00
if ( ! resource ) return null ;
2019-07-29 12:16:47 +02:00
}
2018-05-23 13:14:38 +02:00
2018-02-25 19:01:16 +02:00
const newBody = [ ] ;
2018-05-10 11:45:44 +02:00
if ( position === null ) {
2020-05-02 17:41:07 +02:00
position = noteBody ? noteBody . length : 0 ;
2018-05-10 11:45:44 +02:00
}
2020-05-02 17:41:07 +02:00
if ( noteBody && position ) newBody . push ( noteBody . substr ( 0 , position ) ) ;
2019-07-29 12:16:47 +02:00
2020-03-31 23:40:38 +02:00
if ( ! options . createFileURL ) {
2019-07-29 12:16:47 +02:00
newBody . push ( Resource . markdownTag ( resource ) ) ;
} else {
2020-06-07 13:55:40 +02:00
const filename = escapeTitleText ( basename ( filePath ) ) ; // to get same filename as standard drag and drop
2020-03-14 01:46:14 +02:00
const fileURL = ` [ ${ filename } ]( ${ toFileProtocolPath ( filePath ) } ) ` ;
2019-07-29 12:16:47 +02:00
newBody . push ( fileURL ) ;
}
2020-05-02 17:41:07 +02:00
if ( noteBody ) newBody . push ( noteBody . substr ( position ) ) ;
return newBody . join ( '\n\n' ) ;
} ;
shim . attachFileToNote = async function ( note , filePath , position = null , options = null ) {
const newBody = await shim . attachFileToNoteBody ( note . body , filePath , position , options ) ;
if ( ! newBody ) return null ;
2018-02-25 19:01:16 +02:00
2017-11-11 00:18:00 +02:00
const newNote = Object . assign ( { } , note , {
2020-05-02 17:41:07 +02:00
body : newBody ,
2017-11-11 00:18:00 +02:00
} ) ;
2020-11-10 17:59:30 +02:00
return Note . save ( newNote ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-11 00:18:00 +02:00
2022-02-06 18:42:00 +02:00
shim . imageToDataUrl = async ( filePath , maxSize ) => {
if ( shim . isElectron ( ) ) {
2022-02-11 21:53:42 +02:00
const nativeImage = require ( 'electron' ) . nativeImage ;
2022-02-11 21:08:20 +02:00
let image = nativeImage . createFromPath ( filePath ) ;
2022-02-06 18:42:00 +02:00
if ( ! image ) throw new Error ( ` Could not load image: ${ filePath } ` ) ;
2022-02-07 19:23:20 +02:00
const ext = fileExtension ( filePath ) . toLowerCase ( ) ;
if ( ! [ 'jpg' , 'jpeg' , 'png' ] . includes ( ext ) ) throw new Error ( ` Unsupported file format: ${ ext } ` ) ;
2022-02-06 18:42:00 +02:00
if ( maxSize ) {
const size = image . getSize ( ) ;
2022-02-11 21:08:20 +02:00
if ( size . width > maxSize || size . height > maxSize ) {
console . warn ( ` Image is over ${ maxSize } px - resizing it: ${ filePath } ` ) ;
const options = { } ;
if ( size . width > size . height ) {
options . width = maxSize ;
} else {
options . height = maxSize ;
}
image = image . resize ( options ) ;
}
2022-02-06 18:42:00 +02:00
}
return image . toDataURL ( ) ;
} else {
throw new Error ( 'Unsupported method' ) ;
}
} ,
2018-05-25 09:51:54 +02:00
shim . imageFromDataUrl = async function ( imageDataUrl , filePath , options = null ) {
if ( options === null ) options = { } ;
if ( shim . isElectron ( ) ) {
2022-02-11 21:53:42 +02:00
const nativeImage = require ( 'electron' ) . nativeImage ;
2018-05-25 09:51:54 +02:00
let image = nativeImage . createFromDataURL ( imageDataUrl ) ;
2020-02-14 01:59:23 +02:00
if ( image . isEmpty ( ) ) throw new Error ( 'Could not convert data URL to image - perhaps the format is not supported (eg. image/gif)' ) ; // Would throw for example if the image format is no supported (eg. image/gif)
2018-09-23 19:03:11 +02:00
if ( options . cropRect ) {
// Crop rectangle values need to be rounded or the crop() call will fail
const c = options . cropRect ;
if ( 'x' in c ) c . x = Math . round ( c . x ) ;
if ( 'y' in c ) c . y = Math . round ( c . y ) ;
if ( 'width' in c ) c . width = Math . round ( c . width ) ;
if ( 'height' in c ) c . height = Math . round ( c . height ) ;
image = image . crop ( c ) ;
}
2018-05-25 09:51:54 +02:00
const mime = mimeUtils . fromDataUrl ( imageDataUrl ) ;
await shim . writeImageToFile ( image , mime , filePath ) ;
} else {
2018-09-27 10:14:05 +02:00
if ( options . cropRect ) throw new Error ( 'Crop rect not supported in Node' ) ;
const imageDataURI = require ( 'image-data-uri' ) ;
const result = imageDataURI . decode ( imageDataUrl ) ;
2019-07-29 15:43:53 +02:00
await shim . fsDriver ( ) . writeFile ( filePath , result . dataBuffer , 'buffer' ) ;
2018-05-25 09:51:54 +02:00
}
2019-07-29 15:43:53 +02:00
} ;
2018-05-25 09:51:54 +02:00
2017-10-15 13:13:09 +02:00
const nodeFetch = require ( 'node-fetch' ) ;
2018-09-27 20:35:10 +02:00
// Not used??
2020-05-21 10:14:33 +02:00
shim . readLocalFileBase64 = path => {
2017-11-05 18:51:03 +02:00
const data = fs . readFileSync ( path ) ;
return new Buffer ( data ) . toString ( 'base64' ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-05 18:51:03 +02:00
2022-07-10 15:54:31 +02:00
shim . fetch = async function ( url , options = { } ) {
2022-10-15 23:51:57 +02:00
try { // Check if the url is valid
new URL ( url ) ;
} catch ( error ) { // If the url is not valid, a TypeError will be thrown
throw new Error ( ` Not a valid URL: ${ url } ` ) ;
}
2022-07-10 15:54:31 +02:00
const resolvedProxyUrl = resolveProxyUrl ( proxySettings . proxyUrl ) ;
options . agent = ( resolvedProxyUrl && proxySettings . proxyEnabled ) ? shim . proxyAgent ( url , resolvedProxyUrl ) : null ;
2017-11-13 00:52:54 +02:00
return shim . fetchWithRetry ( ( ) => {
2019-07-29 15:43:53 +02:00
return nodeFetch ( url , options ) ;
2017-11-13 00:52:54 +02:00
} , options ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-07-10 20:09:58 +02:00
shim . fetchBlob = async function ( url , options ) {
if ( ! options || ! options . path ) throw new Error ( 'fetchBlob: target file path is missing' ) ;
if ( ! options . method ) options . method = 'GET' ;
2019-10-09 21:35:13 +02:00
// if (!('maxRetry' in options)) options.maxRetry = 5;
2017-07-10 20:09:58 +02:00
const urlParse = require ( 'url' ) . parse ;
url = urlParse ( url . trim ( ) ) ;
2018-03-24 21:35:10 +02:00
const method = options . method ? options . method : 'GET' ;
2022-07-23 11:33:12 +02:00
const http = url . protocol . toLowerCase ( ) === 'http:' ? require ( 'follow-redirects' ) . http : require ( 'follow-redirects' ) . https ;
2017-07-10 20:09:58 +02:00
const headers = options . headers ? options . headers : { } ;
const filePath = options . path ;
function makeResponse ( response ) {
return {
ok : response . statusCode < 400 ,
path : filePath ,
2019-07-29 15:43:53 +02:00
text : ( ) => {
return response . statusMessage ;
} ,
json : ( ) => {
2019-09-19 23:51:18 +02:00
return { message : ` ${ response . statusCode } : ${ response . statusMessage } ` } ;
2019-07-29 15:43:53 +02:00
} ,
2017-07-10 20:09:58 +02:00
status : response . statusCode ,
headers : response . headers ,
} ;
}
const requestOptions = {
protocol : url . protocol ,
2018-05-20 13:20:15 +02:00
host : url . hostname ,
2017-07-10 20:09:58 +02:00
port : url . port ,
method : method ,
2019-09-19 23:51:18 +02:00
path : url . pathname + ( url . query ? ` ? ${ url . query } ` : '' ) ,
2017-07-10 20:09:58 +02:00
headers : headers ,
} ;
2022-07-10 15:54:31 +02:00
const resolvedProxyUrl = resolveProxyUrl ( proxySettings . proxyUrl ) ;
2022-09-05 11:42:22 +02:00
requestOptions . agent = ( resolvedProxyUrl && proxySettings . proxyEnabled ) ? shim . proxyAgent ( url . href , resolvedProxyUrl ) : null ;
2022-07-10 15:54:31 +02:00
2017-10-22 14:45:56 +02:00
const doFetchOperation = async ( ) => {
return new Promise ( ( resolve , reject ) => {
2018-05-20 13:20:15 +02:00
let file = null ;
2020-05-21 10:14:33 +02:00
const cleanUpOnError = error => {
2018-05-20 13:20:15 +02:00
// We ignore any unlink error as we only want to report on the main error
2019-07-29 15:43:53 +02:00
fs . unlink ( filePath )
2022-09-30 18:23:14 +02:00
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
2019-07-29 15:43:53 +02:00
. catch ( ( ) => { } )
2022-09-30 18:23:14 +02:00
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
2019-07-29 15:43:53 +02:00
. then ( ( ) => {
if ( file ) {
file . close ( ( ) => {
file = null ;
reject ( error ) ;
} ) ;
} else {
2018-05-20 13:20:15 +02:00
reject ( error ) ;
2019-07-29 15:43:53 +02:00
}
} ) ;
} ;
2018-05-20 13:20:15 +02:00
2017-10-22 14:45:56 +02:00
try {
// Note: relative paths aren't supported
2018-05-20 13:20:15 +02:00
file = fs . createWriteStream ( filePath ) ;
file . on ( 'error' , function ( error ) {
cleanUpOnError ( error ) ;
} ) ;
2017-07-10 20:09:58 +02:00
2018-03-24 21:35:10 +02:00
const request = http . request ( requestOptions , function ( response ) {
2017-10-22 14:45:56 +02:00
response . pipe ( file ) ;
2017-07-10 20:09:58 +02:00
2020-10-28 17:47:36 +02:00
const isGzipped = response . headers [ 'content-encoding' ] === 'gzip' ;
2017-10-22 14:45:56 +02:00
file . on ( 'finish' , function ( ) {
2020-10-28 17:47:36 +02:00
file . close ( async ( ) => {
if ( isGzipped ) {
const gzipFilePath = ` ${ filePath } .gzip ` ;
await shim . fsDriver ( ) . move ( filePath , gzipFilePath ) ;
try {
await gunzipFile ( gzipFilePath , filePath ) ;
resolve ( makeResponse ( response ) ) ;
} catch ( error ) {
cleanUpOnError ( error ) ;
}
shim . fsDriver ( ) . remove ( gzipFilePath ) ;
} else {
resolve ( makeResponse ( response ) ) ;
}
2017-10-22 14:45:56 +02:00
} ) ;
2017-07-10 20:09:58 +02:00
} ) ;
2019-07-29 15:43:53 +02:00
} ) ;
2017-07-10 20:09:58 +02:00
2017-10-22 14:45:56 +02:00
request . on ( 'error' , function ( error ) {
2018-05-20 13:20:15 +02:00
cleanUpOnError ( error ) ;
2017-10-22 14:45:56 +02:00
} ) ;
2018-03-24 21:35:10 +02:00
request . end ( ) ;
2018-05-20 13:20:15 +02:00
} catch ( error ) {
cleanUpOnError ( error ) ;
2017-10-22 14:45:56 +02:00
}
} ) ;
} ;
2017-11-13 00:52:54 +02:00
return shim . fetchWithRetry ( doFetchOperation , options ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-12-18 22:46:22 +02:00
shim . uploadBlob = async function ( url , options ) {
2019-07-29 15:43:53 +02:00
if ( ! options || ! options . path ) throw new Error ( 'uploadBlob: source file path is missing' ) ;
2017-12-18 22:46:22 +02:00
const content = await fs . readFile ( options . path ) ;
options = Object . assign ( { } , options , {
body : content ,
} ) ;
return shim . fetch ( url , options ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-12-18 22:46:22 +02:00
2018-02-15 20:33:08 +02:00
shim . stringByteLength = function ( string ) {
return Buffer . byteLength ( string , 'utf-8' ) ;
2019-07-29 15:43:53 +02:00
} ;
2018-02-15 20:33:08 +02:00
2018-03-24 21:35:10 +02:00
shim . Buffer = Buffer ;
2020-05-21 10:14:33 +02:00
shim . openUrl = url => {
2019-12-13 02:40:58 +02:00
// Returns true if it opens the file successfully; returns false if it could
// not find the file.
2021-10-01 20:35:27 +02:00
return shim . electronBridge ( ) . openExternal ( url ) ;
2019-12-13 02:40:58 +02:00
} ;
2020-02-27 02:14:40 +02:00
shim . httpAgent _ = null ;
2020-05-21 10:14:33 +02:00
shim . httpAgent = url => {
2021-03-26 11:09:19 +02:00
if ( ! shim . httpAgent _ ) {
2020-03-14 01:46:14 +02:00
const AgentSettings = {
2020-02-27 02:14:40 +02:00
keepAlive : true ,
maxSockets : 1 ,
keepAliveMsecs : 5000 ,
} ;
2021-06-07 11:19:59 +02:00
shim . httpAgent _ = {
http : new http . Agent ( AgentSettings ) ,
https : new https . Agent ( AgentSettings ) ,
} ;
2020-02-27 02:14:40 +02:00
}
2021-06-07 11:19:59 +02:00
return url . startsWith ( 'https' ) ? shim . httpAgent _ . https : shim . httpAgent _ . http ;
2020-02-27 02:14:40 +02:00
} ;
2022-07-10 15:54:31 +02:00
shim . proxyAgent = ( serverUrl , proxyUrl ) => {
const proxyAgentConfig = {
keepAlive : true ,
maxSockets : proxySettings . maxConcurrentConnections ,
keepAliveMsecs : 5000 ,
proxy : proxyUrl ,
timeout : proxySettings . proxyTimeout * 1000 ,
} ;
// Based on https://github.com/delvedor/hpagent#usage
if ( ! isUrlHttps ( proxyUrl ) && ! isUrlHttps ( serverUrl ) ) {
return new HttpProxyAgent ( proxyAgentConfig ) ;
} else if ( isUrlHttps ( proxyUrl ) && ! isUrlHttps ( serverUrl ) ) {
return new HttpProxyAgent ( proxyAgentConfig ) ;
} else if ( ! isUrlHttps ( proxyUrl ) && isUrlHttps ( serverUrl ) ) {
return new HttpsProxyAgent ( proxyAgentConfig ) ;
} else {
return new HttpsProxyAgent ( proxyAgentConfig ) ;
}
} ;
2019-12-13 02:40:58 +02:00
shim . openOrCreateFile = ( filepath , defaultContents ) => {
// If the file doesn't exist, create it
if ( ! fs . existsSync ( filepath ) ) {
fs . writeFile ( filepath , defaultContents , 'utf-8' , ( error ) => {
if ( error ) {
console . error ( ` error: ${ error } ` ) ;
}
} ) ;
}
// Open the file
return shim . openUrl ( ` file:// ${ filepath } ` ) ;
2019-07-29 15:43:53 +02:00
} ;
2018-06-25 19:14:57 +02:00
2019-07-29 15:43:53 +02:00
shim . waitForFrame = ( ) => { } ;
2020-01-24 22:56:44 +02:00
shim . appVersion = ( ) => {
2021-04-07 19:12:37 +02:00
if ( appVersion ) return appVersion ( ) ;
// Should not happen but don't throw an error because version number is
// used in error messages.
return 'unknown-version!' ;
2020-01-24 22:56:44 +02:00
} ;
2020-03-10 01:24:57 +02:00
shim . pathRelativeToCwd = ( path ) => {
return toRelative ( process . cwd ( ) , path ) ;
} ;
2020-06-03 18:07:50 +02:00
2020-10-09 19:35:46 +02:00
shim . setTimeout = ( fn , interval ) => {
return timers . setTimeout ( fn , interval ) ;
} ;
shim . setInterval = ( fn , interval ) => {
return timers . setInterval ( fn , interval ) ;
} ;
shim . clearTimeout = ( id ) => {
return timers . clearTimeout ( id ) ;
} ;
shim . clearInterval = ( id ) => {
return timers . clearInterval ( id ) ;
} ;
2020-11-05 18:58:23 +02:00
shim . keytar = ( ) => {
return keytar ;
} ;
2020-12-20 09:52:28 +02:00
shim . requireDynamic = ( path ) => {
2021-01-27 19:42:58 +02:00
if ( path . indexOf ( '.' ) === 0 ) {
const sites = callsites ( ) ;
if ( sites . length <= 1 ) throw new Error ( ` Cannot require file (1) ${ path } ` ) ;
const filename = sites [ 1 ] . getFileName ( ) ;
if ( ! filename ) throw new Error ( ` Cannot require file (2) ${ path } ` ) ;
const fileDirName = require ( 'path' ) . dirname ( filename ) ;
return require ( ` ${ fileDirName } / ${ path } ` ) ;
} else {
return require ( path ) ;
}
2020-12-20 09:52:28 +02:00
} ;
2017-07-10 20:09:58 +02:00
}
2022-07-10 15:54:31 +02:00
module . exports = { shimInit , setupProxySettings } ;