瀏覽代碼

Initial CGI script

Sergey Ponomarev 3 年之前
父節點
當前提交
250ecb9e44
共有 8 個文件被更改,包括 149 次插入1 次删除
  1. 58 1
      README.md
  2. 70 0
      cgi-bin/dav.sh
  3. 1 0
      cgi-bin/index.cgi
  4. 3 0
      dav-get.sh
  5. 7 0
      dav-lock.sh
  6. 3 0
      dav-propfind.sh
  7. 6 0
      dav-put.sh
  8. 1 0
      start-bb-httpd.sh

+ 58 - 1
README.md

@@ -1,2 +1,59 @@
 # cgi-webdav
-Essential WebDAV as a CGI script so it can be used with any Web Server even with BusyBox httpd
+Essential WebDAV server as a CGI program. You can mount a WebDAV share as a network disk then list files in a folder, add and remove files.
+
+The main purpose is to make the smallest possible WebDAV server implementation, so it can fit into old 4mb routers and to be used with BusyBox httpd or OpenWrt uhttpd.
+But if it's size will be very small it may be even included by default to OpenWrt.
+This opens an easy way to millions users to set up their own personal cloud by putting a flash drive or disk into their router.
+Interesting application may be for a very small embedded devices that even doesn't have an SSH server but just a plain Telnet, so it's not possible to upload files into it.
+
+With a simple UI like [webdav-js](https://github.com/dom111/webdav-js) the WebDAV share can be used just from a browser.
+This will be a similar to NextCloud but use much less resources.
+
+Currently, the WebDAV is always implemented as a web server module e.g. Lighttpd [mod_webdav](https://redmine.lighttpd.net/projects/1/wiki/Docs_ModWebDAV) or Apache Httpd [mod_dav](https://httpd.apache.org/docs/current/mod/mod_dav.html).
+The Lighttpd is used in GL.Inet and Turris routers which are powerful enough and using a custom OpenWrt based firmware.
+I created an instruction [WebDAV with Lighttpd on OpenWRT](https://gist.github.com/stokito/5db2aa2cc184717d45600889d8115100) and it works perfectly.
+But a vanilla OpenWrt uses own uhttpd and there is no such WebDAV module for it.
+The BusyBox httpd is not modular at all.
+I believe that having just a simple CGI program will be preferable that trying to add the WebDAV functionality into them.
+
+There is already exists the [webdavcgi](https://github.com/DanRohde/webdavcgi) but it's Perl based and too heavy for embedded devices. It looks like it was developed before even Apache mod_dav was developed.
+
+One another interesting project is [webdav-server-rs](https://github.com/miquels/webdav-server-rs) which is a full WebDAV server implemented in Rust.
+Still, our main target is embedded devices with MIPS processors that are still not supported by Rust compiler.
+
+
+## Design goals
+### Only few clients supported
+Let's say that the main client that should be supported is Windows Net Share and macOS Finder.
+They are proprietary, even without any ability to report a bug and may behave not by specification.
+But most users will use them. That means that instead reading of spec we have to sniff traffic and use them as a reference.
+The webdavfs in Linux is probably the second client by priority.
+GNOME and KDE uses their own libraries to access WebDAV so they may have their own bugs.
+I sniffed traffic that sends GNOME when opening a WebDAV and it's just terrible. There is opened bugs not fixed for many years. 
+
+### Don't to use any dependencies like XML parsers.
+That means that things like a custom namespaces may cause it to not work. Ideally just not to parse anything.
+For example if in the `PROPFIND` was requested only few fields the webdav.cgi anyway will ignore it and return a full set of fields.
+
+### Don't be a fully compliant to a specification.
+For example locks may be not implemented. The [Litmus test](http://www.webdav.org/neon/litmus/) will be failed, but anyway it covers 80% of needs.
+
+
+## See also
+* [Awesome WebDAV](https://github.com/fstanis/awesome-webdav)
+
+
+## Implementation
+Discussion for Lighttpd https://redmine.lighttpd.net/boards/2/topics/10872
+
+Here is a PoC project with a webdav server in Go that works without XML parsing and locks and was tested with Windows MiniRedirector 
+https://github.com/stokito/go-webdav-server/tree/noxml
+It has a mocked pseudo locks because Windows sends them but generally it works!
+So now I need to make the plain C implementation.
+
+There should be additional problems: the uhttpd/BusyBox httpd won't forward WebDAV methods (e.g. PROPFIND) into a CGI script. So we need to patch them.
+The uclient, OpenWrt's wget clone, also can't be used to send the PROPFIND requests and needs for a patch too.
+But basically if just add GET/HEAD request handler then we can turn it into a dedicated server.
+
+It may be based on the BusyBox directory listing CGi script
+https://git.busybox.net/busybox/tree/networking/httpd_indexcgi.c

+ 70 - 0
cgi-bin/dav.sh

@@ -0,0 +1,70 @@
+#!/bin/sh
+path="./files$REQUEST_URI"
+if [ "$REQUEST_METHOD" = "GET" ]; then
+  if [ ! -e "$path" ]; then
+    printf "Status: 404\r\n"
+    printf "\r\n"
+  else
+    printf "Content-Type: text/html\r\n"
+    printf "\r\n"
+    cat "$path"
+  fi
+elif [ "$REQUEST_METHOD" = "PUT" ]; then
+  CONTENT=$(cat -)
+  printf "%s" "$CONTENT" > "$path"
+  printf "Status: 204\r\n"
+  printf "\r\n"
+elif [ "$REQUEST_METHOD" = "DELETE" ]; then
+  rm -rf "$path"
+  printf "Status: 204\r\n"
+  printf "\r\n"
+elif [ "$REQUEST_METHOD" = "UNLOCK" ]; then
+  printf "Status: 204\r\n"
+  printf "\r\n"
+elif [ "$REQUEST_METHOD" = "LOCK" ]; then
+  printf "Content-Type: text/xml\r\n"
+  printf "\r\n"
+  printf '<?xml version="1.0" encoding="utf-8" ?>
+<d:prop xmlns:d="DAV:">
+  <d:lockdiscovery>
+    <d:activelock>
+      <d:locktype><d:write/></d:locktype>
+      <d:lockscope><d:exclusive/></d:lockscope>
+      <d:depth>Infinity</d:depth>
+      <d:owner>
+        <d:href>https://www.contoso.com/~user/contact.htm</d:href>
+      </d:owner>
+      <d:timeout>Second-345600</d:timeout>
+      <d:locktoken>
+        <d:href>opaquelocktoken:e71d4fae-5dec-22df-fea5-00a0c93bd5eb1</d:href>
+      </d:locktoken>
+    </d:activelock>
+  </d:lockdiscovery>
+</d:prop>'
+elif [ "$REQUEST_METHOD" = "PROPFIND" ]; then
+  printf "Status: 207\r\n"
+  printf "Content-Type: text/xml\r\n"
+  printf "\r\n"
+
+  printf '%s' '<?xml version="1.0"?>
+        <a:multistatus xmlns:a="DAV:">'
+  LC_ALL=C ls -lA -- "$path" |
+  tail -n +2 |
+  while IFS= read -r line; do
+    IFS=' ' read -r f_mode f_links f_owner f_group f_size f_month f_day f_time f_name <<< "$line"
+    printf '<a:response>
+             <a:href>%s</a:href>
+             <a:propstat>
+              <a:status>HTTP/1.1 200 OK</a:status>
+               <a:prop>
+                <a:getcontenttype>text/plain</a:getcontenttype>
+                <a:getcontentlength>%d</a:getcontentlength>
+               </a:prop>
+             </a:propstat>
+            </a:response>
+           ' "$f_name" "$f_size"
+  done
+  printf '%s' '</a:multistatus>'
+else
+  printf "Status: 405\r\n"
+fi

+ 1 - 0
cgi-bin/index.cgi

@@ -0,0 +1 @@
+./dav.sh

+ 3 - 0
dav-get.sh

@@ -0,0 +1,3 @@
+export REQUEST_URI="/test.txt"
+export REQUEST_METHOD="GET"
+./cgi-bin/dav.sh

+ 7 - 0
dav-lock.sh

@@ -0,0 +1,7 @@
+export REQUEST_URI="/test.txt"
+export REQUEST_METHOD="LOCK"
+./cgi-bin/dav.sh
+
+echo
+export REQUEST_METHOD="UNLOCK"
+./cgi-bin/dav.sh

+ 3 - 0
dav-propfind.sh

@@ -0,0 +1,3 @@
+export REQUEST_URI="/"
+export REQUEST_METHOD="PROPFIND"
+./cgi-bin/dav.sh

+ 6 - 0
dav-put.sh

@@ -0,0 +1,6 @@
+export REQUEST_URI="/test.txt"
+export REQUEST_METHOD="PUT"
+export CONTENT_TYPE="text/plain"
+CONTENT="line1
+line2"
+printf "%s" "$CONTENT" | ./cgi-bin/dav.sh

+ 1 - 0
start-bb-httpd.sh

@@ -0,0 +1 @@
+busybox httpd -p 8080 -f -vv