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' ) ;
const { time } = require ( 'lib/time-utils.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' ) ;
2017-07-24 20:01:40 +02:00
2017-07-10 20:09:58 +02:00
function shimInit ( ) {
2018-01-21 19:01:37 +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 _ ;
}
2017-12-12 19:51:07 +02:00
shim . randomBytes = async ( count ) => {
const buffer = require ( 'crypto' ) . randomBytes ( count ) ;
return Array . from ( buffer ) ;
}
2017-11-04 14:23:46 +02:00
shim . detectAndSetLocale = function ( Setting ) {
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 ;
}
2018-05-10 11:45:44 +02:00
shim . writeImageToFile = async function ( nativeImage , mime , targetPath ) {
2018-05-25 09:51:54 +02:00
if ( shim . isElectron ( ) ) { // For Electron
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
2018-05-25 09:51:54 +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' ) ;
}
2018-05-10 11:45:44 +02:00
}
2018-04-22 21:10:43 +02:00
const resizeImage _ = async function ( filePath , targetPath , mime ) {
if ( shim . isElectron ( ) ) { // For Electron
const nativeImage = require ( 'electron' ) . nativeImage ;
let image = nativeImage . createFromPath ( filePath ) ;
if ( image . isEmpty ( ) ) throw new Error ( 'Image is invalid or does not exist: ' + filePath ) ;
const maxDim = Resource . IMAGE _MAX _DIMENSION ;
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 ) ;
2018-04-22 21:10:43 +02:00
} else { // For the CLI tool
const sharp = require ( 'sharp' ) ;
return new Promise ( ( resolve , reject ) => {
sharp ( filePath )
. resize ( Resource . IMAGE _MAX _DIMENSION , Resource . IMAGE _MAX _DIMENSION )
. max ( )
. withoutEnlargement ( )
. 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
}
2017-11-11 00:18:00 +02:00
}
2018-05-23 13:14:38 +02:00
shim . createResourceFromPath = async function ( filePath ) {
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
const mime = require ( 'mime/lite' ) ;
if ( ! ( await fs . pathExists ( filePath ) ) ) throw new Error ( _ ( 'Cannot access %s' , filePath ) ) ;
let resource = Resource . new ( ) ;
resource . id = uuid . create ( ) ;
resource . mime = mime . getType ( 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' ) {
2018-04-22 21:10:43 +02:00
const result = await resizeImage _ ( filePath , targetPath , resource . mime ) ;
2017-11-11 00:18:00 +02:00
} else {
2018-05-03 12:31:07 +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.' ) ;
2017-11-11 00:18:00 +02:00
await fs . copy ( filePath , targetPath , { overwrite : true } ) ;
}
await Resource . save ( resource , { isNew : true } ) ;
2018-05-23 13:14:38 +02:00
return resource ;
}
shim . attachFileToNote = async function ( note , filePath , position = null ) {
2018-05-25 09:51:54 +02:00
const 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 ) ) ;
2018-02-25 19:01:16 +02:00
newBody . push ( Resource . markdownTag ( resource ) ) ;
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 ) ;
}
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 {
throw new Error ( 'Node support not implemented' ) ;
}
}
2017-10-15 13:13:09 +02:00
const nodeFetch = require ( 'node-fetch' ) ;
2017-11-05 18:51:03 +02:00
shim . readLocalFileBase64 = ( path ) => {
const data = fs . readFileSync ( path ) ;
return new Buffer ( data ) . toString ( 'base64' ) ;
}
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 ) ;
if ( ! validatedUrl ) throw new Error ( 'Not a valid URL: ' + url ) ;
2017-11-13 00:52:54 +02:00
return shim . fetchWithRetry ( ( ) => {
return nodeFetch ( url , options )
} , options ) ;
2017-10-15 13:13:09 +02:00
}
2017-07-24 20:01:40 +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' ;
2017-11-13 00:52:54 +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 ,
text : ( ) => { return response . statusMessage ; } ,
json : ( ) => { return { message : response . statusCode + ': ' + response . statusMessage } ; } ,
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 ,
path : url . path + ( url . query ? '?' + url . query : '' ) ,
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 ;
const cleanUpOnError = ( error ) => {
// We ignore any unlink error as we only want to report on the main error
fs . unlink ( filePath ) . catch ( ( ) => { } ) . then ( ( ) => {
if ( file ) {
file . close ( ( ) => {
file = null ;
reject ( error ) ;
} ) ;
} else {
reject ( error ) ;
}
} ) ;
}
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
} ) ;
2017-10-22 14:45:56 +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 ) ;
2017-07-10 20:09:58 +02:00
}
2017-12-18 22:46:22 +02:00
shim . uploadBlob = async function ( url , options ) {
if ( ! options || ! options . path ) throw new Error ( 'uploadBlob: source file path is missing' ) ;
const content = await fs . readFile ( options . path ) ;
options = Object . assign ( { } , options , {
body : content ,
} ) ;
return shim . fetch ( url , options ) ;
}
2018-02-15 20:33:08 +02:00
shim . stringByteLength = function ( string ) {
return Buffer . byteLength ( string , 'utf-8' ) ;
}
2018-03-24 21:35:10 +02:00
shim . Buffer = Buffer ;
2018-03-27 01:55:44 +02:00
shim . openUrl = ( url ) => {
const { bridge } = require ( 'electron' ) . remote . require ( './bridge' ) ;
bridge ( ) . openExternal ( url )
}
2018-06-25 19:14:57 +02:00
shim . waitForFrame = ( ) => { }
2017-07-10 20:09:58 +02:00
}
2017-11-03 02:13:17 +02:00
module . exports = { shimInit } ;