2017-11-03 02:09:34 +02:00
const fs = require ( 'fs-extra' ) ;
const { shim } = require ( 'lib/shim.js' ) ;
const { GeolocationNode } = require ( 'lib/geolocation-node.js' ) ;
const { FileApiDriverLocal } = require ( 'lib/file-api-driver-local.js' ) ;
2017-11-04 14:23:46 +02:00
const { setLocale , defaultLocale , closestSupportedLocale } = require ( 'lib/locale.js' ) ;
2018-01-21 19:01:37 +02:00
const { FsDriverNode } = require ( 'lib/fs-driver-node.js' ) ;
2018-05-25 09:51:54 +02:00
const mimeUtils = require ( 'lib/mime-utils.js' ) . mime ;
2018-06-10 02:19:24 +02:00
const Note = require ( 'lib/models/Note.js' ) ;
const Resource = require ( 'lib/models/Resource.js' ) ;
2018-05-01 20:05:14 +02:00
const urlValidator = require ( 'valid-url' ) ;
2019-07-30 09:35:42 +02:00
const { _ } = require ( 'lib/locale.js' ) ;
2017-07-24 20:01:40 +02:00
2017-07-10 20:09:58 +02:00
function shimInit ( ) {
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' ) ;
2017-12-12 01:52:42 +02:00
shim . sjclModule = require ( 'lib/vendor/sjcl.js' ) ;
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
2019-07-29 15:43:53 +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
2018-04-22 21:10:43 +02:00
const resizeImage _ = async function ( filePath , targetPath , mime ) {
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
2018-04-22 21:10:43 +02:00
const nativeImage = require ( 'electron' ) . nativeImage ;
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 ( ) ;
if ( size . width <= maxDim && size . height <= maxDim ) {
shim . fsDriver ( ) . copy ( filePath , targetPath ) ;
return ;
}
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
2018-04-22 21:10:43 +02:00
const sharp = require ( 'sharp' ) ;
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 ) ;
return ;
}
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
}
2019-07-29 15:43:53 +02:00
} ;
2017-11-11 00:18:00 +02:00
2019-02-03 20:58:44 +02:00
shim . createResourceFromPath = async function ( filePath , defaultProps = null ) {
2018-05-23 15:25:59 +02:00
const readChunk = require ( 'read-chunk' ) ;
const imageType = require ( 'image-type' ) ;
2017-11-11 00:18:00 +02:00
const { uuid } = require ( 'lib/uuid.js' ) ;
2017-12-02 01:15:49 +02:00
const { basename , fileExtension , safeFileExtension } = require ( 'lib/path-utils.js' ) ;
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 : { } ;
const resourceId = defaultProps . id ? defaultProps . id : uuid . create ( ) ;
2017-11-11 00:18:00 +02:00
let resource = Resource . new ( ) ;
2019-02-03 20:58:44 +02:00
resource . id = resourceId ;
2019-10-08 21:36:33 +02:00
resource . mime = mimeUtils . fromFilename ( filePath ) ;
2017-12-02 01:15:49 +02:00
resource . title = basename ( filePath ) ;
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
let targetPath = Resource . fullPath ( resource ) ;
if ( resource . mime == 'image/jpeg' || resource . mime == 'image/jpg' || resource . mime == 'image/png' ) {
2019-07-30 09:35:42 +02:00
await resizeImage _ ( filePath , targetPath , resource . mime ) ;
2017-11-11 00:18:00 +02:00
} else {
2019-05-12 02:15:52 +02:00
// const stat = await shim.fsDriver().stat(filePath);
// if (stat.size >= 10000000) throw new Error('Resources larger than 10 MB are not currently supported as they may crash the mobile applications. The issue is being investigated and will be fixed at a later time.');
2018-05-03 12:31:07 +02:00
2017-11-11 00:18:00 +02:00
await fs . copy ( filePath , targetPath , { overwrite : true } ) ;
}
2019-02-03 20:58:44 +02:00
if ( defaultProps ) {
resource = Object . assign ( { } , resource , defaultProps ) ;
}
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 ;
2019-07-30 09:35:42 +02:00
return Resource . save ( resource , { isNew : true } ) ;
2019-07-29 15:43:53 +02:00
} ;
2018-05-23 13:14:38 +02:00
2019-07-29 12:16:47 +02:00
shim . attachFileToNote = async function ( note , filePath , position = null , createFileURL = false ) {
const { basename } = require ( 'path' ) ;
const { escapeLinkText } = require ( 'lib/markdownUtils' ) ;
const { toFileProtocolPath } = require ( 'lib/path-utils' ) ;
let resource = [ ] ;
if ( ! createFileURL ) {
resource = await shim . createResourceFromPath ( filePath ) ;
}
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 ) {
position = note . body ? note . body . length : 0 ;
}
if ( note . body && position ) newBody . push ( note . body . substr ( 0 , position ) ) ;
2019-07-29 12:16:47 +02:00
if ( ! createFileURL ) {
newBody . push ( Resource . markdownTag ( resource ) ) ;
} else {
let filename = escapeLinkText ( basename ( filePath ) ) ; // to get same filename as standard drag and drop
2019-09-19 23:51:18 +02:00
let fileURL = ` [ ${ filename } ]( ${ toFileProtocolPath ( filePath ) } ) ` ;
2019-07-29 12:16:47 +02:00
newBody . push ( fileURL ) ;
}
2018-05-14 12:23:18 +02:00
if ( note . body ) newBody . push ( note . body . substr ( position ) ) ;
2018-02-25 19:01:16 +02:00
2017-11-11 00:18:00 +02:00
const newNote = Object . assign ( { } , note , {
2018-02-25 19:01:16 +02:00
body : newBody . join ( '\n\n' ) ,
2017-11-11 00:18:00 +02:00
} ) ;
return await Note . save ( newNote ) ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-11 00:18:00 +02:00
2018-05-25 09:51:54 +02:00
shim . imageFromDataUrl = async function ( imageDataUrl , filePath , options = null ) {
if ( options === null ) options = { } ;
if ( shim . isElectron ( ) ) {
const nativeImage = require ( 'electron' ) . nativeImage ;
let image = nativeImage . createFromDataURL ( imageDataUrl ) ;
2018-09-24 21:15:23 +02:00
if ( image . isEmpty ( ) ) throw new Error ( 'Could not convert data URL to image' ) ; // 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??
2019-07-29 15:43:53 +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
2017-10-22 14:45:56 +02:00
shim . fetch = async function ( url , options = null ) {
2018-05-01 20:05:14 +02:00
const validatedUrl = urlValidator . isUri ( url ) ;
2019-09-19 23:51:18 +02:00
if ( ! validatedUrl ) throw new Error ( ` Not a valid URL: ${ url } ` ) ;
2018-05-01 20:05:14 +02:00
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' ;
2017-07-10 20:09:58 +02:00
const http = url . protocol . toLowerCase ( ) == 'http:' ? require ( 'follow-redirects' ) . http : require ( 'follow-redirects' ) . https ;
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 ,
} ;
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 ;
2019-07-29 15:43:53 +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 )
. catch ( ( ) => { } )
. 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
2017-10-22 14:45:56 +02:00
file . on ( 'finish' , function ( ) {
file . close ( ( ) => {
resolve ( makeResponse ( response ) ) ;
} ) ;
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 ;
2019-07-29 15:43:53 +02:00
shim . openUrl = url => {
2018-03-27 01:55:44 +02:00
const { bridge } = require ( 'electron' ) . remote . require ( './bridge' ) ;
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.
return bridge ( ) . openExternal ( url ) ;
} ;
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 = ( ) => { } ;
2017-07-10 20:09:58 +02:00
}
2018-11-14 00:37:39 +02:00
module . exports = { shimInit } ;