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/chapath.nim5
-rw-r--r--src/config/config.nim14
-rw-r--r--src/loader/gopher.nim125
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)