| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 | const pickle = require('chromium-pickle-js')const path = require('path')const UINT64 = require('cuint').UINT64const fs = require('fs')/* global WIKI *//** * Based of express-serve-asar (https://github.com/toyobayashi/express-serve-asar) * by Fenglin Li (https://github.com/toyobayashi) */const packages = {  'twemoji': path.join(WIKI.ROOTPATH, `assets/svg/twemoji.asar`)}module.exports = {  fdCache: {},  async serve (pkgName, req, res, next) {    const file = this.readFilesystemSync(packages[pkgName])    const { filesystem, fd } = file    const info = filesystem.getFile(req.path.substring(1))    if (info) {      res.set({        'Content-Type': 'image/svg+xml',        'Content-Length': info.size      })      fs.createReadStream('', {        fd,        autoClose: false,        start: 8 + filesystem.headerSize + parseInt(info.offset, 10),        end: 8 + filesystem.headerSize + parseInt(info.offset, 10) + info.size - 1      }).on('error', (err) => {        WIKI.logger.warn(err)        res.sendStatus(404)      }).pipe(res.status(200))    } else {      res.sendStatus(404)    }  },  async unload () {    const fds = Object.values(this.fdCache)    if (fds.length > 0) {      WIKI.logger.info('Closing ASAR file descriptors...')      const closeAsync = require('util').promisify(fs.close)      await Promise.all(fds.map(x => closeAsync(x.fd)))      this.fdCache = {}    }  },  readArchiveHeaderSync (fd) {    let size    let headerBuf    const sizeBuf = Buffer.alloc(8)    if (fs.readSync(fd, sizeBuf, 0, 8, null) !== 8) {      throw new Error('Unable to read header size')    }    const sizePickle = pickle.createFromBuffer(sizeBuf)    size = sizePickle.createIterator().readUInt32()    headerBuf = Buffer.alloc(size)    if (fs.readSync(fd, headerBuf, 0, size, null) !== size) {      throw new Error('Unable to read header')    }    const headerPickle = pickle.createFromBuffer(headerBuf)    const header = headerPickle.createIterator().readString()    return { header: JSON.parse(header), headerSize: size }  },  readFilesystemSync (archive) {    if (!this.fdCache[archive]) {      const fd = fs.openSync(archive, 'r')      const header = this.readArchiveHeaderSync(fd)      const filesystem = new Filesystem(archive)      filesystem.header = header.header      filesystem.headerSize = header.headerSize      this.fdCache[archive] = {        fd,        filesystem: filesystem      }    }    return this.fdCache[archive]  }}class Filesystem {  constructor (src) {    this.src = path.resolve(src)    this.header = { files: {} }    this.offset = UINT64(0)  }  searchNodeFromDirectory (p) {    let json = this.header    const dirs = p.split(path.sep)    for (const dir of dirs) {      if (dir !== '.') {        json = json.files[dir]      }    }    return json  }  getNode (p) {    const node = this.searchNodeFromDirectory(path.dirname(p))    const name = path.basename(p)    if (name) {      return node.files[name]    } else {      return node    }  }  getFile (p, followLinks) {    followLinks = typeof followLinks === 'undefined' ? true : followLinks    const info = this.getNode(p)    if (!info) {      return false    }    if (info.link && followLinks) {      return this.getFile(info.link)    } else {      return info    }  }}
 |