webdav-server

An extremely simplistic WebDAV server built around go-webdav

git clone https://git.8pit.net/webdav-server.git

 1// Copyright (c) 2020 Simon Ser <contact@emersion.fr>
 2// Copyright (c) 2026 Sören Tempel <soeren@soeren-tempel.net>
 3//
 4// Permission is hereby granted, free of charge, to any person obtaining a copy
 5// of this software and associated documentation files (the "Software"), to deal
 6// in the Software without restriction, including without limitation the rights
 7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 8// copies of the Software, and to permit persons to whom the Software is
 9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all
12// copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21
22package main
23
24import (
25	"errors"
26	"flag"
27	"fmt"
28	"log"
29	"net"
30	"net/http"
31	"os"
32	"os/signal"
33	"path/filepath"
34	"strconv"
35	"syscall"
36
37	"github.com/emersion/go-webdav"
38)
39
40func main() {
41	var addr, mode string
42	cmdName := filepath.Base(os.Args[0])
43	defAddr := filepath.Join(os.TempDir(), cmdName)
44	// Keep this backwards-compatible with the default webdav-server options.
45	// See https://github.com/emersion/go-webdav/blob/master/cmd/webdav-server/main.go
46	flag.StringVar(&addr, "addr", defAddr, "listening address (path for Unix socket)")
47	flag.StringVar(&mode, "mode", "", "file mode in octal for the Unix socket")
48	flag.Usage = func() {
49		fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [options...] [directory]\n", cmdName)
50		flag.PrintDefaults()
51	}
52	flag.Parse()
53
54	path := flag.Arg(0)
55	if path == "" {
56		path = "."
57	}
58
59	handler := webdav.Handler{
60		FileSystem: webdav.LocalFileSystem(path),
61	}
62
63	ln, err := net.Listen("unix", addr)
64	if err != nil {
65		log.Fatalf("failed to create listener on address %q: %v", addr, err)
66	}
67	if mode != "" {
68		modeBits, err := strconv.ParseUint(mode, 8, 32)
69		if err != nil {
70			log.Fatalf("file mode has invalid value %q", mode)
71		}
72		mode := os.FileMode(modeBits)
73		if err := os.Chmod(addr, mode); err != nil {
74			log.Fatalf("failed to chmod Unix socket: %v", err)
75		}
76	}
77
78	srv := &http.Server{Addr: addr, Handler: &handler}
79	defer srv.Close() // This will close 'ln' too.
80
81	sigCh := make(chan os.Signal, 1)
82	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP)
83	go func() {
84		<-sigCh
85		log.Print("stopping server")
86		if err := srv.Close(); err != nil {
87			log.Printf("failed to close server: %v", err)
88		}
89	}()
90
91	log.Printf("WebDAV server listening on %v", addr)
92	if err := srv.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
93		log.Fatalf("failed to serve: %v", err)
94	}
95}