From 207ba7ec8e6b665cc318aa20cd50ebabbc1024af Mon Sep 17 00:00:00 2001
From: James Ross <itzexor@gmail.com>
Date: Mon, 18 Sep 2023 13:08:19 -0700
Subject: [PATCH] x-accel: encode all paths to URIs updates util function 
 encodeUriPath to use node:url with a file:// path prefix, and updates all
 instances x-accel redirection to use this helper util instead of sending
 unencoded paths into the header.

---
 server/controllers/BackupController.js      |  6 ++++--
 server/controllers/LibraryItemController.js | 22 ++++++++++++---------
 server/managers/CacheManager.js             | 11 +++++++----
 server/utils/fileUtils.js                   |  3 ++-
 4 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/server/controllers/BackupController.js b/server/controllers/BackupController.js
index 207b3c74..f33624ac 100644
--- a/server/controllers/BackupController.js
+++ b/server/controllers/BackupController.js
@@ -1,4 +1,5 @@
 const Logger = require('../Logger')
+const { encodeUriPath } = require('../utils/fileUtils')
 
 class BackupController {
   constructor() { }
@@ -37,8 +38,9 @@ class BackupController {
    */
   download(req, res) {
     if (global.XAccel) {
-      Logger.debug(`Use X-Accel to serve static file ${req.backup.fullPath}`)
-      return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + req.backup.fullPath }).send()
+      const encodedURI = encodeUriPath(global.XAccel + req.backup.fullPath)
+      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
     }
     res.sendFile(req.backup.fullPath)
   }
diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js
index 4def9c04..fa6d5f2f 100644
--- a/server/controllers/LibraryItemController.js
+++ b/server/controllers/LibraryItemController.js
@@ -7,7 +7,7 @@ const Database = require('../Database')
 const zipHelpers = require('../utils/zipHelpers')
 const { reqSupportsWebp } = require('../utils/index')
 const { ScanResult } = require('../utils/constants')
-const { getAudioMimeTypeFromExtname } = require('../utils/fileUtils')
+const { getAudioMimeTypeFromExtname, encodeUriPath } = require('../utils/fileUtils')
 const LibraryItemScanner = require('../scanner/LibraryItemScanner')
 const AudioFileScanner = require('../scanner/AudioFileScanner')
 const Scanner = require('../scanner/Scanner')
@@ -235,8 +235,9 @@ class LibraryItemController {
       }
 
       if (global.XAccel) {
-        Logger.debug(`Use X-Accel to serve static file ${libraryItem.media.coverPath}`)
-        return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + libraryItem.media.coverPath }).send()
+        const encodedURI = encodeUriPath(global.XAccel + libraryItem.media.coverPath)
+        Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+        return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
       }
       return res.sendFile(libraryItem.media.coverPath)
     }
@@ -575,8 +576,9 @@ class LibraryItemController {
     const libraryFile = req.libraryFile
 
     if (global.XAccel) {
-      Logger.debug(`Use X-Accel to serve static file ${libraryFile.metadata.path}`)
-      return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + libraryFile.metadata.path }).send()
+      const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path)
+      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
     }
 
     // Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
@@ -632,8 +634,9 @@ class LibraryItemController {
     Logger.info(`[LibraryItemController] User "${req.user.username}" requested file download at "${libraryFile.metadata.path}"`)
 
     if (global.XAccel) {
-      Logger.debug(`Use X-Accel to serve static file ${libraryFile.metadata.path}`)
-      return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + libraryFile.metadata.path }).send()
+      const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path)
+      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
     }
 
     // Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
@@ -673,8 +676,9 @@ class LibraryItemController {
     const ebookFilePath = ebookFile.metadata.path
 
     if (global.XAccel) {
-      Logger.debug(`Use X-Accel to serve static file ${ebookFilePath}`)
-      return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + ebookFilePath }).send()
+      const encodedURI = encodeUriPath(global.XAccel + ebookFilePath)
+      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
     }
 
     res.sendFile(ebookFilePath)
diff --git a/server/managers/CacheManager.js b/server/managers/CacheManager.js
index fb0cd26c..d7baee3b 100644
--- a/server/managers/CacheManager.js
+++ b/server/managers/CacheManager.js
@@ -3,6 +3,7 @@ const fs = require('../libs/fsExtra')
 const stream = require('stream')
 const Logger = require('../Logger')
 const { resizeImage } = require('../utils/ffmpegHelpers')
+const { encodeUriPath } = require('../utils/fileUtils')
 
 class CacheManager {
   constructor() {
@@ -50,8 +51,9 @@ class CacheManager {
     // Cache exists
     if (await fs.pathExists(path)) {
       if (global.XAccel) {
-        Logger.debug(`Use X-Accel to serve static file ${path}`)
-        return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + path }).send()
+        const encodedURI = encodeUriPath(global.XAccel + path)
+        Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+        return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
       }
 
       const r = fs.createReadStream(path)
@@ -73,8 +75,9 @@ class CacheManager {
     if (!writtenFile) return res.sendStatus(500)
 
     if (global.XAccel) {
-      Logger.debug(`Use X-Accel to serve static file ${writtenFile}`)
-      return res.status(204).header({ 'X-Accel-Redirect': global.XAccel + writtenFile }).send()
+      const encodedURI = encodeUriPath(global.XAccel + writtenFile)
+      Logger.debug(`Use X-Accel to serve static file ${encodedURI}`)
+      return res.status(204).header({ 'X-Accel-Redirect': encodedURI }).send()
     }
 
     var readStream = fs.createReadStream(writtenFile)
diff --git a/server/utils/fileUtils.js b/server/utils/fileUtils.js
index 16aae18c..966c7a93 100644
--- a/server/utils/fileUtils.js
+++ b/server/utils/fileUtils.js
@@ -293,5 +293,6 @@ module.exports.removeFile = (path) => {
 }
 
 module.exports.encodeUriPath = (path) => {
-  return filePathToPOSIX(path).replace(/%/g, '%25').replace(/#/g, '%23')
+  const uri = new URL(path, "file://")
+  return uri.pathname
 }