Browse Source

Implemented saljut-proxy prototype

Started implementing proxy-server...

serving stuff works atm

got the proxy to werk more good

fixup

more improvements

finished refactoring
pull/1/head
Vincent Truchseß 9 months ago
parent
commit
5d2ade1afd
9 changed files with 191 additions and 17 deletions
  1. +4
    -1
      project.clj
  2. +22
    -2
      src/saljut/core.clj
  3. +13
    -0
      src/saljut/macros.clj
  4. +8
    -4
      src/saljut/sbot.clj
  5. +40
    -0
      src/saljut/server.clj
  6. +70
    -0
      src/saljut/util/codec.clj
  7. +1
    -1
      src/saljut/util/io.clj
  8. +5
    -9
      src/saljut/util/sbot.clj
  9. +28
    -0
      src/saljut/util/server.clj

+ 4
- 1
project.clj View File

@@ -5,7 +5,10 @@
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [
[org.clojure/clojure "1.10.0"]
[org.clojure/data.json "0.2.6"]]
[org.clojure/data.json "0.2.6"]
[ring/ring-core "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]
[commons-codec/commons-codec "1.13"]]
:main ^:skip-aot saljut.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})

+ 22
- 2
src/saljut/core.clj View File

@@ -1,7 +1,27 @@
(ns saljut.core
(:require [saljut.server :as srv]
[saljut.sbot :as sbt]
[saljut.util.codec :as co])
(:gen-class))

