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.
		
		
		
		
		
			
		
			
	
	
		
			131 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
		
		
			
		
	
	
			131 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
| 
											5 years ago
										 | import crypto from 'crypto'; | ||
|  | import path from 'path'; | ||
|  | 
 | ||
|  | import fse from 'fs-extra'; | ||
| 
											4 years ago
										 | import { isArrayBuffer, isBuffer, isString } from 'lodash'; | ||
| 
											4 years ago
										 | import { | ||
| 
											4 years ago
										 |   decryptAttachmentBufferRenderer, | ||
|  |   encryptAttachmentBufferRenderer, | ||
|  | } from './local_attachments_encrypter'; | ||
| 
											8 years ago
										 | 
 | ||
| 
											4 years ago
										 | // to me, this file is only used in the renderer
 | ||
|  | // import { decryptAttachmentBuffer, encryptAttachmentBuffer } from './encrypt_attachment_buffer';
 | ||
| 
											8 years ago
										 | 
 | ||
| 
											8 years ago
										 | //      createReader :: AttachmentsPath ->
 | ||
|  | //                      RelativePath ->
 | ||
|  | //                      IO (Promise ArrayBuffer)
 | ||
| 
											5 years ago
										 | export const createReader = (root: string) => { | ||
| 
											8 years ago
										 |   if (!isString(root)) { | ||
| 
											8 years ago
										 |     throw new TypeError("'root' must be a path"); | ||
| 
											8 years ago
										 |   } | ||
|  | 
 | ||
| 
											5 years ago
										 |   return async (relativePath: string) => { | ||
| 
											8 years ago
										 |     if (!isString(relativePath)) { | ||
| 
											8 years ago
										 |       throw new TypeError("'relativePath' must be a string"); | ||
| 
											8 years ago
										 |     } | ||
|  |     const absolutePath = path.join(root, relativePath); | ||
| 
											8 years ago
										 |     const normalized = path.normalize(absolutePath); | ||
|  |     if (!normalized.startsWith(root)) { | ||
|  |       throw new Error('Invalid relative path'); | ||
|  |     } | ||
|  |     const buffer = await fse.readFile(normalized); | ||
| 
											4 years ago
										 |     if (!isBuffer(buffer)) { | ||
|  |       throw new TypeError("'bufferIn' must be a buffer"); | ||
|  |     } | ||
| 
											5 years ago
										 | 
 | ||
| 
											4 years ago
										 |     const decryptedData = await decryptAttachmentBufferRenderer(buffer.buffer); | ||
| 
											5 years ago
										 | 
 | ||
|  |     return decryptedData.buffer; | ||
| 
											8 years ago
										 |   }; | ||
|  | }; | ||
|  | 
 | ||
| 
											8 years ago
										 | //      createWriterForNew :: AttachmentsPath ->
 | ||
|  | //                            ArrayBuffer ->
 | ||
|  | //                            IO (Promise RelativePath)
 | ||
| 
											5 years ago
										 | export const createWriterForNew = (root: string) => { | ||
| 
											8 years ago
										 |   if (!isString(root)) { | ||
| 
											8 years ago
										 |     throw new TypeError("'root' must be a path"); | ||
| 
											8 years ago
										 |   } | ||
|  | 
 | ||
| 
											5 years ago
										 |   return async (arrayBuffer: ArrayBuffer) => { | ||
| 
											8 years ago
										 |     if (!isArrayBuffer(arrayBuffer)) { | ||
| 
											8 years ago
										 |       throw new TypeError("'arrayBuffer' must be an array buffer"); | ||
| 
											8 years ago
										 |     } | ||
|  | 
 | ||
| 
											4 years ago
										 |     const name = createName(); | ||
|  |     const relativePath = getRelativePath(name); | ||
|  |     return createWriterForExisting(root)({ | ||
| 
											8 years ago
										 |       data: arrayBuffer, | ||
|  |       path: relativePath, | ||
|  |     }); | ||
|  |   }; | ||
|  | }; | ||
|  | 
 | ||
