diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config/chapath.nim | 5 | ||||
-rw-r--r-- | src/config/config.nim | 14 | ||||
-rw-r--r-- | src/loader/gopher.nim | 125 |
3 files changed, 28 insertions, 116 deletions
diff --git a/src/config/chapath.nim b/src/config/chapath.nim index caaf9435..8f1ff539 100644 --- a/src/config/chapath.nim +++ b/src/config/chapath.nim @@ -9,6 +9,8 @@ import js/tojs import types/opt import utils/twtstr +const libexecPath {.strdefine.} = "${%CHA_BIN_DIR}/../libexec/chawan" + type ChaPath* = distinct string func `$`*(p: ChaPath): string = @@ -34,6 +36,7 @@ type ChaPathResult[T] = Result[T, ChaPathError] +proc unquote*(p: ChaPath): ChaPathResult[string] proc unquote(p: string, starti: var int, terminal: Option[char]): ChaPathResult[string] @@ -181,6 +184,8 @@ proc stateCurlyPerc(ctx: var UnquoteContext, c: char): ChaPathResult[void] = if c == '}': if ctx.identStr == "CHA_BIN_DIR": ctx.s &= getAppFileName().beforeLast('/') + elif ctx.identStr == "CHA_LIBEXEC_DIR": + ctx.s &= ?ChaPath(libexecPath).unquote() else: return err("Unknown internal variable " & ctx.identStr) ctx.identStr = "" diff --git a/src/config/config.nim b/src/config/config.nim index 1274e731..ae4a8f85 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -350,7 +350,14 @@ proc readUserStylesheet(dir, file: string): string = # of several individual configuration files known as mailcap files. proc getMailcap*(config: Config): tuple[mailcap: Mailcap, errs: seq[string]] = let configDir = getConfigDir() / "chawan" #TODO store this in config? - var mailcap: Mailcap + const gopherPath0 = ChaPath("${%CHA_LIBEXEC_DIR}/gopher2html -u %u") + let gopherPath = gopherPath0.unquote().get + var mailcap = @[MailcapEntry( + mt: "text", + subt: "gopher", + cmd: gopherPath, + flags: {HTMLOUTPUT} + )] var errs: seq[string] var found = false for p in config.external.mailcap: @@ -363,6 +370,11 @@ proc getMailcap*(config: Config): tuple[mailcap: Mailcap, errs: seq[string]] = errs.add(res.error) found = true if not found: + mailcap.insert(MailcapEntry( + mt: "*", + subt: "*", + cmd: "xdg-open '%s'" + ), 0) return (DefaultMailcap, errs) return (mailcap, errs) diff --git a/src/loader/gopher.nim b/src/loader/gopher.nim index fddc7f7c..5f27b213 100644 --- a/src/loader/gopher.nim +++ b/src/loader/gopher.nim @@ -1,5 +1,4 @@ import options -import strutils import bindings/curl import loader/connecterror @@ -8,7 +7,6 @@ import loader/curlwrap import loader/headers import loader/loaderhandle import loader/request -import types/opt import types/url import utils/twtstr @@ -30,9 +28,6 @@ type GopherType = enum type GopherHandle = ref object of CurlHandle t: GopherType - buffer: string - ispre: bool - surl: string func gopherType(c: char): GopherType = return case c @@ -68,7 +63,9 @@ proc onStatusLine(op: GopherHandle): bool = if not op.handle.sendStatus(cast[int](status)): return false let headers = case op.t - of DIRECTORY, SEARCH, HTML: + of DIRECTORY, SEARCH: + newHeaders({"Content-Type": "text/gopher"}) + of HTML: newHeaders({"Content-Type": "text/html"}) of GIF: newHeaders({"Content-Type": "image/gif"}) @@ -80,35 +77,20 @@ proc onStatusLine(op: GopherHandle): bool = newHeaders() if not op.handle.sendHeaders(headers): return false - if op.t in {DIRECTORY, SEARCH}: - var heads = """ -<!DOCTYPE HTML> -<HTML> -<HEAD> -<BASE HREF="""" & $op.request.url & """"> -</HEAD> -<BODY> - """ - if op.t == DIRECTORY: - heads &= "<H1>Index of " & htmlEscape(op.surl) & "</H1>" - else: # search - heads &= "<H1>Search " & htmlEscape(op.surl) & "</H1>" - if not op.handle.sendData(heads): - return false return true -proc loadSearch(op: GopherHandle) = +proc loadSearch(op: GopherHandle, surl: string) = discard op.handle.sendResult(int(CURLE_OK)) discard op.handle.sendStatus(200) # ok discard op.handle.sendHeaders(newHeaders({"Content-Type": "text/html"})) - var heads = """ + let heads = """ <!DOCTYPE HTML> <HTML> <HEAD> <BASE HREF="""" & $op.request.url & """"> </HEAD> <BODY> -<H1>Search """ & htmlEscape(op.surl) & """</H1> +<H1>Search """ & htmlEscape(surl) & """</H1> <FORM> <INPUT TYPE=SEARCH NAME="NAME"> </FORM> @@ -117,75 +99,6 @@ proc loadSearch(op: GopherHandle) = """ discard op.handle.sendData(heads) -proc flushLine(op: GopherHandle, s: string, fromi, toi: int): bool = - if toi == fromi + 1 and s[fromi] == '.': - return true #TODO this is the file end. maybe return false? - if s.len == 0: - return true # invalid - var i = fromi - let tc = s[i] - let t = gopherType(tc) - inc i - let ni = i - while i < toi and s[i] != '\t': inc i - let name = s.substr(ni, i - 1) - inc i - let fi = i - while i < toi and s[i] != '\t': inc i - let file = s.substr(fi, i - 1) - inc i - let hi = i - while i < toi and s[i] != '\t': inc i - let host = s.substr(hi, i - 1) - inc i - let pi = i - while i < toi and s[i] notin {'\t', '\r', '\n'}: inc i - let port = s.substr(pi, i - 1) - var line: string - if t == INFO: - if not op.ispre: - op.ispre = true - line = "<PRE>" - line &= htmlEscape(name) & "\n" - else: - if op.ispre: - line = "</PRE>" - op.ispre = false - let ts = $t - var names = "" - if ts != "": - names &= '[' & ts & ']' - names &= htmlEscape(name) - var ourls: string - if not file.startsWith("URL:"): - let file = if file.len > 0 and file[0] == '/': - file - else: - '/' & file - let pefile = percentEncode(file, PathPercentEncodeSet) - let iurls = "gopher://" & host & ":" & port & "/" & tc & pefile - let url = newURL(iurls) - ourls = if url.isSome: $url.get else: "" - else: - ourls = file.substr("URL:".len) - line &= "<A HREF=\"" & htmlEscape(ourls) & "\">" & names & "</A><BR>\n" - return op.handle.sendData(line) - -proc onSendChunk(op: GopherHandle, previ: int): bool = - var i = previ - var lasti = 0 - while i < op.buffer.len: - if op.buffer[i] in {'\r', '\n'}: - if not op.flushLine(op.buffer, lasti, i): - return false - while i < op.buffer.high and op.buffer[i] in {'\r', '\n'}: - inc i - lasti = i - inc i - if lasti > 0: - op.buffer.delete(0 .. lasti) - return true - # From the documentation: size is always 1. proc curlWriteBody(p: cstring, size: csize_t, nmemb: csize_t, userdata: pointer): csize_t {.cdecl.} = @@ -194,25 +107,10 @@ proc curlWriteBody(p: cstring, size: csize_t, nmemb: csize_t, op.statusline = true if not op.onStatusLine(): return 0 - if nmemb > 0: - if op.t in {DIRECTORY, SEARCH}: - let i = op.buffer.len - op.buffer.setLen(op.buffer.len + int(nmemb)) - prepareMutation(op.buffer) - copyMem(addr op.buffer[i], p, nmemb) - if not op.onSendChunk(i): - return 0 - else: - if not op.handle.sendData(p, int(nmemb)): - return 0 + if not op.handle.sendData(p, int(nmemb)): + return 0 return nmemb -proc finish(op: CurlHandle) = - let op = cast[GopherHandle](op) - if op.ispre: - discard op.handle.sendData("</PRE>\n") - discard op.handle.sendData("</BODY>\n</HTML>\n") - proc loadGopher*(handle: LoaderHandle, curlm: CURLM, request: Request): CurlHandle = let curl = curl_easy_init() @@ -232,15 +130,12 @@ proc loadGopher*(handle: LoaderHandle, curlm: CURLM, let op = curl.newGopherHandle(request, handle, t) if t == SEARCH: if url.query.isNone: - op.surl = url.serialize() - op.loadSearch() + op.loadSearch(url.serialize()) + handle.close() return nil else: url.query = some(url.query.get.after('=')) let surl = url.serialize() - if t in {DIRECTORY, SEARCH}: - op.surl = surl - op.finish = finish curl.setopt(CURLOPT_URL, surl) curl.setopt(CURLOPT_WRITEDATA, op) curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody) |