(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
(let [[cmd & args] args]
(case cmd
"start" (srv/start-server)
"publish" (let [[path domain] args]
(sbt/publish-dir path domain))
"convert" (let [[enc pkey] args]
(case enc
"b32" (println (co/convert-64-32-key pkey))
"b64" (println (co/convert-32-64-key pkey))))
(println "usage:
saljut <command> [arg [arg [...]]]

commands:
start - starts the saljut proxy
publish <path> <domain> - publishes the content of path under domain
convert <enc> <pubkey> - converts a pubkey to desired encoding.
enc can be 'b32' for base32 or 'b64' for
SSB's usual base64-encoding"))))


+ 13
- 0
src/saljut/macros.clj View File

@@ -0,0 +1,13 @@
(ns saljut.macros
(:gen-class))

(defmacro case-re [arg & branches]
"Behaves similar to the case-macro but matches arg against regular-expressions
instead of values"
(let [go (fn go [[re body & tail]]
(if (or (and re body)
(and re (not (empty? tail))))
`(if (re-matches ~re ~arg)
~body
~(go tail))))]
(go branches)))

+ 8
- 4
src/saljut/sbot.clj View File

@@ -1,6 +1,6 @@
(ns saljut.sbot
(:require [clojure.java.shell :as sh])
(:require [saljut.io :as sio])
(:require [saljut.util.io :as sio])
(:require [saljut.util.sbot :as ut])
(:require [clojure.java.io :as io])
(:gen-class))
@@ -8,11 +8,11 @@
(defn get-latest-msg [pubkey domain]
"Get the most recent SSB-message of type \"webroot\" from the identity of
pubkey referring to domain's webroot"
(first (max-key :sequence
(last
(filter #(= domain (:domain %))
(map ut/parse-msg
(filter ut/validate-msg?
(filter ut/webroot-msg? (ut/get-user-stream pubkey))))))))
(filter ut/webroot-msg? (ut/get-user-stream pubkey)))))))

(defn publish-manifest [domain manifest]
"Publishes a SSB-message of type \"webroot\" referring to domain and
@@ -33,6 +33,10 @@
"publishes a directory in SSB under domain"
(publish-manifest domain (upload-dir dir)))

(defn download-file [blob-id target]
"downloads a file referenced by blob-id from ssb and saves it to target"
(sio/spit-bytes target (ut/get-blob blob-id)))

(defn download-dir [manifest target]
"Downloads all files in manifest into target"
(let [n (count manifest)]
@@ -41,5 +45,5 @@
parent (.getParent file)]
(if (not (.exists (io/file parent)))
(.mkdirs (io/file parent)))
(ut/download-file v (io/file target k))
(download-file v (io/file target k))
(println (str "(" i "/" n ") " v " => " k))))))

+ 40
- 0
src/saljut/server.clj View File

@@ -0,0 +1,40 @@
(ns saljut.server
(:require [ring.adapter.jetty :as jt]
[clojure.string :as st]
[saljut.util.server :as us]
[saljut.macros :as m])
(:gen-class))

(defn handler [request]
"The base-handler. Takes care of requests to \".ssb\" domains."
(let [hostname (get-in request [:headers "host"])]
(m/case-re hostname
;; Handle domain.key.b32.ssb hostnames
#"^[a-zA-Z0-9]+\.[a-z0-9]+-[a-zA-Z0-9]+\.b32\.ssb$"
(let [[domain key32] (st/split hostname #"\.")
path (if (= "/" (:uri request))
"/index.html"
(:uri request))
file (us/serve-file domain key32 path)]
{:status 200
:headers {"ContentType" (m/case-re path
#".*\.html$" "text/html"
#".*\.png$" "image/png"
#".*\.css" "text/css"
#".*\.xml" "application/xml"
#".*" "")
"Connection" "close"}
:body (new java.io.FileInputStream file)})
;; Handle domain.alias.ssb hostnames
#"^([a-zA-Z0-9]+\.){2}ssb$"
{:status 200
:headers {"ContentType" "text/html"}
:body "Going to perform a key-lookup"}
#".*"
{:status 500
:headers {"ContentType" "text/html"}
:body "<h1>500 - internal server error</h1>"})))

(defn start-server []
"Starts the Saljut-proxy on port 9876"
(jt/run-jetty handler {:port 9876}))

+ 70
- 0
src/saljut/util/codec.clj View File

@@ -0,0 +1,70 @@
(ns saljut.util.codec
(:require [clojure.string :as st])
(:import (java.util Base64))
(:import (org.apache.commons.codec.binary Base32))
(:gen-class))

(def b64encoder (Base64/getEncoder))

(def b64decoder (Base64/getDecoder))

(defn encode64 [input]
"Takes a byte-array as input and returns it as base64-string"
(.encodeToString b64encoder input))

(defn decode64 [input]
"Takes a base64-string as input and returns a byte-array."
(.decode b64decoder input))

(def b32codec (new Base32))

(defn encode32 [input]
"Takes a byte-array as input and returns a base32-string"
(.encodeAsString b32codec input))

(defn decode32 [input]
"Takes a base32-string as input and returns a byte-array."
(.decode b32codec input))

(defn decode64-key [pkey]
"Takes the base64-representation of a SSB-Pubkey and returns a map with
:key => byte-array containing the decoded pubkey
:cipher => string, denoting the used cipher"
(if (re-matches #"@[^.]+=*\.[^.]+" pkey)
(let [[base-id cipher] (st/split pkey #"\.")
key64 (st/replace base-id #"^@" "")]
{:key (decode64 key64) :cipher cipher})))

(defn encode64-key [pkey]
"Takes a map with
:key => byte-array containing a SSB-pubkey
:cipher => string denoting the key's cipher
and returns the base64-string-representation of a SSB-pubkey."
(str "@" (encode64 (:key pkey)) "." (:cipher pkey)))

(defn decode32-key [pkey]
"Takes a base32 string-representation of a SSB-pubkey and returns a map with
:key => byte-array containing the decoded pubkey
:cioher => string denoting the used cipher"
(if (re-matches #"^[A-Za-z0-9]+-[a-zA-Z0-9]+$" pkey)
(let [[b32-string cipher] (st/split pkey #"-")]
{:key (decode32 b32-string) :cipher cipher})))

(defn encode32-key [pkey]
"Takes a map with
:key => byte-array containing a SSB-pubkey
:cipher => string denoting the key's cipher
and returns the base32-string-representation of a SSB-pubkey."
(let [b32-with-padding (encode32 (:key pkey))
b32-string (st/lower-case (st/replace b32-with-padding #"=*$" ""))]
(str b32-string "-" (:cipher pkey))))

(defn convert-64-32-key [pkey]
"Converts a base64 string-representation of a SSB-pubkey into a
base32 string-representation."
(encode32-key (decode64-key pkey)))

(defn convert-32-64-key [pkey]
"Converts a base32 string-representation of a SSB-pubkey into a
base64 string-representation."
(encode64-key (decode32-key pkey)))

src/saljut/io.clj → src/saljut/util/io.clj View File

@@ -1,4 +1,4 @@
(ns saljut.io
(ns saljut.util.io
(:require [clojure.string :as st])
(:require [clojure.java.io :as io])
(:gen-class))

+ 5
- 9
src/saljut/util/sbot.clj View File

@@ -2,7 +2,7 @@
(:require [clojure.java.shell :as sh])
(:require [clojure.string :as st])
(:require [clojure.data.json :as json])
(:require [saljut.io :as sio])
(:require [saljut.util.io :as sio])
(:gen-class))

(defn add-blob [content]
@@ -65,15 +65,11 @@
"Returns a vector of strings as commandline-arguments representing manifest's
data to be applied to a shell-call to ssb-server. manifest is a map same as
:manifest in parse-msg's output."
(concat (map (fn [[i [k v]]]
(vector (str "--manifest." i ".path") k
(str "--manifest." i ".id") v))
(zipmap (range) manifest))))
(apply concat (map (fn [i [k v]]
(vector (str "--manifest." i ".path") k
(str "--manifest." i ".id") v))
(range) manifest)))

(defn upload-file [file]
"Uploads file as SSB-blob. Returns the blob-id as string"
(add-blob (sio/slurp-bytes file)))

(defn download-file [blob-id target]
"downloads a file referenced by blob-id from ssb and saves it to target"
(sio/spit-bytes target (get-blob blob-id)))

+ 28
- 0
src/saljut/util/server.clj View File

@@ -0,0 +1,28 @@
(ns saljut.util.server
(:require [clojure.java.io :as io]
[saljut.sbot :as sbt]
[saljut.util.codec :as co]
[clojure.string :as st])
(:gen-class))

(defn serve-file [domain key32 path]
"Ensures the most resent version of \"domain.key32.b32.ssb\" is present in the
cache-dir. Returns a file-object at path from the webroot"
(let [site-dir (io/file "/tmp/saljut" key32 domain)
content-dir (io/file site-dir "content")
metafile (io/file site-dir "meta.edn")
metadata (if (.exists metafile)
(read-string (slurp metafile))
{:timestamp 0,
:sequence 0,
:manifest {}})
timestamp (quot (System/currentTimeMillis) 1000)]
(if (< 500 (- timestamp (:timestamp metadata)))
(if-let [msg (sbt/get-latest-msg (co/convert-32-64-key key32) domain)]
(do (if (< (:sequence metadata) (:sequence msg))
(doseq [[k v] (filter (fn [[k v]] (not (= (get (:manifest metadata) k) v)))
(:manifest msg))]
(println (str "Downloading " k))
(sbt/download-file v (io/file content-dir k))))
(spit metafile (str (assoc msg :timestamp timestamp))))))
(io/file content-dir (st/replace path #"^/" ""))))

Loading…
Cancel
Save