|  | //      createWriter :: AttachmentsPath ->
 | ||
|  | //                      { data: ArrayBuffer, path: RelativePath } ->
 | ||
|  | //                      IO (Promise RelativePath)
 | ||
| 
											4 years ago
										 | const createWriterForExisting = (root: string) => { | ||
| 
											8 years ago
										 |   if (!isString(root)) { | ||
| 
											8 years ago
										 |     throw new TypeError("'root' must be a path"); | ||
| 
											8 years ago
										 |   } | ||
|  | 
 | ||
| 
											5 years ago
										 |   return async ({ | ||
|  |     data: arrayBuffer, | ||
|  |     path: relativePath, | ||
|  |   }: { data?: ArrayBuffer; path?: string } = {}) => { | ||
| 
											8 years ago
										 |     if (!isString(relativePath)) { | ||
| 
											8 years ago
										 |       throw new TypeError("'relativePath' must be a path"); | ||
| 
											8 years ago
										 |     } | ||
|  | 
 | ||
|  |     if (!isArrayBuffer(arrayBuffer)) { | ||
| 
											8 years ago
										 |       throw new TypeError("'arrayBuffer' must be an array buffer"); | ||
| 
											8 years ago
										 |     } | ||
|  | 
 | ||
| 
											8 years ago
										 |     const absolutePath = path.join(root, relativePath); | ||
| 
											8 years ago
										 |     const normalized = path.normalize(absolutePath); | ||
|  |     if (!normalized.startsWith(root)) { | ||
|  |       throw new Error('Invalid relative path'); | ||
|  |     } | ||
|  | 
 | ||
|  |     await fse.ensureFile(normalized); | ||
| 
											4 years ago
										 |     if (!isArrayBuffer(arrayBuffer)) { | ||
|  |       throw new TypeError("'bufferIn' must be an array buffer"); | ||
|  |     } | ||
|  | 
 | ||
|  |     const { encryptedBufferWithHeader } = (await encryptAttachmentBufferRenderer( | ||
|  |       arrayBuffer | ||
|  |     )) as any; | ||
| 
											5 years ago
										 |     const buffer = Buffer.from(encryptedBufferWithHeader.buffer); | ||
|  | 
 | ||
| 
											8 years ago
										 |     await fse.writeFile(normalized, buffer); | ||
| 
											5 years ago
										 | 
 | ||
| 
											8 years ago
										 |     return relativePath; | ||
|  |   }; | ||
|  | }; | ||
|  | 
 | ||
|  | //      createName :: Unit -> IO String
 | ||
| 
											4 years ago
										 | const createName = () => { | ||
| 
											8 years ago
										 |   const buffer = crypto.randomBytes(32); | ||
|  |   return buffer.toString('hex'); | ||
|  | }; | ||
|  | 
 | ||
| 
											8 years ago
										 | //      getRelativePath :: String -> Path
 | ||
| 
											4 years ago
										 | const getRelativePath = (name: string) => { | ||
| 
											8 years ago
										 |   if (!isString(name)) { | ||
| 
											8 years ago
										 |     throw new TypeError("'name' must be a string"); | ||
| 
											8 years ago
										 |   } | ||
|  | 
 | ||
|  |   const prefix = name.slice(0, 2); | ||
|  |   return path.join(prefix, name); | ||
|  | }; | ||
| 
											8 years ago
										 | 
 | ||
| 
											7 years ago
										 | //      createAbsolutePathGetter :: RootPath -> RelativePath -> AbsolutePath
 | ||
| 
											5 years ago
										 | export const createAbsolutePathGetter = (rootPath: string) => (relativePath: string) => { | ||
| 
											8 years ago
										 |   const absolutePath = path.join(rootPath, relativePath); | ||
|  |   const normalized = path.normalize(absolutePath); | ||
|  |   if (!normalized.startsWith(rootPath)) { | ||
|  |     throw new Error('Invalid relative path'); | ||
|  |   } | ||
|  |   return normalized; | ||
|  | }; |