diff --git a/internal/web/files.go b/internal/web/files.go index 07b91a7..91b35b8 100644 --- a/internal/web/files.go +++ b/internal/web/files.go @@ -142,7 +142,7 @@ func (s *Server) handleFilesMkdir(w http.ResponseWriter, r *http.Request, root, } name := strings.TrimSpace(r.PostForm.Get("name")) if !resourceNameRe.MatchString(name) { - http.Error(w, "name must be 1-64 letters, digits, '-' or '_'", http.StatusBadRequest) + http.Error(w, "name must be 1-64 letters, digits, dots, '-' or '_'", http.StatusBadRequest) return } diff --git a/internal/web/resources.go b/internal/web/resources.go index 6e051a4..8d76f19 100644 --- a/internal/web/resources.go +++ b/internal/web/resources.go @@ -12,7 +12,8 @@ import ( // resourceNameRe restricts calendar/address book names to characters that // are safe as both a URL path segment and a filesystem directory name. -var resourceNameRe = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,64}$`) +// Includes dots for hidden files/folders (Unix convention). +var resourceNameRe = regexp.MustCompile(`^[a-zA-Z0-9._-]{1,64}$`) // hexColorRe validates the 6-digit hex color format produced by an HTML // , e.g. "#3b82f6". diff --git a/web/static/files.js b/web/static/files.js index 47a779b..5bdcbc6 100644 --- a/web/static/files.js +++ b/web/static/files.js @@ -56,7 +56,7 @@ void upload(Array.from(folderInput.files || [])); folderInput.value = ""; }); - const folderNameRe = /^[a-zA-Z0-9_-]{1,64}$/; + const folderNameRe = /^[a-zA-Z0-9._-]{1,64}$/; newFolderButton?.addEventListener("click", async () => { const name = window.prompt("New folder name:"); if (!name) { diff --git a/web/ts/files.ts b/web/ts/files.ts index dd98de0..6c1742d 100644 --- a/web/ts/files.ts +++ b/web/ts/files.ts @@ -61,7 +61,7 @@ folderInput.value = ""; }); - const folderNameRe = /^[a-zA-Z0-9_-]{1,64}$/; + const folderNameRe = /^[a-zA-Z0-9._-]{1,64}$/; newFolderButton?.addEventListener("click", async () => { const name = window.prompt("New folder name:");