about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-04-25 20:31:46 +0200
committerbptato <nincsnevem662@gmail.com>2024-04-25 20:39:01 +0200
commita42f96ee6287c714ce90206009f20494995940db (patch)
treea7d76007e2360c84c0b309d8aa934c186851dbe6
parent91707a9b2ee3ceee6bdd13f5262dcc1dd675fbc7 (diff)
downloadchawan-a42f96ee6287c714ce90206009f20494995940db.tar.gz
data: replace std/base64 with atob
std's version is known to be broken on versions we still support, and it
makes no sense to use different decoders anyway.

(This does introduce a bit of a dependency hell, because js/base64
depends on js/javascript which tries to bring in the entire QuickJS
runtime.  So we move that out into twtstr, and manually convert a
Result[string, string] to DOMException in js/base64.)
-rw-r--r--Makefile4
-rw-r--r--adapter/protocol/data.nim10
-rw-r--r--src/js/base64.nim68
-rw-r--r--src/utils/twtstr.nim63
4 files changed, 75 insertions, 70 deletions
diff --git a/Makefile b/Makefile
index 6a58edd7..fd8ac6c2 100644
--- a/Makefile
+++ b/Makefile
@@ -142,7 +142,9 @@ $(OUTDIR_CGI_BIN)/about: adapter/protocol/about.nim res/chawan.html \
 	@mkdir -p "$(OUTDIR_CGI_BIN)"
 	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/about" -o:"$(OUTDIR_CGI_BIN)/about" adapter/protocol/about.nim
 
