diff options
-rw-r--r-- | doc/config.md | 23 | ||||
-rw-r--r-- | doc/localcgi.md | 19 | ||||
-rw-r--r-- | res/config.toml | 1 | ||||
-rw-r--r-- | src/config/config.nim | 4 | ||||
-rw-r--r-- | src/loader/cgi.nim | 6 | ||||
-rw-r--r-- | src/loader/file.nim | 6 | ||||
-rw-r--r-- | src/loader/loader.nim | 24 | ||||
-rw-r--r-- | src/local/client.nim | 3 | ||||
-rw-r--r-- | src/server/forkserver.nim | 6 | ||||
-rw-r--r-- | src/types/urimethodmap.nim | 4 | ||||
-rw-r--r-- | src/types/url.nim | 43 |
11 files changed, 101 insertions, 38 deletions
diff --git a/doc/config.md b/doc/config.md index 7af5d5db..a82712c1 100644 --- a/doc/config.md +++ b/doc/config.md @@ -220,6 +220,21 @@ MANOFF --> </td> </tr> +<tr> +<td>w3m-cgi-compat</td> +<td>boolean</td> +<td>Enable local CGI compatibility with w3m. In short, it redirects +`file:///cgi-bin/*` and `file:///$LIB/cgi-bin/*` to `cgi-bin:*`. For further +details, see +<!-- MANOFF --> +[localcgi.md](localcgi.md). +<!-- MANON --> +<!-- MANON +**cha-localcgi**(5). +MANOFF --> +</td> +</tr> + </table> ## Input @@ -1123,10 +1138,10 @@ with a caret (^) sign or end with an unescaped dollar ($) sign. In other words, the following transformations occur: ``` -^abcd -> ^abcd -efgh$ -> efgh$ -^ijkl$ -> ^ijkl$ -mnop -> ^mnop$ +^abcd -> ^abcd (no change) +efgh$ -> efgh$ (no change) +^ijkl$ -> ^ijkl$ (no change) +mnop -> ^mnop$ (changed to exact match) ``` <!-- MANON diff --git a/doc/localcgi.md b/doc/localcgi.md index ba7d5232..b9b3e5f5 100644 --- a/doc/localcgi.md +++ b/doc/localcgi.md @@ -19,14 +19,23 @@ Further notes on processing CGI paths: * The URL must be opaque, so you must not add a double slash after the scheme. e.g. `cgi-bin://script-name` will NOT work, only `cgi-bin:script-name`. +* Paths beginning with `/cgi-bin/` or `/$LIB/` are stripped of this segment + automatically. So e.g. `file:///cgi-bin/script-name` becomes + `cgi-bin:script-name`. +* If `extern.w3m-cgi-compat` is true, file: URLs are converted to cgi-bin: URLs + if the path name starts with `/cgi-bin/`, `/$LIB/`, or the path of a local + CGI script. * Absolute paths are accepted as e.g. `cgi-bin:/path/to/cgi/dir/script-name`. Note however, that this only works if `/path/to/cgi/dir` has already been specified as a CGI directory in `external.cgi-dir`. Note that this is different from w3m's cgi-bin functionality, in that we use a custom scheme for local CGI instead of interpreting all requests to -a designated path as a CGI request. Also, for now Chawan has no equivalent -to the W3m-control headers (but this may change in the future). +a designated path as a CGI request. (This incompatibility is bridged over when +`external.cgi-dir` is true.) + +Also, for now Chawan has no equivalent to the W3m-control headers (but this +may change in the future). ## Environment variables @@ -109,3 +118,9 @@ script's executable bit is set, i.e. run `chmod +x /path/to/cgi/script`. This means that either `pipe` or `fork` failed. Something strange is going on with your system; we recommend exorcism. (Maybe you are running out of memory?) + +<!-- MANON +## See also + +**cha**(1) +MANOFF --> diff --git a/res/config.toml b/res/config.toml index 0b3536a8..a1678361 100644 --- a/res/config.toml +++ b/res/config.toml @@ -32,6 +32,7 @@ urimethodmap = [ ] tmpdir = "/tmp/cha" editor = "vi %s +%d" +w3m-cgi-compat = true [network] max-redirect = 10 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 = |