about summary refs log tree commit diff stats
path: root/adapter/protocol/file.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-12-12 20:28:54 +0100
committerbptato <nincsnevem662@gmail.com>2023-12-12 20:49:30 +0100
commita6166c4dfc7b028761a884127cc78aeb0056ed8d (patch)
treee19cda9ba3e46fa4cf42aa383c3530d472282c10 /adapter/protocol/file.nim
parent1dcb9cf6567145a0d3705b95717ccbf37eac6140 (diff)
downloadchawan-a6166c4dfc7b028761a884127cc78aeb0056ed8d.tar.gz
Move out file: protocol handling to adapter/
Diffstat (limited to 'adapter/protocol/file.nim')
-rw-r--r--adapter/protocol/file.nim111
1 files changed, 111 insertions, 0 deletions
diff --git a/adapter/protocol/file.nim b/adapter/protocol/file.nim
new file mode 100644
index 00000000..f3ffa93e
--- /dev/null
+++ b/adapter/protocol/file.nim
@@ -0,0 +1,111 @@
+import std/algorithm
+import std/os
+import std/streams
+import std/times
+import std/envvars
+
+import loader/connecterror
+import loader/dirlist
+import utils/twtstr
+
+proc loadDir(path: string) =
+  var path = path
+  if path[^1] != '/': #TODO dos/windows
+    path &= '/'
+  var base = getEnv("QUERY_STRING")
+  if base[^1] != '/': #TODO dos/windows
+    base &= '/'
+  stdout.write("Content-Type: text/html\n\n")
+  stdout.write("""
+<HTML>
+<HEAD>
+<BASE HREF="""" & base & """">
+<TITLE>Directory list of """ & path & """</TITLE>
+</HEAD>
+<BODY>
+<H1>Directory list of """ & path & """</H1>
+<PRE>
+""")
+  var fs: seq[(PathComponent, string)]
+  for pc, file in walkDir(path, relative = true):
+    fs.add((pc, file))
+  fs.sort(cmp = proc(a, b: (PathComponent, string)): int = cmp(a[1], b[1]))
+  var items: seq[DirlistItem]
+  for (pc, file) in fs:
+    let fullpath = path / file
+    var info: FileInfo
+    try:
+      info = getFileInfo(fullpath, followSymlink = false)
+    except OSError:
+      continue
+    let modified = $info.lastWriteTime.local().format("MMM/dd/yyyy HH:MM")
+    case pc
+    of pcDir:
+      items.add(DirlistItem(
+        t: ITEM_DIR,
+        name: file,
+        modified: modified
+      ))
+    of pcFile:
+      items.add(DirlistItem(
+        t: ITEM_FILE,
+        name: file,
+        modified: modified,
+        nsize: int(info.size)
+      ))
+    of pcLinkToDir, pcLinkToFile:
+      var target = expandSymlink(fullpath)
+      if pc == pcLinkToDir:
+        target &= '/'
+      items.add(DirlistItem(
+        t: ITEM_LINK,
+        name: file,
+        modified: modified,
+        linkto: target
+      ))
+  stdout.write(makeDirlist(items))
+  stdout.write("\n</PRE>\n</BODY>\n</HTML>\n")
+
+proc loadSymlink(path: string) =
+  stdout.write("Content-Type: text/html\n\n")
+  let sl = expandSymlink(path)
+  stdout.write("""
+<HTML>
+<HEAD>
+<TITLE>Symlink view<TITLE>
+</HEAD>
+<BODY>
+Symbolic link to <A HREF="""" & sl & """">""" & sl & """</A></br>
+</BODY>
+</HTML>""")
+
+proc loadFile(istream: Stream) =
+  # No headers, we'll let the browser figure out the file type.
+  stdout.write("\n")
+  let outs = newFileStream(stdout)
+  while not istream.atEnd:
+    const bufferSize = 4096
+    var buffer {.noinit.}: array[bufferSize, char]
+    while true:
+      let n = readData(istream, addr buffer[0], bufferSize)
+      if n == 0:
+        break
+      outs.writeData(addr buffer[0], n)
+      if n < bufferSize:
+        break
+
+proc main() =
+  let path = percentDecode(getEnv("MAPPED_URI_PATH"))
+  let istream = newFileStream(path, fmRead)
+  if istream == nil:
+    if dirExists(path):
+      loadDir(path)
+    elif symlinkExists(path):
+      loadSymlink(path)
+    else:
+      let code = int(ERROR_FILE_NOT_FOUND)
+      stdout.write("Cha-Control: ConnectionError " & $code)
+  else:
+    loadFile(istream)
+
+main()