commit 92dc248455a438dae1a3d917fcfbf843a9590565 Author: Nihilazo Date: Wed Jan 27 11:02:15 2021 +0000 initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cbb4608 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +INSTALLDIR=/run/media/nico/KOBOeReader/.adds/gemini + +all: kobo-gemini update + +kobo-gemini: + CGO_ENABLED=1 GOARCH=arm CC=arm-kobo-linux-gnueabihf-gcc CXX=arm-kobo-linux-gnueabihf-g++ go build + +update: kobo-gemini run.sh nickel.sh + cp kobo-gemini run.sh nickel.sh ${INSTALLDIR} + chmod a+x ${INSTALLDIR}/* diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b2bab01 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +CGO_ENABLED=1 GOARCH=arm CC=arm-kobo-linux-gnueabihf-gcc CXX=arm-kobo-linux-gnueabihf-g++ go build diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ae2c8c1 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module tildegit.org/nihilazo/kobo-gemini + +go 1.15 + +require ( + git.sr.ht/~adnano/go-gemini v0.1.13 + github.com/fogleman/gg v1.3.0 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/shermp/go-fbink-v2 v1.20.2 + github.com/shermp/go-fbink-v2/v2 v2.21.0 + github.com/shermp/go-kobo-input v0.0.0-20180928074949-be0734a2dcc6 + golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9acafba --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +git.sr.ht/~adnano/go-gemini v0.1.13 h1:vzKkkVrOzMpfJ1AAeE/PChg0Rw5Zf+9HrnwsgVxXUT4= +git.sr.ht/~adnano/go-gemini v0.1.13/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/shermp/go-fbink-v2 v1.20.2 h1:EtRKDZwrc8fkNGDZppsYB2nxcMosYU7hqYLovMP78/4= +github.com/shermp/go-fbink-v2 v1.20.2/go.mod h1:88bOAwruwze/4JB/KW8uoyPtWm5OPa1BZEraFMHJgpQ= +github.com/shermp/go-fbink-v2/v2 v2.21.0 h1:S2Q8B5zLGo7xTGILpqkVNZB94YwkehvTmECIYkxKH04= +github.com/shermp/go-fbink-v2/v2 v2.21.0/go.mod h1:QPZ+LtEGpr7FkAwfS1AuCHXUrTJWg57PKRl4SmnzKak= +github.com/shermp/go-kobo-input v0.0.0-20180928074949-be0734a2dcc6 h1:Tf6xr3SyROl/gYEbLVVRoASIuehPrjzq1y3P7krVqlo= +github.com/shermp/go-kobo-input v0.0.0-20180928074949-be0734a2dcc6/go.mod h1:DuK6sbQLR5V3MLqWS8y+wYaFj4KIyPeNMe5HrOgRH2w= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +tildegit.org/nihilazo/go-kobo-input v0.0.0-20210121201215-df5621661ac7 h1:7VnVnqyc+wmztFdkwujdIdjAEEmpAta2xGv/bvLZMXQ= diff --git a/kobo-gemini b/kobo-gemini new file mode 100755 index 0000000..1f14ba4 Binary files /dev/null and b/kobo-gemini differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..77fed84 --- /dev/null +++ b/main.go @@ -0,0 +1,149 @@ +package main + +import ( + //"github.com/fogleman/gg" + "github.com/shermp/go-fbink-v2/gofbink" + "github.com/shermp/go-kobo-input/koboin" + //"image" + "git.sr.ht/~adnano/go-gemini" + "git.sr.ht/~adnano/go-gemini/tofu" + "crypto/x509" +// "time" +// "bytes" + "errors" + "net/url" + "bufio" + "io/ioutil" + "fmt" + "time" +) + +var ( + hosts tofu.KnownHosts + hostsfile *tofu.HostWriter + scanner *bufio.Scanner +) + +var height int = 1080 +var width int = 1440 // TODO get from device instead of hardcoding + +func trustCertificate(hostname string, cert *x509.Certificate) error { // TODO actually check certs + /*host := tofu.NewHost(hostname, cert.Raw, cert.NotAfter) + knownHost, ok := hosts.Lookup(hostname) + if ok && time.Now().Before(knownHost.Expires) { + // Check fingerprint + if bytes.Equal(knownHost.Fingerprint, host.Fingerprint) { + return nil + } + return errors.New("error: fingerprint does not match!") + } + return errors.New("error: fingerprint does not match!")*/ + return nil +} + +func do(req *gemini.Request, via []*gemini.Request) (*gemini.Response, error) { + client := gemini.Client{ + TrustCertificate: trustCertificate, + } + resp, err := client.Do(req) + if err != nil { + return resp, err + } + + switch resp.Status.Class() { + case gemini.StatusClassInput: // TODO implement input + /* input, ok := getInput(resp.Meta, resp.Status == gemini.StatusSensitiveInput) + if !ok { + break + } + req.URL.ForceQuery = true + req.URL.RawQuery = gemini.QueryEscape(input) + return do(req, via) */ + + + case gemini.StatusClassRedirect: + via = append(via, req) + if len(via) > 5 { + return resp, errors.New("too many redirects") + } + + target, err := url.Parse(resp.Meta) + if err != nil { + return resp, err + } + target = req.URL.ResolveReference(target) + redirect := *req + redirect.URL = target + return do(&redirect, via) + } + + return resp, err +} + +func main() { + // Framebuffer setup + fbinkOpts := gofbink.FBInkConfig{} + rOpts := gofbink.RestrictedConfig{} + fb := gofbink.New(&fbinkOpts, &rOpts) + fb.Open() + defer fb.Close() + fb.Init(&fbinkOpts) + fb.ClearScreen(&fbinkOpts) + fb.Refresh(0,0,0,0, gofbink.DitherPassthrough, &fbinkOpts) + + // Touchscreen setup + touchPath := "/dev/input/event1" + t := koboin.New(touchPath, height, width) + if t == nil { + return + } + defer t.Close() + + // Load known hosts file + path := "/mnt/onboard/.adds/gemini/known-hosts" + err := hosts.Load(path) + if err != nil { + fb.Println(err) + time.Sleep(1000) + } + + hostsfile, err = tofu.NewHostsFile(path) + if err != nil { + fb.Println(err) + time.Sleep(1000) + } + + url := "gemini://breadpunk.club/~bagel" + req, err := gemini.NewRequest(url) + if err != nil { + fb.Println(err) + time.Sleep(1000) + } + resp, err := do(req, nil) + if err != nil { + fb.Println(err) + time.Sleep(1000) + } + defer resp.Body.Close() + + // Handle response + if resp.Status.Class() == gemini.StatusClassSuccess { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fb.Println(err) + } + fb.Println(string(body)) + } else { + fb.Println(fmt.Sprintf("%d %s\n", resp.Status, resp.Meta)) + } + + for { + x, y, err := t.GetInput() + if err != nil { + continue + } + if x < 100 && y < 100 { + break + } + } +} diff --git a/nickel.sh b/nickel.sh new file mode 100755 index 0000000..3ea2bfc --- /dev/null +++ b/nickel.sh @@ -0,0 +1,102 @@ +#!/bin/sh +# This script is from koreader under AGPL-v3 +PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/lib:" + +# We don't need to duplicate any of the env setup from rcS, since we will only ever run this to *restart* nickel, and not bootstrap it. +# Meaning we've already got most of the necessary env from nickel itself via both our launcher (fmon/KFMon) and our own startup script. +# NOTE: LD_LIBRARY_PATH is the only late export from rcS we don't siphon in koreader.sh, for obvious reasons ;). +export LD_LIBRARY_PATH="/usr/local/Kobo" + +# Reset PWD, and clear up our own custom stuff from the env while we're there, otherwise, USBMS may become very wonky on newer FW... +# shellcheck disable=SC2164 +cd / +unset OLDPWD +unset LC_ALL TESSDATA_PREFIX STARDICT_DATA_DIR EXT_FONT_DIR +unset KOREADER_DIR KO_DONT_GRAB_INPUT + +# Ensures fmon will restart. Note that we don't have to worry about reaping this, nickel kills on-animator.sh on start. +( + if [ "${PLATFORM}" = "freescale" ] || [ "${PLATFORM}" = "mx50-ntx" ] || [ "${PLATFORM}" = "mx6sl-ntx" ]; then + usleep 400000 + fi + /etc/init.d/on-animator.sh +) & + + Make sure we kill the Wi-Fi first, because nickel apparently doesn't like it if it's up... (cf. #1520) + NOTE: That check is possibly wrong on PLATFORM == freescale (because I don't know if the sdio_wifi_pwr module exists there), but we don't terribly care about that. +if grep -q "sdio_wifi_pwr" "/proc/modules"; then + killall -q -TERM restore-wifi-async.sh enable-wifi.sh obtain-ip.sh + cp -a "/etc/resolv.conf" "/tmp/resolv.ko" + old_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')" + if [ -x "/sbin/dhcpcd" ]; then + env -u LD_LIBRARY_PATH dhcpcd -d -k "${INTERFACE}" + killall -q -TERM udhcpc default.script + else + killall -q -TERM udhcpc default.script dhcpcd + fi + # NOTE: dhcpcd -k waits for the signalled process to die, but busybox's killall doesn't have a -w, --wait flag, + # so we have to wait for udhcpc to die ourselves... + # NOTE: But if all is well, there *isn't* any udhcpc process or script left to begin with... + kill_timeout=0 + while pkill -0 udhcpc; do + # Stop waiting after 5s + if [ ${kill_timeout} -ge 20 ]; then + break + fi + usleep 250000 + kill_timeout=$((kill_timeout + 1)) + done + + new_hash="$(md5sum "/etc/resolv.conf" | cut -f1 -d' ')" + # Restore our network-specific resolv.conf if the DHCP client wiped it when releasing the lease... + if [ "${new_hash}" != "${old_hash}" ]; then + mv -f "/tmp/resolv.ko" "/etc/resolv.conf" + else + rm -f "/tmp/resolv.ko" + fi + wpa_cli terminate + [ "${WIFI_MODULE}" != "8189fs" ] && [ "${WIFI_MODULE}" != "8192es" ] && wlarm_le -i "${INTERFACE}" down + ifconfig "${INTERFACE}" down + # NOTE: Kobo's busybox build is weird. rmmod appears to be modprobe in disguise, defaulting to the -r flag... + # But since there's currently no modules.dep file being shipped, nor do they include the depmod applet, + # go with what the FW is doing, which is rmmod. + # c.f., #2394? + usleep 250000 + rmmod "${WIFI_MODULE}" + + if [ -n "${CPUFREQ_DVFS}" ]; then + echo "0" >"/sys/devices/platform/mxc_dvfs_core.0/enable" + # Leave Nickel in its usual state, don't try to use conservative + echo "userspace" >"/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" + cat "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" >"/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed" + fi + usleep 250000 + rmmod sdio_wifi_pwr +fi + +unset CPUFREQ_DVFS CPUFREQ_CONSERVATIVE + +# Recreate Nickel's FIFO ourselves, like rcS does, because udev *will* write to it! +# Plus, we actually *do* want the stuff udev writes in there to be processed by Nickel, anyway. +rm -f "/tmp/nickel-hardware-status" +mkfifo "/tmp/nickel-hardware-status" + +# Flush buffers to disk, who knows. +sync + +# Handle the sdcard: +# We need to unmount it ourselves, or Nickel wigs out and shows an "unrecognized FS" popup until the next fake sd add event. +# The following udev trigger should then ensure there's a single sd add event enqueued in the FIFO for it to process, +# ensuring it gets sanely detected and remounted RO. +if [ -e "/dev/mmcblk1p1" ]; then + umount /mnt/sd +fi + +# And finally, simply restart nickel. +# We don't care about horribly legacy stuff, because if people switch between nickel and KOReader in the first place, I assume they're using a decently recent enough FW version. +# Last tested on an H2O & a Forma running FW 4.7.x - 4.25.x +/usr/local/Kobo/hindenburg & +LIBC_FATAL_STDERR_=1 /usr/local/Kobo/nickel -platform kobo -skipFontLoad & +[ "${PLATFORM}" != "freescale" ] && udevadm trigger & + +return 0 diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..846e70d --- /dev/null +++ b/run.sh @@ -0,0 +1,6 @@ +#!/bin/sh +export DBUS_SESSION_BUS_ADDRESS NICKEL_HOME WIFI_MODULE LANG WIFI_MODULE_PATH INTERFACE +sync +killall -TERM nickel hindenburg sickel fickel 2>/dev/null +/mnt/onboard/.adds/gemini/kobo-gemini +exec /mnt/onboard/.adds/gemini/nickel.sh