diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config/config.nim | 50 | ||||
-rw-r--r-- | src/loader/connecterror.nim | 2 | ||||
-rw-r--r-- | src/loader/loader.nim | 109 | ||||
-rw-r--r-- | src/local/client.nim | 1 | ||||
-rw-r--r-- | src/local/container.nim | 6 | ||||
-rw-r--r-- | src/local/pager.nim | 10 | ||||
-rw-r--r-- | src/server/buffer.nim | 7 | ||||
-rw-r--r-- | src/server/forkserver.nim | 23 | ||||
-rw-r--r-- | src/types/urimethodmap.nim | 68 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 6 |
10 files changed, 196 insertions, 86 deletions
diff --git a/src/config/config.nim b/src/config/config.nim index da897133..fb0218ce 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -11,13 +11,15 @@ import js/error import js/javascript import js/regex import loader/headers +import loader/loader import types/cell import types/color import types/cookie +import types/opt import types/referer +import types/urimethodmap import types/url import utils/mimeguess -import types/opt import utils/twtstr import chakasu/charset @@ -88,6 +90,7 @@ type mailcap* {.jsgetset.}: seq[string] mime_types* {.jsgetset.}: seq[string] cgi_dir* {.jsgetset.}: seq[string] + urimethodmap* {.jsgetset.}: seq[string] InputConfig = object vi_numeric_prefix* {.jsgetset.}: bool @@ -132,15 +135,12 @@ type BufferConfig* = object userstyle*: string - filter*: URLFilter - cookiejar*: CookieJar - headers*: Headers referer_from*: bool referrerpolicy*: ReferrerPolicy scripting*: bool charsets*: seq[Charset] images*: bool - proxy*: URL + loaderConfig*: LoaderConfig mimeTypes*: MimeTypes cgiDir*: seq[string] @@ -197,12 +197,6 @@ proc bindLineKey(config: Config, key, action: string) {.jsfunc.} = proc hasprop(a: ptr ActionMap, s: string): bool {.jshasprop.} = return s in a[] -func getForkServerConfig*(config: Config): ForkServerConfig = - return ForkServerConfig( - tmpdir: config.external.tmpdir, - ambiguous_double: config.display.double_width_ambiguous - ) - func getProxy*(config: Config): URL = if config.network.proxy.isSome: let s = config.network.proxy.get @@ -218,25 +212,28 @@ func getDefaultHeaders*(config: Config): Headers = proc getBufferConfig*(config: Config, location: URL, cookiejar: CookieJar, headers: Headers, referer_from, scripting: bool, charsets: seq[Charset], - images: bool, userstyle: string, proxy: URL, mimeTypes: MimeTypes): - BufferConfig = + images: bool, userstyle: string, proxy: URL, mimeTypes: MimeTypes, + urimethodmap: URIMethodMap): BufferConfig = let filter = newURLFilter( scheme = some(location.scheme), allowschemes = @["data"], default = true ) - result = BufferConfig( + return BufferConfig( userstyle: userstyle, - filter: filter, - cookiejar: cookiejar, - headers: headers, referer_from: referer_from, scripting: scripting, charsets: charsets, images: images, - proxy: proxy, mimeTypes: mimeTypes, - cgiDir: config.external.cgi_dir + loaderConfig: LoaderConfig( + defaultHeaders: headers, + filter: filter, + cookiejar: cookiejar, + proxy: proxy, + cgiDir: config.external.cgi_dir, + urimethodmap: urimethodmap + ) ) proc getSiteConfig*(config: Config, jsctx: JSContext): seq[SiteConfig] = @@ -369,6 +366,21 @@ proc getMimeTypes*(config: Config): MimeTypes = return DefaultGuess return mimeTypes +proc getURIMethodMap*(config: Config): URIMethodMap = + let configDir = getConfigDir() / "chawan" #TODO store this in config? + var urimethodmap: URIMethodMap + for p in config.external.urimethodmap: + let f = openFileExpand(configDir, p) + if f != nil: + urimethodmap.parseURIMethodMap(f.readAll()) + return urimethodmap + +proc getForkServerConfig*(config: Config): ForkServerConfig = + return ForkServerConfig( + tmpdir: config.external.tmpdir, + ambiguous_double: config.display.double_width_ambiguous + ) + proc parseConfig(config: Config, dir: string, stream: Stream, name = "<input>", laxnames = false) proc parseConfig*(config: Config, dir: string, s: string, name = "<input>", diff --git a/src/loader/connecterror.nim b/src/loader/connecterror.nim index f10285d2..8f2f95d2 100644 --- a/src/loader/connecterror.nim +++ b/src/loader/connecterror.nim @@ -1,6 +1,8 @@ import bindings/curl type ConnectErrorCode* = enum + ERROR_TOO_MANY_REWRITES = (-14, "too many URI method map rewrites") + ERROR_INVALID_URI_METHOD_ENTRY = (-13, "invalid URI method entry") ERROR_CGI_FILE_NOT_FOUND = (-12, "CGI file not found") ERROR_INVALID_CGI_PATH = (-11, "invalid CGI path") ERROR_FAIL_SETUP_CGI = (-10, "failed to set up CGI script") diff --git a/src/loader/loader.nim b/src/loader/loader.nim index 809219fe..a148b77b 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -43,6 +43,7 @@ import loader/request import loader/response import types/cookie import types/referer +import types/urimethodmap import types/url import utils/mimeguess import utils/twtstr @@ -79,6 +80,7 @@ type RESUME ADDREF UNREF + SET_REFERRER_POLICY LoaderContext = ref object refcount: int @@ -89,17 +91,18 @@ type extra_fds: seq[curl_waitfd] handleList: seq[CurlHandle] handleMap: Table[int, LoaderHandle] + referrerpolicy: ReferrerPolicy LoaderConfig* = object defaultheaders*: Headers filter*: URLFilter cookiejar*: CookieJar - referrerpolicy*: ReferrerPolicy proxy*: URL # When set to false, requests with a proxy URL are overridden by the # loader proxy. acceptProxy*: bool cgiDir*: seq[string] + uriMethodMap*: URIMethodMap FetchPromise* = Promise[JSResult[Response]] @@ -109,34 +112,51 @@ proc addFd(ctx: LoaderContext, fd: int, flags: int) = events: cast[cshort](flags) )) +const MaxRewrites = 2 # should be enough? TODO find out what w3m thinks + proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) = - case request.url.scheme - of "file": - handle.loadFilePath(request.url) - handle.close() - of "http", "https": - let handleData = handle.loadHttp(ctx.curlm, request) - if handleData != nil: - ctx.handleList.add(handleData) - of "about": - handle.loadAbout(request) - handle.close() - of "data": - handle.loadData(request) - handle.close() - of "ftp", "ftps", "sftp": - let handleData = handle.loadFtp(ctx.curlm, request) - if handleData != nil: - ctx.handleList.add(handleData) - of "gopher", "gophers": - let handleData = handle.loadGopher(ctx.curlm, request) - if handleData != nil: - ctx.handleList.add(handleData) - of "cgi-bin": - handle.loadCGI(request, ctx.config.cgiDir) - handle.close() - else: - discard handle.sendResult(ERROR_UNKNOWN_SCHEME) + var redo = true + var tries = 0 + while redo and tries < MaxRewrites: + redo = false + case request.url.scheme + of "file": + handle.loadFilePath(request.url) + handle.close() + of "http", "https": + let handleData = handle.loadHttp(ctx.curlm, request) + if handleData != nil: + ctx.handleList.add(handleData) + of "about": + handle.loadAbout(request) + handle.close() + of "data": + handle.loadData(request) + handle.close() + of "ftp", "ftps", "sftp": + let handleData = handle.loadFtp(ctx.curlm, request) + if handleData != nil: + ctx.handleList.add(handleData) + of "gopher", "gophers": + let handleData = handle.loadGopher(ctx.curlm, request) + if handleData != nil: + ctx.handleList.add(handleData) + of "cgi-bin": + handle.loadCGI(request, ctx.config.cgiDir) + handle.close() + else: + case ctx.config.urimethodmap.findAndRewrite(request.url) + of URI_RESULT_SUCCESS: + inc tries + redo = true + of URI_RESULT_WRONG_URL: + discard handle.sendResult(ERROR_INVALID_URI_METHOD_ENTRY) + handle.close() + of URI_RESULT_NOT_FOUND: + discard handle.sendResult(ERROR_UNKNOWN_SCHEME) + handle.close() + if tries >= MaxRewrites: + discard handle.sendResult(ERROR_TOO_MANY_REWRITES) handle.close() proc onLoad(ctx: LoaderContext, stream: SocketStream) = @@ -155,8 +175,8 @@ proc onLoad(ctx: LoaderContext, stream: SocketStream) = let cookie = ctx.config.cookiejar.serialize(request.url) if cookie != "": request.headers["Cookie"] = cookie - if request.referer != nil and "Referer" notin request.headers.table: - let r = getReferer(request.referer, request.url, ctx.config.referrerpolicy) + if request.referer != nil and "Referer" notin request.headers: + let r = getReferer(request.referer, request.url, ctx.referrerpolicy) if r != "": request.headers["Referer"] = r if request.proxy == nil or not ctx.config.acceptProxy: @@ -185,15 +205,6 @@ proc acceptConnection(ctx: LoaderContext) = let handle = ctx.handleMap[fd] handle.addOutputStream(stream) stream.swrite(true) - of ADDREF: - inc ctx.refcount - of UNREF: - dec ctx.refcount - if ctx.refcount == 0: - ctx.alive = false - stream.close() - else: - assert ctx.refcount > 0 of SUSPEND: var fds: seq[int] stream.sread(fds) @@ -206,6 +217,18 @@ proc acceptConnection(ctx: LoaderContext) = for fd in fds: ctx.handleMap.withValue(fd, handlep): handlep[].resume() + of ADDREF: + inc ctx.refcount + of UNREF: + dec ctx.refcount + if ctx.refcount == 0: + ctx.alive = false + stream.close() + else: + assert ctx.refcount > 0 + of SET_REFERRER_POLICY: + stream.sread(ctx.referrerpolicy) + stream.close() except IOError: # End-of-file, broken pipe, or something else. For now we just # ignore it and pray nothing breaks. @@ -492,8 +515,16 @@ proc addref*(loader: FileLoader) = let stream = connectSocketStream(loader.process) if stream != nil: stream.swrite(ADDREF) + stream.close() proc unref*(loader: FileLoader) = let stream = connectSocketStream(loader.process) if stream != nil: stream.swrite(UNREF) + +proc setReferrerPolicy*(loader: FileLoader, referrerpolicy: ReferrerPolicy) = + let stream = connectSocketStream(loader.process) + if stream != nil: + stream.swrite(SET_REFERRER_POLICY) + stream.swrite(referrerpolicy) + stream.close() diff --git a/src/local/client.nim b/src/local/client.nim index 5a02d2a8..8351c4b1 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -630,6 +630,7 @@ proc newClient*(config: Config, forkserver: ForkServer, mainproc: Pid): Client = loader: forkserver.newFileLoader( defaultHeaders = config.getDefaultHeaders(), proxy = config.getProxy(), + urimethodmap = config.getURIMethodMap(), acceptProxy = true ), jsrt: jsrt, diff --git a/src/local/container.nim b/src/local/container.nim index ce049c08..d3d0b879 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -822,8 +822,10 @@ proc load(container: Container) = if res.code == 0: container.triggerEvent(SUCCESS) # accept cookies - if res.cookies.len > 0 and container.config.cookiejar != nil: - container.config.cookiejar.add(res.cookies) + let cookiejar = container.config.loaderConfig.cookiejar + if res.cookies.len > 0 and cookiejar != nil: + cookiejar.add(res.cookies) + # set referrer policy, if any if res.referrerpolicy.isSome and container.config.referer_from: container.config.referrerpolicy = res.referrerpolicy.get container.setLoadInfo("Connected to " & $container.source.location & ". Downloading...") diff --git a/src/local/pager.nim b/src/local/pager.nim index b07d300e..1333e32f 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -35,8 +35,9 @@ import types/buffersource import types/cell import types/color import types/cookie -import types/url import types/opt +import types/urimethodmap +import types/url import utils/twtstr import chakasu/charset @@ -82,6 +83,7 @@ type term*: Terminal tty: File unreg*: seq[(Pid, SocketStream)] + urimethodmap: URIMethodMap username: string jsDestructor(Pager) @@ -211,7 +213,8 @@ proc newPager*(config: Config, attrs: WindowAttributes, statusgrid: newFixedGrid(attrs.width), term: newTerminal(stdout, config, attrs), mimeTypes: config.getMimeTypes(), - mailcap: mailcap + mailcap: mailcap, + urimethodmap: config.getURIMethodMap() ) for err in errs: pager.alert("Error reading mailcap: " & err) @@ -607,6 +610,7 @@ proc applySiteconf(pager: Pager, url: var URL): BufferConfig = var userstyle = pager.config.css.stylesheet var proxy = pager.proxy let mimeTypes = pager.mimeTypes + let urimethodmap = pager.urimethodmap for sc in pager.siteconf: if sc.url.isSome and not sc.url.get.match($url): continue @@ -640,7 +644,7 @@ proc applySiteconf(pager: Pager, url: var URL): BufferConfig = if sc.proxy.isSome: proxy = sc.proxy.get return pager.config.getBufferConfig(url, cookiejar, headers, referer_from, - scripting, charsets, images, userstyle, proxy, mimeTypes) + scripting, charsets, images, userstyle, proxy, mimeTypes, urimethodmap) # Load request in a new buffer. proc gotoURL(pager: Pager, request: Request, prevurl = none(URL), diff --git a/src/server/buffer.nim b/src/server/buffer.nim index 22dd8a8a..1e97a4bb 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -37,6 +37,7 @@ import js/regex import js/timeout import layout/box import loader/connecterror +import loader/headers import loader/loader import render/renderdocument import render/rendertext @@ -741,8 +742,10 @@ proc connect*(buffer: Buffer): ConnectResult {.proxy.} = let cookie = newCookie(s, response.url) if cookie.isOk: cookies.add(cookie.get) - if "Referrer-Policy" in response.headers.table: - referrerpolicy = getReferrerPolicy(response.headers.table["Referrer-Policy"][0]) + if "Referrer-Policy" in response.headers: + referrerpolicy = getReferrerPolicy(response.headers["Referrer-Policy"]) + if referrerpolicy.isSome: + buffer.loader.setReferrerPolicy(referrerpolicy.get) buffer.connected = true let contentType = buffer.source.contentType.get("") buffer.ishtml = contentType == "text/html" diff --git a/src/server/forkserver.nim b/src/server/forkserver.nim index 8ece45e6..5f89d6ca 100644 --- a/src/server/forkserver.nim +++ b/src/server/forkserver.nim @@ -16,6 +16,7 @@ import loader/loader import server/buffer import types/buffersource import types/cookie +import types/urimethodmap import types/url import utils/twtstr @@ -35,16 +36,14 @@ type children: seq[(Pid, Pid)] proc newFileLoader*(forkserver: ForkServer, defaultHeaders: Headers, - filter = newURLFilter(default = true), cookiejar: CookieJar = nil, - proxy: URL = nil, acceptProxy = false): FileLoader = + proxy: URL, urimethodmap: URIMethodMap, acceptProxy: bool): FileLoader = forkserver.ostream.swrite(FORK_LOADER) - var defaultHeaders = defaultHeaders let config = LoaderConfig( defaultHeaders: defaultHeaders, - filter: filter, - cookiejar: cookiejar, + filter: newURLFilter(default = true), proxy: proxy, - acceptProxy: acceptProxy + acceptProxy: acceptProxy, + urimethodmap: urimethodmap ) forkserver.ostream.swrite(config) forkserver.ostream.flush() @@ -111,17 +110,7 @@ proc forkBuffer(ctx: var ForkServerContext): Pid = ctx.istream.sread(config) ctx.istream.sread(attrs) ctx.istream.sread(mainproc) - let loaderPid = ctx.forkLoader( - LoaderConfig( - defaultHeaders: config.headers, - filter: config.filter, - cookiejar: config.cookiejar, - referrerpolicy: config.referrerpolicy, - #TODO these should be in a separate config I think - proxy: config.proxy, - cgiDir: config.cgiDir - ) - ) + let loaderPid = ctx.forkLoader(config.loaderConfig) var pipefd: array[2, cint] if pipe(pipefd) == -1: raise newException(Defect, "Failed to open pipe.") diff --git a/src/types/urimethodmap.nim b/src/types/urimethodmap.nim new file mode 100644 index 00000000..6d57230b --- /dev/null +++ b/src/types/urimethodmap.nim @@ -0,0 +1,68 @@ +# w3m's URI method map format. + +import strutils +import tables + +import types/opt +import types/url +import utils/twtstr + +type URIMethodMap* = object + map: Table[string, string] + +func rewriteURL(pattern, surl: string): string = + result = "" + var was_perc = false + for c in pattern: + if was_perc: + if c == '%': + result &= '%' + elif c == 's': + result.percentEncode(surl, ComponentPercentEncodeSet) + else: + result &= '%' + result &= c + was_perc = false + elif c != '%': + result &= c + else: + was_perc = true + if was_perc: + result &= '%' + +proc `[]=`*(this: var URIMethodMap, k, v: string) = + this.map[k] = v + +type URIMethodMapResult* = enum + URI_RESULT_NOT_FOUND, URI_RESULT_SUCCESS, URI_RESULT_WRONG_URL + +proc findAndRewrite*(this: URIMethodMap, url: var URL): URIMethodMapResult = + let protocol = url.protocol + if protocol in this.map: + let surl = this.map[protocol].rewriteURL($url) + let x = newURL(surl) + if x.isNone: + return URI_RESULT_WRONG_URL + url = x.get + return URI_RESULT_SUCCESS + return URI_RESULT_NOT_FOUND + +proc parseURIMethodMap*(this: var URIMethodMap, s: string) = + for line in s.split('\n'): + if line.len == 0 or line[0] == '#': + continue # comments + var k = "" + var i = 0 + while i < line.len and line[i] != ':': + k &= line[i].toLowerAscii() + inc i + if i >= line.len: + continue # invalid + while i < line.len and line[i] in AsciiWhitespace: + inc i + var v = line.until(AsciiWhitespace, i) + if 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/utils/twtstr.nim b/src/utils/twtstr.nim index ca79fa76..d8ce9ae8 100644 --- a/src/utils/twtstr.nim +++ b/src/utils/twtstr.nim @@ -206,13 +206,11 @@ func skipBlanks*(buf: string, at: int): int = while result < buf.len and buf[result].isWhitespace(): inc result -func until*(s: string, c: set[char]): string = - var i = 0 - while i < s.len: +func until*(s: string, c: set[char], starti = 0): string = + for i in starti ..< s.len: if s[i] in c: break result.add(s[i]) - inc i func until*(s: string, c: char): string = s.until({c}) |