about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config/config.nim4
-rw-r--r--src/loader/cgi.nim6
-rw-r--r--src/loader/file.nim6
-rw-r--r--src/loader/loader.nim24
-rw-r--r--src/local/client.nim3
-rw-r--r--src/server/forkserver.nim6
-rw-r--r--src/types/urimethodmap.nim4
-rw-r--r--src/types/url.nim43
8 files changed, 64 insertions, 32 deletions
diff --git a/src/config/config.nim b/src/config/config.nim
index fb0218ce..0df6285a 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -91,6 +91,7 @@ type
     mime_types* {.jsgetset.}: seq[string]
     cgi_dir* {.jsgetset.}: seq[string]
     urimethodmap* {.jsgetset.}: seq[string]
+    w3m_cgi_compat* {.jsgetset.}: bool
 
   InputConfig = object
     vi_numeric_prefix* {.jsgetset.}: bool
@@ -232,7 +233,8 @@ proc getBufferConfig*(config: Config, location: URL, cookiejar: CookieJar,
       cookiejar: cookiejar,
       proxy: proxy,
       cgiDir: config.external.cgi_dir,
-      urimethodmap: urimethodmap
+      urimethodmap: urimethodmap,
+      w3mCGICompat: config.external.w3m_cgi_compat
     )
   )
 
diff --git a/src/loader/cgi.nim b/src/loader/cgi.nim
index d94a2243..b0341a59 100644
--- a/src/loader/cgi.nim
+++ b/src/loader/cgi.nim
@@ -54,7 +54,11 @@ proc loadCGI*(handle: LoaderHandle, request: Request, cgiDir: seq[string]) =
   if cgiDir.len == 0:
     discard handle.sendResult(ERROR_NO_CGI_DIR)
     return
-  let path = percentDecode(request.url.pathname)
+  var path = percentDecode(request.url.pathname)
+  if path.startsWith("/cgi-bin/"):
+    path.delete(0 .. "/cgi-bin/".high)
+  elif path.startsWith("/$LIB/"):
+    path.delete(0 .. "/$LIB/".high)
   if path == "" or request.url.hostname != "":
     discard handle.sendResult(ERROR_INVALID_CGI_PATH)
     return
diff --git a/src/loader/file.nim b/src/loader/file.nim
index 5d552e40..cdb7afc2 100644
--- a/src/loader/file.nim
+++ b/src/loader/file.nim
@@ -108,11 +108,7 @@ proc loadFile(handle: LoaderHandle, istream: Stream) =
       if n < bufferSize:
         break
 
-proc loadFilePath*(handle: LoaderHandle, url: URL) =
-  when defined(windows) or defined(OS2) or defined(DOS):
-    let path = url.path.serialize_unicode_dos()
-  else:
-    let path = url.path.serialize_unicode()
+proc loadFilePath*(handle: LoaderHandle, url: URL, path: string) =
   let istream = newFileStream(path, fmRead)
   if istream == nil:
     if dirExists(path):
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index a148b77b..1f313a64 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -103,6 +103,7 @@ type
     acceptProxy*: bool
     cgiDir*: seq[string]
     uriMethodMap*: URIMethodMap
+    w3mCGICompat*: bool
 
   FetchPromise* = Promise[JSResult[Response]]
 
@@ -114,6 +115,16 @@ proc addFd(ctx: LoaderContext, fd: int, flags: int) =
 
 const MaxRewrites = 2 # should be enough? TODO find out what w3m thinks
 
+func canRewriteForCGICompat(ctx: LoaderContext, path: string): bool =
+  if not ctx.config.w3mCGICompat:
+    return false
+  if path.startsWith("/cgi-bin/") or path.startsWith("/$LIB/"):
+    return true
+  for dir in ctx.config.cgiDir:
+    if path.startsWith(dir):
+      return true
+  return false
+
 proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) =
   var redo = true
   var tries = 0
@@ -121,7 +132,15 @@ proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) =
     redo = false
     case request.url.scheme
     of "file":
-      handle.loadFilePath(request.url)
+      let path = request.url.path.serialize_unicode()
+      if ctx.canRewriteForCGICompat(path):
+        let newURL = newURL("cgi-bin:" & path & request.url.search)
+        if newURL.isSome:
+          request.url = newURL.get
+          inc tries
+          redo = true
+          continue
+      handle.loadFilePath(request.url, path)
       handle.close()
     of "http", "https":
       let handleData = handle.loadHttp(ctx.curlm, request)
@@ -280,6 +299,9 @@ proc initLoaderContext(fd: cint, config: LoaderConfig): LoaderContext =
     discard sig
     gctx.exitLoader()
   ctx.addFd(int(ctx.ssock.sock.getFd()), CURL_WAIT_POLLIN)
+  for dir in ctx.config.cgiDir.mitems:
+    if dir.len > 0 and dir[^1] != '/':
+      dir &= '/'
   return ctx
 
 proc runFileLoader*(fd: cint, config: LoaderConfig) =
