Skip to content

Commit

Permalink
fix: bad file name downloading non-ascii files with firefox on android
Browse files Browse the repository at this point in the history
  • Loading branch information
rejetto committed Oct 30, 2024
1 parent 39bb325 commit 2926a60
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/api.log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HTTP_NOT_ACCEPTABLE, HTTP_NOT_FOUND, wait } from './cross'
import events from './events'
import { loggers } from './log'
import { SendListReadable } from './SendList'
import { serveFile } from './serveFile'
import { forceDownload, serveFile } from './serveFile'
import { ips } from './ips'

export default {
Expand All @@ -15,7 +15,7 @@ export default {
throw HTTP_NOT_FOUND
if (!log.path)
throw HTTP_NOT_ACCEPTABLE
ctx.attachment(log.path)
forceDownload(ctx, log.path)
if (range)
ctx.request.header.range = `bytes=${range}`
if (ctx.method === 'POST') // this would cause method_not_allowed
Expand Down
2 changes: 1 addition & 1 deletion src/errorPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function getErrorSections() {
// to be used with errors whose recipient is possibly human
export async function sendErrorPage(ctx: Koa.Context, code=ctx.status) {
ctx.type = 'text'
ctx.set('content-disposition', '') // reset ctx.attachment
ctx.set('content-disposition', '') // reset ctx.attachment (or forceDownload)
ctx.status = code
const msg = HTTP_MESSAGES[ctx.status]
if (!msg) return
Expand Down
16 changes: 13 additions & 3 deletions src/serveFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@ import { getCurrentUsername } from './auth'
import { sendErrorPage } from './errorPages'
import { Readable } from 'stream'
import { createHash } from 'crypto'
import iconv from 'iconv-lite'

const allowedReferer = defineConfig('allowed_referer', '')
const maxDownloads = downloadLimiter(defineConfig(CFG.max_downloads, 0), () => true)
const maxDownloadsPerIp = downloadLimiter(defineConfig(CFG.max_downloads_per_ip, 0), ctx => ctx.ip)
const maxDownloadsPerAccount = downloadLimiter(defineConfig(CFG.max_downloads_per_account, 0), ctx => getCurrentUsername(ctx) || undefined)

function toAsciiEquivalent(s: string) {
return iconv.encode(iconv.decode(Buffer.from(s), 'utf-8'), 'ascii').toString().replaceAll('?', '')
}

export function forceDownload(ctx: Koa.Context, name='') {
// ctx.attachment is not working well on Windows. Eg: for file "èÖ.txt" it is producing `Content-Disposition: attachment; filename="??.txt"`. Koa uses module content-disposition, that actually produces a better result anyway: ``
ctx.set('Content-Disposition', 'attachment'
+ (name && `; filename="${toAsciiEquivalent(name)}"; filename*=UTF-8''${encodeURI(name).replace(/#/g, '%23')}`))
}

export async function serveFileNode(ctx: Koa.Context, node: VfsNode) {
const { source, mime } = node
const name = getNodeName(node)
Expand All @@ -37,7 +48,7 @@ export async function serveFileNode(ctx: Koa.Context, node: VfsNode) {
ctx.vfsNode = // legacy pre-0.51 (download-quota)
ctx.state.vfsNode = node // useful to tell service files from files shared by the user
if ('dl' in ctx.query) // please, download
ctx.attachment(name)
forceDownload(ctx, name)
else if (ctx.get('referer')?.endsWith('/') && with_(ctx.get('accept'), x => x && !x.includes('text')))
ctx.state.considerAsGui = true
await serveFile(ctx, source||'', mimeString)
Expand All @@ -63,8 +74,7 @@ const cacheControlDiskFiles = defineConfig('cache_control_disk_files', 5)
export async function serveFile(ctx: Koa.Context, source:string, mime?:string, content?: string | Buffer) {
if (!source)
return
const fn = basename(source)
mime = mime ?? mimeCfg.compiled()(fn)
mime ??= mimeCfg.compiled()(basename(source))
if (mime === undefined || mime === MIME_AUTO)
mime = mimetypes.lookup(source) || ''
if (mime)
Expand Down
4 changes: 2 additions & 2 deletions src/zip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createReadStream } from 'fs'
import fs from 'fs/promises'
import { defineConfig } from './config'
import { basename, dirname } from 'path'
import { applyRange, monitorAsDownload } from './serveFile'
import { applyRange, forceDownload, monitorAsDownload } from './serveFile'
import { HTTP_OK } from './const'

// expects 'node' to have had permissions checked by caller
Expand All @@ -19,7 +19,7 @@ export async function zipStreamFromFolder(node: VfsNode, ctx: Koa.Context) {
ctx.mime = 'zip'
// ctx.query.list is undefined | string | string[]
const name = list?.length === 1 ? safeDecodeURIComponent(basename(list[0]!)) : getNodeName(node)
ctx.attachment((isWindowsDrive(name) ? name[0] : (name || 'archive')) + '.zip')
forceDownload(ctx, (isWindowsDrive(name) ? name[0] : (name || 'archive')) + '.zip')
const filter = pattern2filter(String(ctx.query.search||''))
const walker = !list ? walkNode(node, { ctx, requiredPerm: 'can_archive' })
: (async function*(): AsyncIterableIterator<VfsNode> {
Expand Down

0 comments on commit 2926a60

Please sign in to comment.