mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
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.
23 lines
1.0 KiB
TypeScript
23 lines
1.0 KiB
TypeScript
/** What Stalwart stores for content without a usable media type. */
|
|
export const OCTET_STREAM = 'application/octet-stream';
|
|
|
|
// RFC 6838 restricted-name: a letter or digit, then at most 126 more from a
|
|
// small set.
|
|
const RESTRICTED_NAME = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}$/;
|
|
|
|
/**
|
|
* The essence of a media type: `top/sub` in lowercase, without parameters
|
|
* ("Text/Plain; charset=utf-8" becomes "text/plain"), or
|
|
* application/octet-stream when there is none. Stalwart 1.0 refuses any
|
|
* other FileNode `type` (`is_valid_media_type`, types/src/media_type/mod.rs);
|
|
* 0.16.6+ take anything with a "/" up to 256 characters.
|
|
*/
|
|
export function mediaTypeEssence(value: string | null | undefined): string {
|
|
const essence = (value ?? '').split(';', 1)[0].replace(/^[\t\n\f\r ]+|[\t\n\f\r ]+$/g, '');
|
|
const slash = essence.indexOf('/');
|
|
if (slash < 0) return OCTET_STREAM;
|
|
const top = essence.slice(0, slash);
|
|
const sub = essence.slice(slash + 1);
|
|
return RESTRICTED_NAME.test(top) && RESTRICTED_NAME.test(sub) ? essence.toLowerCase() : OCTET_STREAM;
|
|
}
|