Files
webmail/lib/file-errors.ts
T
Linus Rath e712454ec2 fix: store awkward file names and explain file errors on Stalwart 1.0
Stalwart 1.0 checks names more strictly than its capability says: a
reserved name may not be the stem either (`con.txt`, `Aux.tar.gz`), and
control characters are refused (macOS `Icon\r`). Names over 255 bytes
of UTF-8 were never accepted. Uploaded names are now made acceptable
before they are sent: control characters become "_", a reserved stem
gets "_" after it (`con.txt` becomes `con_.txt`), and a long name is
shortened, keeping its extension. The rename and new-folder dialogs
refuse such names with their own messages. The rules apply wherever the
server publishes naming rules, so 0.16.6 to 0.16.23 get `con_.txt` too.

One refused file used to abort a whole upload. Uploads now go on and
report the files that failed at the end, and a folder upload skips the
contents of a folder it could not create instead of dropping them into
the folder the upload started in. What would sit deeper than 1.0's
`maxFileNodeDepth` (64) is left out before it is uploaded, and a folder
copy that would go past it fails before the first create.

File errors keep their JMAP type, and the Files toasts now say why an
action failed: too many files or folders (`overQuota`), folders nested
too deeply, a refused name, missing rights, or a file too large. New
and copied files get a bare lowercase media type, which 1.0 requires.
2026-09-25 18:37:41 +02:00

41 lines
1.5 KiB
TypeScript

import { jmapError, jmapErrorProperties, jmapErrorType } from '@/lib/jmap/jmap-error';
/**
* Why a FileNode write failed, from the SetError the JMAP client threw
* (see jmapError), for the Files toasts to explain under their own title.
*/
export type FileErrorKind = 'overQuota' | 'tooDeep' | 'invalidName' | 'forbidden' | 'tooLarge';
// Stalwart 1.0's description for a node past maxFileNodeDepth.
const TOO_DEEP = 'Maximum folder depth exceeded.';
/** The error for a node that would sit deeper than the server allows, worded as Stalwart 1.0 does. */
export function folderDepthError(): Error {
return jmapError(TOO_DEEP, { type: 'invalidProperties', properties: ['parentId'] });
}
export function fileErrorKind(error: unknown): FileErrorKind | null {
switch (jmapErrorType(error)) {
// Stalwart 1.0, once an administrator limits files or folders per account.
case 'overQuota':
return 'overQuota';
// 1.0 for content over 4 GiB.
case 'tooLarge':
return 'tooLarge';
case 'forbidden':
return 'forbidden';
case 'invalidProperties': {
const properties = jmapErrorProperties(error);
if (properties.includes('name')) return 'invalidName';
// `parentId` is refused for other reasons too (the folder is gone, a
// folder moved into itself); only the description tells depth apart.
if (properties.includes('parentId') && error instanceof Error && /depth/i.test(error.message)) {
return 'tooDeep';
}
return null;
}
default:
return null;
}
}