-$(OUTDIR_CGI_BIN)/data: adapter/protocol/data.nim src/utils/twtstr.nim
+$(OUTDIR_CGI_BIN)/data: adapter/protocol/data.nim src/utils/twtstr.nim \
+		src/types/opt.nim src/utils/map.nim src/utils/charcategory.nim \
+		src/loader/connecterror.nim
 	@mkdir -p "$(OUTDIR_CGI_BIN)"
 	$(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/data" -o:"$(OUTDIR_CGI_BIN)/data" adapter/protocol/data.nim
 
diff --git a/adapter/protocol/data.nim b/adapter/protocol/data.nim
index e6c7318a..72263780 100644
--- a/adapter/protocol/data.nim
+++ b/adapter/protocol/data.nim
@@ -2,10 +2,10 @@ when NimMajor >= 2:
   import std/envvars
 else:
   import std/os
-import std/base64
 import std/strutils
 
 import loader/connecterror
+import types/opt
 import utils/twtstr
 
 proc main() =
@@ -18,12 +18,12 @@ proc main() =
   let sd = ct.len + 1 # data start
   let body = percentDecode(str, sd)
   if ct.endsWith(";base64"):
-    try:
-      let d = base64.decode(body) # decode from ct end + 1
+    let d = atob0(body) # decode from ct end + 1
+    if d.isSome:
       ct.setLen(ct.len - ";base64".len) # remove base64 indicator
       stdout.write("Content-Type: " & ct & "\n\n")
-      stdout.write(d)
-    except ValueError:
+      stdout.write(d.get)
+    else:
       stdout.write("Cha-Control: ConnectionError " & iu  & " invalid data URL")
   else:
     stdout.write("Content-Type: " & ct & "\n\n")
diff --git a/src/js/base64.nim b/src/js/base64.nim
index 9d1d35e5..a1693aa3 100644
--- a/src/js/base64.nim
+++ b/src/js/base64.nim
@@ -8,71 +8,11 @@ import utils/twtstr
 # atob and btoa convert Latin-1 to base64 and vice versa. (And throw on
 # anything above latin-1.)
 
-func atob(c: char): uint8 {.inline.} =
-  # see RFC 4648 table
-  if c in AsciiUpperAlpha:
-    return uint8(c) - uint8('A')
-  if c in AsciiLowerAlpha:
-    return uint8(c) - uint8('a') + 26
-  if c in AsciiDigit:
-    return uint8(c) - uint8('0') + 52
-  if c == '+':
-    return 62
-  if c == '/':
-    return 63
-  return uint8.high
-
 proc atob*(data: string): DOMResult[NarrowString] =
-  var outs = newStringOfCap(data.len div 4 * 3)
-  var buf: array[4, uint8]
-  var i = 0
-  var j = 0
-  var pad = 0
-  while true:
-    i = data.skipBlanks(i)
-    if i >= data.len:
-      break
-    if data[i] == '=':
-      i = data.skipBlanks(i + 1)
-      inc pad
-      break
-    buf[j] = atob(data[i])
-    if buf[j] == uint8.high:
-      return errDOMException("Invalid character in encoded string",
-        "InvalidCharacterError")
-    if j == 3:
-      let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
-      let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2
-      let ob3 = (buf[2] shl 6) or buf[3]         # 2 bits of b2 | 6 bits of b3
-      outs &= char(ob1)
-      outs &= char(ob2)
-      outs &= char(ob3)
-      j = 0
-    else:
-      inc j
-    inc i
-  if i < data.len:
-    if i < data.len and data[i] == '=':
-      inc pad
-      inc i
-    i = data.skipBlanks(i)
-  if pad > 0 and j + pad != 4:
-    return errDOMException("Too much padding", "InvalidCharacterError")
-  if i < data.len:
-    return errDOMException("Invalid character after encoded string",
-      "InvalidCharacterError")
-  if j == 3:
-    let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
-    let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2
-    outs &= char(ob1)
-    outs &= char(ob2)
-  elif j == 2:
-    let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
-    outs &= char(ob1)
-  elif j != 0:
-    return errDOMException("Incorrect number of characters in encoded string",
-      "InvalidCharacterError")
-  return ok(NarrowString(outs))
+  let r = atob0(data)
+  if r.isNone:
+    return err(newDOMException(r.error, "InvalidCharacterError"))
+  return ok(NarrowString(r.get))
 
 const AMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 
diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim
index 66a211b9..6cab9ac7 100644
--- a/src/utils/twtstr.nim
+++ b/src/utils/twtstr.nim
@@ -711,3 +711,66 @@ proc getContentTypeAttr*(contentType, attrname: string): string =
     else:
       s &= c
   return s
+
+func atob(c: char): uint8 {.inline.} =
+  # see RFC 4648 table
+  if c in AsciiUpperAlpha:
+    return uint8(c) - uint8('A')
+  if c in AsciiLowerAlpha:
+    return uint8(c) - uint8('a') + 26
+  if c in AsciiDigit:
+    return uint8(c) - uint8('0') + 52
+  if c == '+':
+    return 62
+  if c == '/':
+    return 63
+  return uint8.high
+
+func atob0*(data: string): Result[string, string] =
+  var outs = newStringOfCap(data.len div 4 * 3)
+  var buf: array[4, uint8]
+  var i = 0
+  var j = 0
+  var pad = 0
+  while true:
+    i = data.skipBlanks(i)
+    if i >= data.len:
+      break
+    if data[i] == '=':
+      i = data.skipBlanks(i + 1)
+      inc pad
+      break
+    buf[j] = atob(data[i])
+    if buf[j] == uint8.high:
+      return err("Invalid character in encoded string")
+    if j == 3:
+      let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
+      let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2
+      let ob3 = (buf[2] shl 6) or buf[3]         # 2 bits of b2 | 6 bits of b3
+      outs &= char(ob1)
+      outs &= char(ob2)
+      outs &= char(ob3)
+      j = 0
+    else:
+      inc j
+    inc i
+  if i < data.len:
+    if i < data.len and data[i] == '=':
+      inc pad
+      inc i
+    i = data.skipBlanks(i)
+  if pad > 0 and j + pad != 4:
+    return err("Too much padding")
+  if i < data.len:
+    return err("Invalid character after encoded string")
+  if j == 3:
+    let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
+    let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2
+    outs &= char(ob1)
+    outs &= char(ob2)
+  elif j == 2:
+    let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1
+    outs &= char(ob1)
+  elif j != 0:
+    return err("Incorrect number of characters in encoded string")
+  return ok(outs)