about summary refs log tree commit diff stats
path: root/src/io
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-09-01 21:49:19 +0200
committerbptato <nincsnevem662@gmail.com>2023-09-01 21:49:19 +0200
commitb57be7b89d656d1eda13f464b41373c2535e106f (patch)
treeb01685e5eb364b105f70119d3a1f152846d6c0e1 /src/io
parent5be61f0ab87d4ca7877815b09a38f1fcffa4d7fa (diff)
downloadchawan-b57be7b89d656d1eda13f464b41373c2535e106f.tar.gz
loader: add data URLs
Diffstat (limited to 'src/io')
-rw-r--r--src/io/connecterror.nim1
-rw-r--r--src/io/data.nim45
-rw-r--r--src/io/loader.nim6
-rw-r--r--src/io/urlfilter.nim19
4 files changed, 66 insertions, 5 deletions
diff --git a/src/io/connecterror.nim b/src/io/connecterror.nim
index 563a4291..d2af5762 100644
--- a/src/io/connecterror.nim
+++ b/src/io/connecterror.nim
@@ -1,6 +1,7 @@
 import bindings/curl
 
 type ConnectErrorCode* = enum
+  ERROR_INVALID_DATA_URL = (-7, "invalid data URL")
   ERROR_ABOUT_PAGE_NOT_FOUND = (-6, "about page not found")
   ERROR_FILE_NOT_FOUND = (-5, "file not found")
   ERROR_SOURCE_NOT_FOUND = (-4, "clone source could not be found"),
diff --git a/src/io/data.nim b/src/io/data.nim
new file mode 100644
index 00000000..012cba05
--- /dev/null
+++ b/src/io/data.nim
@@ -0,0 +1,45 @@
+import base64
+import strutils
+import tables
+
+import io/connecterror
+import io/headers
+import io/loaderhandle
+import io/request
+import types/url
+
+proc loadData*(handle: LoaderHandle, request: Request) =
+  template t(body: untyped) =
+    if not body:
+      return
+  let str = $request.url
+  let si = "data:".len # start index
+  var ct = ""
+  eprint "load data", str
+  for i in si ..< str.len:
+    if str[i] == ',':
+      break
+    ct &= str[i]
+  let sd = si + ct.len + 1 # data start
+  if ct.endsWith(";base64"):
+    try:
+      let d = base64.decode(str[sd .. ^1]) # decode from ct end + 1
+      t handle.sendResult(0)
+      t handle.sendStatus(200)
+      ct.setLen(ct.len - ";base64".len) # remove base64 indicator
+      t handle.sendHeaders(newHeaders({
+        "Content-Type": ct
+      }.toTable()))
+      if d.len > 0:
+        t handle.sendData(d)
+    except ValueError:
+      discard handle.sendResult(ERROR_INVALID_DATA_URL)
+  else:
+    t handle.sendResult(0)
+    t handle.sendStatus(200)
+    t handle.sendHeaders(newHeaders({
+      "Content-Type": ct
+    }.toTable()))
+    if ct.len + 1 < str.len:
+      eprint "send data", str[sd .. ^1], sd, str.len - sd
+      t handle.sendData(addr str[sd], str.len - sd)
diff --git a/src/io/loader.nim b/src/io/loader.nim
index c497b1dd..45e19357 100644
--- a/src/io/loader.nim
+++ b/src/io/loader.nim
@@ -22,6 +22,7 @@ import tables
 import bindings/curl
 import io/about
 import io/connecterror
+import io/data
 import io/file
 import io/headers
 import io/http
@@ -109,6 +110,9 @@ proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) =
   of "about":
     handle.loadAbout(request)
     handle.close()
+  of "data":
+    handle.loadData(request)
+    handle.close()
   else:
     discard handle.sendResult(ERROR_UNKNOWN_SCHEME)
     handle.close()
@@ -116,7 +120,9 @@ proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) =
 proc onLoad(ctx: LoaderContext, stream: Stream) =
   var request: Request
   stream.sread(request)
+  eprint "FETCH ONLOAD", $request.url
   if not ctx.config.filter.match(request.url):
+    eprint "disallowed"
     stream.swrite(ERROR_DISALLOWED_URL)
     stream.close()
   else:
diff --git a/src/io/urlfilter.nim b/src/io/urlfilter.nim
index 9541a699..c0832b17 100644
--- a/src/io/urlfilter.nim
+++ b/src/io/urlfilter.nim
@@ -6,26 +6,35 @@ import types/url
 #TODO add denyhost/s for blocklists
 type URLFilter* = object
   scheme: Option[string]
+  allowschemes: seq[string]
   allowhost*: Option[string]
   allowhosts: seq[Regex]
   default: bool
 
-proc newURLFilter*(scheme = none(string), allowhost = none(string),
-                   allowhosts: seq[Regex] = @[], default = false): URLFilter =
+proc newURLFilter*(scheme = none(string), allowschemes: seq[string] = @[],
+    allowhost = none(string), allowhosts: seq[Regex] = @[],
+    default = false): URLFilter =
+  doAssert scheme.isSome or allowschemes.len == 0,
+    "allowschemes without scheme is not supported"
   return URLFilter(
     scheme: scheme,
+    allowschemes: allowschemes,
     allowhost: allowhost,
     allowhosts: allowhosts,
     default: default
   )
 
 # Filters as follows:
-# If scheme is given, only URLs with the same scheme are matched.
+# If scheme/s are given, only URLs with the same scheme are matched.
 # Then, allowhost and allowhosts are checked; if none of these match the host,
 # the function returns the value of `default'.
 proc match*(filter: URLFilter, url: URL): bool =
-  if filter.scheme.isSome and filter.scheme.get != url.scheme:
-    return false
+  block check_scheme:
+    if filter.scheme.isSome and filter.scheme.get != url.scheme:
+      for scheme in filter.allowschemes:
+        if scheme == url.scheme:
+          break check_scheme
+      return false
   let host = url.host
   if filter.allowhost.isSome and filter.allowhost.get == host:
     return true