diff --git a/src/local/client.nim b/src/local/client.nim
index 8351c4b1..937b4a8b 100644
--- a/src/local/client.nim
+++ b/src/local/client.nim
@@ -631,7 +631,8 @@ proc newClient*(config: Config, forkserver: ForkServer, mainproc: Pid): Client =
       defaultHeaders = config.getDefaultHeaders(),
       proxy = config.getProxy(),
       urimethodmap = config.getURIMethodMap(),
-      acceptProxy = true
+      acceptProxy = true,
+      w3mCGICompat = config.external.w3m_cgi_compat
     ),
     jsrt: jsrt,
     jsctx: jsctx,
diff --git a/src/server/forkserver.nim b/src/server/forkserver.nim
index 5f89d6ca..80cc0338 100644
--- a/src/server/forkserver.nim
+++ b/src/server/forkserver.nim
@@ -36,14 +36,16 @@ type
     children: seq[(Pid, Pid)]
 
 proc newFileLoader*(forkserver: ForkServer, defaultHeaders: Headers,
-    proxy: URL, urimethodmap: URIMethodMap, acceptProxy: bool): FileLoader =
+    proxy: URL, urimethodmap: URIMethodMap,
+    acceptProxy, w3mCGICompat: bool): FileLoader =
   forkserver.ostream.swrite(FORK_LOADER)
   let config = LoaderConfig(
     defaultHeaders: defaultHeaders,
     filter: newURLFilter(default = true),
     proxy: proxy,
     acceptProxy: acceptProxy,
-    urimethodmap: urimethodmap
+    urimethodmap: urimethodmap,
+    w3mCGICompat: w3mCGICompat
   )
   forkserver.ostream.swrite(config)
   forkserver.ostream.flush()
diff --git a/src/types/urimethodmap.nim b/src/types/urimethodmap.nim
index 6d57230b..f49162dd 100644
--- a/src/types/urimethodmap.nim
+++ b/src/types/urimethodmap.nim
@@ -61,8 +61,12 @@ proc parseURIMethodMap*(this: var URIMethodMap, s: string) =
     while i < line.len and line[i] in AsciiWhitespace:
       inc i
     var v = line.until(AsciiWhitespace, i)
+    # Basic w3m compatibility.
+    # If needed, w3m-cgi-compat covers more cases.
     if v.startsWith("file:/cgi-bin/"):
       v = "cgi-bin:" & v.substr("file:/cgi-bin/".len)
+    elif v.startsWith("file:///cgi-bin/"):
+      v = "cgi-bin:" & v.substr("file:///cgi-bin/".len)
     elif v.startsWith("/cgi-bin/"):
       v = "cgi-bin:" & v.substr("/cgi-bin/".len)
     this[k] = v
diff --git a/src/types/url.nim b/src/types/url.nim
index ab8c9e8b..562ec31b 100644
--- a/src/types/url.nim
+++ b/src/types/url.nim
@@ -351,8 +351,6 @@ proc basicParseURL*(input: string, base = none(URL), url: URL = URL(),
     i + 2 <= endi and input[i] in AsciiAlpha and input[i + 1] in {':', '|'}
   template is_normalized_windows_drive_letter(s: string): bool =
     s.len == 2 and s[0] in AsciiAlpha and s[1] == ':'
-  template is_windows_drive_letter(s: string): bool =
-    s.len == 2 and s[0] in AsciiAlpha and (s[1] == ':' or s[1] == '|')
   template is_double_dot_path_segment(s: string): bool =
     s == ".." or s.equalsIgnoreCase(".%2e") or s.equalsIgnoreCase("%2e.") or
       s.equalsIgnoreCase("%2e%2e")
@@ -779,26 +777,29 @@ func serialize*(path: URLPath): string {.inline.} =
     result &= '/'
     result &= s
 
-func serialize_unicode*(path: URLPath): string {.inline.} =
-  if path.opaque:
-    return percentDecode(path.s)
-  for s in path.ss:
-    result &= '/'
-    result &= percentDecode(s)
-
-func serialize_unicode_dos*(path: URLPath): string {.inline.} =
-  if path.opaque:
-    return percentDecode(path.s)
-  var i = 0
-  if i < path.ss.len:
-    if path.ss[i].is_windows_drive_letter:
-      result &= path.ss[i]
+when defined(windows) or defined(OS2) or defined(DOS):
+  func serialize_unicode_dos(path: URLPath): string =
+    if path.opaque:
+      return percentDecode(path.s)
+    var i = 0
+    if i < path.ss.len:
+      if path.ss[i].is_windows_drive_letter:
+        result &= path.ss[i]
+        inc i
+    while i < path.ss.len:
+      let s = path.ss[i]
+      result &= '\\'
+      result &= percentDecode(s)
       inc i
-  while i < path.ss.len:
-    let s = path.ss[i]
-    result &= '\\'
-    result &= percentDecode(s)
-    inc i
+  func serialize_unicode*(path: URLPath): string =
+    return path.serialize_unicode_dos()
+else:
+  func serialize_unicode*(path: URLPath): string =
+    if path.opaque:
+      return percentDecode(path.s)
+    for s in path.ss:
+      result &= '/'
+      result &= percentDecode(s)
 
 func serialize*(url: URL, excludefragment = false, excludepassword = false):
     string =