2020-12-09 14:50:51 +02:00
import time from './time' ;
import Setting from './models/Setting' ;
import { filename , fileExtension } from './path-utils' ;
2020-03-10 01:24:57 +02:00
const md5 = require ( 'md5' ) ;
2024-07-26 13:22:49 +02:00
import resolvePathWithinDir from './utils/resolvePathWithinDir' ;
2024-03-06 12:03:11 +02:00
import { Buffer } from 'buffer' ;
2018-11-20 02:42:21 +02:00
2020-12-09 14:50:51 +02:00
export interface Stat {
2023-12-06 21:24:00 +02:00
birthtime : Date ;
mtime : Date ;
2020-12-09 14:50:51 +02:00
isDirectory ( ) : boolean ;
path : string ;
size : number ;
}
export interface ReadDirStatsOptions {
recursive : boolean ;
}
2024-08-02 15:51:49 +02:00
export interface RemoveOptions {
recursive? : boolean ;
}
2020-12-09 14:50:51 +02:00
export default class FsDriverBase {
public async stat ( _path : string ) : Promise < Stat > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: stat()' ) ;
2020-12-09 14:50:51 +02:00
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2023-06-30 10:11:26 +02:00
public async readFile ( _path : string , _encoding = 'utf8' ) : Promise < any > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: readFile' ) ;
2021-09-06 17:57:07 +02:00
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2023-10-22 12:51:31 +02:00
public async appendFile ( _path : string , _content : string , _encoding = 'base64' ) : Promise < any > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: appendFile' ) ;
2023-10-22 12:51:31 +02:00
}
2021-09-06 17:57:07 +02:00
public async copy ( _source : string , _dest : string ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: copy' ) ;
2021-09-06 17:57:07 +02:00
}
2023-07-16 18:42:42 +02:00
public async chmod ( _source : string , _mode : string | number ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: chmod' ) ;
2023-07-16 18:42:42 +02:00
}
2024-03-06 12:03:11 +02:00
// Must also create parent directories
2021-09-06 17:57:07 +02:00
public async mkdir ( _path : string ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: mkdir' ) ;
2021-09-06 17:57:07 +02:00
}
public async unlink ( _path : string ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: unlink' ) ;
2021-09-06 17:57:07 +02:00
}
public async move ( _source : string , _dest : string ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: move' ) ;
2021-09-06 17:57:07 +02:00
}
2023-12-06 21:24:00 +02:00
public async rename ( source : string , dest : string ) {
return this . move ( source , dest ) ;
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2023-06-30 10:11:26 +02:00
public async readFileChunk ( _handle : any , _length : number , _encoding = 'base64' ) : Promise < string > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: readFileChunk' ) ;
2021-09-06 17:57:07 +02:00
}
2024-08-02 15:51:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async readFileChunkAsBuffer ( handle : any , length : number ) : Promise < Buffer > {
const chunk = await this . readFileChunk ( handle , length , 'base64' ) ;
if ( chunk ) {
return Buffer . from ( chunk , 'base64' ) ;
} else {
return null ;
}
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2021-09-06 17:57:07 +02:00
public async open ( _path : string , _mode : any ) : Promise < any > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: open' ) ;
2021-09-06 17:57:07 +02:00
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2021-09-06 17:57:07 +02:00
public async close ( _handle : any ) : Promise < any > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: close' ) ;
2021-09-06 17:57:07 +02:00
}
2024-08-02 15:51:49 +02:00
// Like .readFile, but returns a File object.
public async fileAtPath ( _path : string ) : Promise < File > {
throw new Error ( 'Not implemented: fileAtPath' ) ;
}
2020-12-09 14:50:51 +02:00
public async readDirStats ( _path : string , _options : ReadDirStatsOptions = null ) : Promise < Stat [ ] > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: readDirStats' ) ;
2020-12-09 14:50:51 +02:00
}
public async exists ( _path : string ) : Promise < boolean > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: exists' ) ;
2020-12-09 14:50:51 +02:00
}
2024-08-02 15:51:49 +02:00
public async remove ( _path : string , _options : RemoveOptions = null ) : Promise < void > {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: remove' ) ;
2020-12-09 14:50:51 +02:00
}
2024-04-25 14:31:48 +02:00
public async setTimestamp ( _path : string , _timestampDate : Date ) : Promise < void > {
throw new Error ( 'Not implemented: setTimestamp' ) ;
}
2020-12-09 14:50:51 +02:00
public async isDirectory ( path : string ) {
2018-02-25 23:08:32 +02:00
const stat = await this . stat ( path ) ;
return ! stat ? false : stat . isDirectory ( ) ;
}
2023-06-30 10:11:26 +02:00
public async writeFile ( _path : string , _content : string , _encoding = 'base64' ) : Promise < void > {
2020-12-09 14:50:51 +02:00
throw new Error ( 'Not implemented' ) ;
}
2023-12-06 21:24:00 +02:00
public async md5File ( _path : string ) : Promise < string > {
throw new Error ( 'Not implemented: md5File' ) ;
}
public resolve ( . . . _paths : string [ ] ) : string {
throw new Error ( 'Not implemented: resolve' ) ;
}
2024-03-06 12:03:11 +02:00
// Resolves the provided relative path to an absolute path within baseDir. The function
// also checks that the absolute path is within baseDir, to avoid security issues.
// It is expected that baseDir is a safe path (not user-provided).
public resolveRelativePathWithinDir ( baseDir : string , relativePath : string ) {
2024-07-26 13:22:49 +02:00
const resolvedPath = resolvePathWithinDir ( baseDir , relativePath ) ;
if ( ! resolvedPath ) throw new Error ( ` Resolved path for relative path " ${ relativePath } " is not within base directory " ${ baseDir } " (Was resolved to ${ resolvedPath } ) ` ) ;
2024-03-06 12:03:11 +02:00
return resolvedPath ;
2023-12-06 21:24:00 +02:00
}
public getExternalDirectoryPath ( ) : Promise < string | undefined > {
throw new Error ( 'Not implemented: getExternalDirectoryPath' ) ;
}
2024-08-02 15:51:49 +02:00
public getCacheDirectoryPath ( ) : string {
throw new Error ( 'Not implemented: getCacheDirectoryPath' ) ;
}
public getAppDirectoryPath ( ) : string {
throw new Error ( 'Not implemented: getCacheDirectoryPath' ) ;
}
2023-12-06 21:24:00 +02:00
public isUsingAndroidSAF() {
return false ;
}
2024-03-06 12:03:11 +02:00
public async appendBinaryReadableToFile ( path : string , readable : { read ( ) : number [ ] | null } ) {
let data : number [ ] | null = null ;
while ( ( data = readable . read ( ) ) !== null ) {
const buff = Buffer . from ( data ) ;
const base64Data = buff . toString ( 'base64' ) ;
await this . appendFile ( path , base64Data , 'base64' ) ;
}
}
2020-12-09 14:50:51 +02:00
protected async readDirStatsHandleRecursion_ ( basePath : string , stat : Stat , output : Stat [ ] , options : ReadDirStatsOptions ) : Promise < Stat [ ] > {
2018-02-25 23:08:32 +02:00
if ( options . recursive && stat . isDirectory ( ) ) {
2019-09-19 23:51:18 +02:00
const subPath = ` ${ basePath } / ${ stat . path } ` ;
2018-02-25 23:08:32 +02:00
const subStats = await this . readDirStats ( subPath , options ) ;
for ( let j = 0 ; j < subStats . length ; j ++ ) {
const subStat = subStats [ j ] ;
2019-09-19 23:51:18 +02:00
subStat . path = ` ${ stat . path } / ${ subStat . path } ` ;
2018-02-25 23:08:32 +02:00
output . push ( subStat ) ;
}
}
return output ;
}
2023-06-30 10:11:26 +02:00
public async findUniqueFilename ( name : string , reservedNames : string [ ] = null , markdownSafe = false ) : Promise < string > {
2020-01-18 15:16:14 +02:00
if ( reservedNames === null ) {
reservedNames = [ ] ;
}
2018-11-20 02:42:21 +02:00
let counter = 1 ;
2024-06-11 08:41:23 +02:00
// On Windows, ./FiLe.md and ./file.md are equivalent file paths.
// As such, to avoid overwriting reserved names, comparisons need to be
// case-insensitive.
reservedNames = reservedNames . map ( name = > name . toLowerCase ( ) ) ;
const isReserved = ( testName : string ) = > {
return reservedNames . includes ( testName . toLowerCase ( ) ) ;
} ;
2020-03-14 01:46:14 +02:00
const nameNoExt = filename ( name , true ) ;
2018-11-20 02:42:21 +02:00
let extension = fileExtension ( name ) ;
2019-09-19 23:51:18 +02:00
if ( extension ) extension = ` . ${ extension } ` ;
2018-11-20 02:42:21 +02:00
let nameToTry = nameNoExt + extension ;
while ( true ) {
2020-01-18 15:16:14 +02:00
// Check if the filename does not exist in the filesystem and is not reserved
2024-06-11 08:41:23 +02:00
const exists = await this . exists ( nameToTry ) || isReserved ( nameToTry ) ;
2018-11-20 02:42:21 +02:00
if ( ! exists ) return nameToTry ;
2021-08-23 01:35:45 +02:00
if ( ! markdownSafe ) {
nameToTry = ` ${ nameNoExt } ( ${ counter } ) ${ extension } ` ;
} else {
nameToTry = ` ${ nameNoExt } - ${ counter } ${ extension } ` ;
}
2018-11-20 02:42:21 +02:00
counter ++ ;
2020-02-11 16:14:04 +02:00
if ( counter >= 1000 ) {
nameToTry = ` ${ nameNoExt } ( ${ new Date ( ) . getTime ( ) } ) ${ extension } ` ;
await time . msleep ( 10 ) ;
}
if ( counter >= 1100 ) throw new Error ( 'Cannot find unique filename' ) ;
2018-11-20 02:42:21 +02:00
}
}
2020-12-09 14:50:51 +02:00
public async removeAllThatStartWith ( dirPath : string , filenameStart : string ) {
2019-05-11 12:46:13 +02:00
if ( ! filenameStart || ! dirPath ) throw new Error ( 'dirPath and filenameStart cannot be empty' ) ;
const stats = await this . readDirStats ( dirPath ) ;
for ( const stat of stats ) {
if ( stat . path . indexOf ( filenameStart ) === 0 ) {
2019-09-19 23:51:18 +02:00
await this . remove ( ` ${ dirPath } / ${ stat . path } ` ) ;
2019-05-11 12:46:13 +02:00
}
}
}
2023-06-30 10:11:26 +02:00
public async waitTillExists ( path : string , timeout = 10000 ) {
2019-05-12 02:15:52 +02:00
const startTime = Date . now ( ) ;
while ( true ) {
const e = await this . exists ( path ) ;
if ( e ) return true ;
if ( Date . now ( ) - startTime > timeout ) return false ;
await time . msleep ( 100 ) ;
}
}
2020-03-10 01:24:57 +02:00
// TODO: move out of here and make it part of joplin-renderer
// or assign to option using .bind(fsDriver())
2020-12-09 14:50:51 +02:00
public async cacheCssToFile ( cssStrings : string [ ] ) {
2020-03-23 02:47:25 +02:00
const cssString = Array . isArray ( cssStrings ) ? cssStrings . join ( '\n' ) : cssStrings ;
2020-03-10 01:24:57 +02:00
const cssFilePath = ` ${ Setting . value ( 'tempDir' ) } / ${ md5 ( escape ( cssString ) ) } .css ` ;
if ( ! ( await this . exists ( cssFilePath ) ) ) {
await this . writeFile ( cssFilePath , cssString , 'utf8' ) ;
}
return {
path : cssFilePath ,
mime : 'text/css' ,
} ;
}
2018-02-25 23:08:32 +02:00
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2021-01-27 19:42:58 +02:00
public async tarExtract ( _options : any ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: tarExtract' ) ;
2021-01-27 19:42:58 +02:00
}
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2021-01-27 19:42:58 +02:00
public async tarCreate ( _options : any , _filePaths : string [ ] ) {
2024-03-06 12:03:11 +02:00
throw new Error ( 'Not implemented: tarCreate' ) ;
2021-01-27 19:42:58 +02:00
}
2020-12-09 14:50:51 +02:00
}