From e1cbf8164afe2cbfaac8b2ee3be90a1a00f2d660 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Fri, 23 Sep 2022 10:27:19 +0200 Subject: [PATCH 01/22] gitmodules: Add cl-ipfs-api2. --- .gitmodules | 4 ++++ _build/cl-ipfs-api2 | 1 + 2 files changed, 5 insertions(+) create mode 160000 _build/cl-ipfs-api2 diff --git a/.gitmodules b/.gitmodules index b069a502fc0..266bd63d1cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -582,3 +582,7 @@ path = _build/ospm url = https://github.com/atlas-engineer/ospm shallow = true +[submodule "_build/cl-ipfs-api2"] + path = _build/cl-ipfs-api2 + url = https://github.com/ambrevar/cl-ipfs-api2 + shallow = true diff --git a/_build/cl-ipfs-api2 b/_build/cl-ipfs-api2 new file mode 160000 index 00000000000..a52676d5c3a --- /dev/null +++ b/_build/cl-ipfs-api2 @@ -0,0 +1 @@ +Subproject commit a52676d5c3a60adce297dd2c5a223aff36916849 From ef8ff03a7213b8738d4bb3a2b518ed23d76906e0 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 21 Sep 2022 12:07:51 +0200 Subject: [PATCH 02/22] mode/ipfs: Initialize. --- nyxt.asd | 2 + source/mode/ipfs.lisp | 137 +++++++++++++++++++++++++++++++++++++++++ tests/online/ipfs.lisp | 41 ++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 source/mode/ipfs.lisp create mode 100644 tests/online/ipfs.lisp diff --git a/nyxt.asd b/nyxt.asd index 5f8c7bf48b4..d3c40f44e2f 100644 --- a/nyxt.asd +++ b/nyxt.asd @@ -27,6 +27,7 @@ cl-css cl-gopher cl-html-diff + cl-ipfs-api2 cl-json cl-ppcre cl-ppcre-unicode @@ -195,6 +196,7 @@ (:file "emacs") (:file "expedition") (:file "force-https") + (:file "ipfs") (:file "macro-edit") (:file "no-image") (:file "no-procrastinate" :depends-on ("blocker")) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp new file mode 100644 index 00000000000..e9148aff947 --- /dev/null +++ b/source/mode/ipfs.lisp @@ -0,0 +1,137 @@ +;;;; SPDX-FileCopyrightText: Atlas Engineer LLC +;;;; SPDX-License-Identifier: BSD-3-Clause + +;; TODO: Support public gateways. +;; TODO: Add support for adding files. + +;; TODO: Handle IPNS as well. + +;; Test data: +;; - https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu +;; - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html +;; - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_Museum_Amsterdam.jpg +;; - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_-_Bildnis_der_Mutter_des_K%C3%BCnstlers.jpeg + +;; REFERENCE: +;; https://docs.ipfs.tech/reference/kubo/rpc/#getting-started +;; +;; https://www.reddit.com/r/ipfs/comments/63ev6h/list_of_ipfs_websites/ +;; https://github.com/brave/brave-browser/issues/10220 +;; https://github.com/ipfs/in-web-browsers +;; https://github.com/ipfs-shipyard/is-ipfs + +(nyxt:define-package :nyxt/ipfs-mode + (:documentation "Mode for IPFS page browsing.")) +(in-package :nyxt/ipfs-mode) + +(define-mode ipfs-mode () + "Handle ipfs:// URLs." + ((visible-in-status-p nil) + (rememberable-p nil) + (daemon + nil + :allocation :class + :documentation "Daemon UIOP process. +There can be only one per Nyxt instance.") + (program + "ipfs" + :type types:pathname-designator + :documentation "Name or path of the IPFS executable. +See also the `arguments' slot.") + (arguments + '() + :type (cons string *) + :documentation "Arguments passed to the daemon when starting. +See also the `program' slot.") + (daemon-timeout + 30 + :type alex:non-negative-float + :documentation "Time in seconds to wait for the daemon to start. +After this time has elapsed, signal an error.") + (public-gateways + '("https://dweb.link") + :type (list-of (or url string)) + :documentation "Redirection example: +https://dweb.link/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR")) + (:toggler-command-p nil)) + +(defmethod daemon-running-p ((mode ipfs-mode)) ; Unused? Useful for debugging though. + (= 0 + (nth 2 + (uiop:run-program (list (program mode) "diag" "cmds") + :ignore-error-status t)))) + +(defmethod start-daemon ((mode ipfs-mode)) + "Wait until IPFS daemon is started. +Return immediately if already started." + (sera:synchronized ((daemon mode)) + (when (and (not (daemon mode)) + (sera:resolve-executable (program mode))) + (let ((p-i (uiop:launch-program (append (list (program mode) "daemon") + (arguments mode)) + :output :stream + :error-output :stream))) + (handler-case + (progn + (bt:with-timeout ((daemon-timeout mode)) + (loop + (unless (uiop:process-alive-p p-i) + (error "Already started?")) + (when (and (uiop:process-alive-p p-i) + (string= "Daemon is ready" (read-line (uiop:process-info-output p-i)))) + (return 'ready)))) + (setf (daemon mode) p-i)) + (bt:timeout () + (uiop:terminate-process p-i :urgent t) + (error "IPFS daemon timed out before it could start:~&~a" + (uiop:slurp-stream-string (uiop:process-info-output p-i)))) + (t () + (uiop:terminate-process p-i :urgent t) + (error "IPFS daemon failed to start:~&~a" + (uiop:slurp-stream-string (uiop:process-info-error-output p-i))))))))) + +(defmethod quit-daemon ((mode ipfs-mode)) + (when (daemon mode) + (uiop:terminate-process (daemon mode)) + (setf (daemon mode) nil))) + +(defun ipfs-request-p (request-data) + (and (toplevel-p request-data) + (or (str:s-member '("ipfs" "ipns") (quri:uri-scheme (url request-data))) + ;; TODO: It's in the response header. + ;; Test: https://en.wikipedia-on-ipfs.org + ;; See https://webkitgtk.org/reference/webkit2gtk/stable/method.URIResponse.get_http_headers.html. + (str:starts-with-p "x-ipfs-path" (mime-type request-data))))) + +(defmethod enable ((mode ipfs-mode) &key) + (hooks:add-hook + (pre-request-hook (buffer mode)) + (make-instance + 'hooks:handler + :fn (lambda (request-data) + (unless (ipfs-request-p request-data) + (disable-modes 'ipfs-mode (buffer mode))) + request-data) + :name 'ipfs-mode-disable))) + +(defmethod disable ((mode ipfs-mode) &key) + ;; TODO: Terminate daemon here? Maybe not, since it's an expensive operation. + ;; But when then? In (before-exit-hook *browser*)? + (hooks:remove-hook + (pre-request-hook (buffer mode)) + 'ipfs-mode-disable)) + +(defmethod fetch ((mode ipfs-mode) url) + ;; TODO: Fallback to public gateway. + (start-daemon mode) + (let ((mime (or (mimes:mime (pathname (quri:uri-path url))) + "text/html;charset=utf8"))) + ;; WARNING: Need byte-vector here because strings CL encoding may confuse the browser. + (values (flex:string-to-octets (ipfs:cat (nyxt::schemeless-url url))) ; TODO: Export? + mime))) + +(define-internal-scheme "ipfs" + (lambda (url-string buffer) + ;; FIXME: This better become a default auto-mode rule. + (enable-modes '(ipfs-mode) buffer) + (fetch (find-submode 'ipfs-mode buffer) (quri:uri url-string)))) diff --git a/tests/online/ipfs.lisp b/tests/online/ipfs.lisp new file mode 100644 index 00000000000..2c545d291f9 --- /dev/null +++ b/tests/online/ipfs.lisp @@ -0,0 +1,41 @@ +;; https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu +(defvar *url-image* "Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu") + +;; TODO: Need path with subpath. +;; ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html +;; ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_Museum_Amsterdam.jpg +;; ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_-_Bildnis_der_Mutter_des_K%C3%BCnstlers.jpeg + +;; http://127.0.0.1:8080/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu + +;; REFERENCE: +;; https://www.reddit.com/r/ipfs/comments/63ev6h/list_of_ipfs_websites/ +;; https://github.com/brave/brave-browser/issues/10220 +;; https://github.com/ipfs/in-web-browsers +;; RPC: https://docs.ipfs.tech/reference/kubo/rpc/#getting-started + +;; REVIEW: Website: +;; http://127.0.0.1:8080/ipfs/QmfQiLpdBDbSkb2oySwFHzNucvLkHmGFxgK4oA2BUSwi4t/ +;; http://127.0.0.1:8080/ipfs/QmZBuTfLH1LLi4JqgutzBdwSYS5ybrkztnyWAfRBP729WB/archives/index.html +;; https://blog.ipfs.tech/24-uncensorable-wikipedia/ + +;; GATEWAYS: +;; https://ipfs.github.io/public-gateway-checker/ +;; https://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi.ipfs.dweb.link/ + +;; https://github.com/ipfs/in-web-browsers + + +;; ADDRESS validator +;; https://github.com/ipfs-shipyard/is-ipfs + + +;; (CL-IPFS-API2::IPFS-CALL "cat" (("arg" "bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_-_Bildnis_der_Mutter_des_K%C3%BCnstlers.jpeg") NIL NIL) :PARAMETERS NIL :WANT-STREAM NIL) +;; Locals: +;; ARGUMENTS = (("arg" "bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_-_Bildnis_der_Mutter_des_K%C3%BCnstlers.jpeg") NIL NIL) +;; CALL = "cat" +;; METHOD = :POST +;; PARAMETERS = NIL +;; WANT-STREAM = NIL + +;; (DRAKMA:HTTP-REQUEST "http://127.0.0.1:5001/api/v0/cat?arg=bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/I/m/Van_Gogh_-_Bildnis_der_Mutter_des_K%C3%BCnstlers.jpeg" :METHOD :POST) From 5375227b1502ae649014a924cc6065c3ec90a27e Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Fri, 23 Sep 2022 11:38:47 +0200 Subject: [PATCH 03/22] mode/ipfs: Support redirect to public gateways. --- source/mode/ipfs.lisp | 50 +++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index e9148aff947..45172337e48 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -1,7 +1,6 @@ ;;;; SPDX-FileCopyrightText: Atlas Engineer LLC ;;;; SPDX-License-Identifier: BSD-3-Clause -;; TODO: Support public gateways. ;; TODO: Add support for adding files. ;; TODO: Handle IPNS as well. @@ -48,11 +47,17 @@ See also the `program' slot.") :type alex:non-negative-float :documentation "Time in seconds to wait for the daemon to start. After this time has elapsed, signal an error.") - (public-gateways - '("https://dweb.link") - :type (list-of (or url string)) - :documentation "Redirection example: -https://dweb.link/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR")) + (gateway + (quri:uri "https://dweb.link") + :type quri:uri + :documentation "Fallback remote node when local daemon is not running. +Redirection example: + + ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html + +to + + https://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.dweb.link/wiki/Vincent_van_Gogh.html")) (:toggler-command-p nil)) (defmethod daemon-running-p ((mode ipfs-mode)) ; Unused? Useful for debugging though. @@ -121,14 +126,37 @@ Return immediately if already started." (pre-request-hook (buffer mode)) 'ipfs-mode-disable)) +(defmethod url->gateway-url ((mode ipfs-mode) url) + "Turn IPFS URL into an HTTP URL on MODE's `gateway' URL." + (let ((cid (quri:uri-domain url))) + (quri:copy-uri url + :scheme "https" + :port (quri.port:scheme-default-port "https") + :host (uiop:strcat cid ".ipfs." (quri:uri-host (gateway mode)))))) + (defmethod fetch ((mode ipfs-mode) url) ;; TODO: Fallback to public gateway. (start-daemon mode) - (let ((mime (or (mimes:mime (pathname (quri:uri-path url))) - "text/html;charset=utf8"))) - ;; WARNING: Need byte-vector here because strings CL encoding may confuse the browser. - (values (flex:string-to-octets (ipfs:cat (nyxt::schemeless-url url))) ; TODO: Export? - mime))) + (if (daemon mode) + (let ((mime (or (mimes:mime (pathname (quri:uri-path url))) + "text/html;charset=utf8"))) + ;; WARNING: Need byte-vector here because strings CL encoding may confuse the browser. + (values (flex:string-to-octets (ipfs:cat (nyxt::schemeless-url url))) ; TODO: Export? + mime)) + (let* ((new-url (url->gateway-url mode url)) + (redirect-html + (spinneret:with-html-string + (:head + (:title "Redirect") + (:style (style (buffer mode)))) + (:body + (:h1 "Redirecting...") + (:p + (:a url) + " to " + (:a new-url)))))) + (buffer-load new-url :buffer (buffer mode)) + (values redirect-html "text/html;charset=utf8")))) (define-internal-scheme "ipfs" (lambda (url-string buffer) From 6f1487a064478aed8e77592a4f43d7c68742111f Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Fri, 23 Sep 2022 11:44:05 +0200 Subject: [PATCH 04/22] build-scripts/nyxt: Add cl-ipfs-api2 input. --- build-scripts/nyxt.scm | 1 + 1 file changed, 1 insertion(+) diff --git a/build-scripts/nyxt.scm b/build-scripts/nyxt.scm index 618236a7ae7..13474c15d0e 100644 --- a/build-scripts/nyxt.scm +++ b/build-scripts/nyxt.scm @@ -152,6 +152,7 @@ cl-html-diff cl-hu.dwim.defclass-star cl-iolib + cl-ipfs-api2 cl-json cl-local-time cl-lparallel From d0f5862b2874a2f23a5910e2b8d33b22a636323d Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Sat, 24 Sep 2022 11:04:55 +0200 Subject: [PATCH 05/22] mode/ipfs: Add support for adding files. --- source/mode/ipfs.lisp | 90 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 45172337e48..0af72df92da 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -1,10 +1,12 @@ ;;;; SPDX-FileCopyrightText: Atlas Engineer LLC ;;;; SPDX-License-Identifier: BSD-3-Clause -;; TODO: Add support for adding files. - ;; TODO: Handle IPNS as well. +;; TODO: Add remove command. +;; TODO: Add pin / unpin command. Display 'pinned' property. + + ;; Test data: ;; - https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu ;; - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html @@ -23,6 +25,13 @@ (:documentation "Mode for IPFS page browsing.")) (in-package :nyxt/ipfs-mode) +;; (define-class uploads-file (files:data-file nyxt-lisp-file) +;; ((files:base-path #p"ipfs-uploads") +;; (files:name "ipfs-uploads")) +;; (:export-class-name-p t) +;; (:accessor-name-transformer (class*:make-name-transformer name)) +;; (:documentation "Listing of all files uploaded to IPFS.")) + (define-mode ipfs-mode () "Handle ipfs:// URLs." ((visible-in-status-p nil) @@ -163,3 +172,80 @@ Return immediately if already started." ;; FIXME: This better become a default auto-mode rule. (enable-modes '(ipfs-mode) buffer) (fetch (find-submode 'ipfs-mode buffer) (quri:uri url-string)))) + +(define-class ipfs-file () + ((name + "" + :type string + :documentation "Name as stored on the IPFS network.") + (file-type + 0 + :type integer) + (size + 0 + :type integer) + (hash + "" + :type string + :documentation "IPFS hash of the file to uniquely identify it.")) + (:export-class-name-p t) + (:accessor-name-transformer (class*:make-name-transformer name))) + +(defmethod prompter:object-attributes ((file ipfs-file) (source prompter:source)) + (declare (ignore source)) + `(("Name" ,(name file)) + ("Size" ,(size file)) + ("Hash" ,(hash file)))) + +(defmethod all-uploads ((mode ipfs-mode)) ; TODO: Report error when daemon is not running. + (when (daemon-running-p mode) + (mapcar (lambda (entry) + (make-instance 'ipfs-file + :name (alex:assoc-value entry "Name") + :file-type (alex:assoc-value entry "Type") + :size (alex:assoc-value entry "Size") + :hash (alex:assoc-value entry "Hash"))) + (ipfs:files-ls)))) + +(defmethod add ((mode ipfs-mode) path) ; TODO: Report error when daemon is not running. + "Add PATH to IPFS and the MFS so that it's listed by 'ipfs files ls'. +See the help message of 'ipfs files'." + (when (daemon-running-p mode) + (dolist (result (ipfs:add path)) + (let ((hash (alex:assoc-value result "Hash")) + (name (alex:assoc-value result "Name"))) + (ipfs:files-cp (uiop:strcat "/ipfs/" hash) + (serapeum:ensure-prefix "/" name))) ))) + +(define-command upload ; TODO: ipfs-companion names it "import". + (&key (paths (prompt + :extra-modes 'nyxt/file-manager-mode:file-manager-mode + :sources (make-instance + 'nyxt/file-manager-mode:file-source)))) + "Import files and directories to the IPFS network." + (mapc (curry #'add (find-submode 'ipfs-mode)) paths) + (echo "Uploaded ~a" paths)) + +(define-class uploads-source (prompter:source) ; TODO: Use in commands? + ((prompter:name "IPFS uploads") + (prompter:constructor (all-uploads (find-submode 'ipfs-mode (current-buffer)))) + (prompter:multi-selection-p t))) + +(define-internal-page-command-global list-uploads () + (ipfs-uploads-buffer "*IPFS uploads*") + "List all IPFS uploads in a new buffer." + (let* ((mode (find-submode 'bookmark-mode (current-buffer))) + (uploads (all-uploads))) + (spinneret:with-html-string + (:style (style mode)) + (:h1 "IPFS Uploads") + (cond + ((null uploads) + (:p "No IPFS uploads.")) + (t + (:ul (mapc + (lambda (upload) + (:div :class "upload" + (:li (name upload) + (:pre (hash upload))))) + uploads))))))) From f83a38e16ea6cd9e62b75b506d97144fa69dc20b Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 15:52:37 +0200 Subject: [PATCH 06/22] gitmodule: Add symbol-munger (for cl-ipfs-api2). --- .gitmodules | 4 ++++ _build/symbol-munger | 1 + 2 files changed, 5 insertions(+) create mode 160000 _build/symbol-munger diff --git a/.gitmodules b/.gitmodules index 266bd63d1cc..08c2eb2fb96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -586,3 +586,7 @@ path = _build/cl-ipfs-api2 url = https://github.com/ambrevar/cl-ipfs-api2 shallow = true +[submodule "_build/symbol-munger"] + path = _build/symbol-munger + url = https://github.com/AccelerationNet/symbol-munger + shallow = true diff --git a/_build/symbol-munger b/_build/symbol-munger new file mode 160000 index 00000000000..e96558e8315 --- /dev/null +++ b/_build/symbol-munger @@ -0,0 +1 @@ +Subproject commit e96558e8315b8eef3822be713354787b2348b25e From c0fc3cbf7e346c7309beada057a4f439f2d8d43c Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 15:53:54 +0200 Subject: [PATCH 07/22] gitmodule: Add collectors (for cl-ipfs-api2). --- .gitmodules | 4 ++++ _build/collectors | 1 + 2 files changed, 5 insertions(+) create mode 160000 _build/collectors diff --git a/.gitmodules b/.gitmodules index 08c2eb2fb96..095ae1029fa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -590,3 +590,7 @@ path = _build/symbol-munger url = https://github.com/AccelerationNet/symbol-munger shallow = true +[submodule "_build/collectors"] + path = _build/collectors + url = https://github.com/AccelerationNet/collectors + shallow = true diff --git a/_build/collectors b/_build/collectors new file mode 160000 index 00000000000..748f0a1613c --- /dev/null +++ b/_build/collectors @@ -0,0 +1 @@ +Subproject commit 748f0a1613ce161edccad4cc815eccd7fc55aaf3 From be7454f5dbdf76c8845f6e63ed548987ca1774b6 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 15:54:42 +0200 Subject: [PATCH 08/22] gitmodule: Add arnesi (for cl-ipfs-api2). --- .gitmodules | 4 ++++ _build/arnesi | 1 + 2 files changed, 5 insertions(+) create mode 160000 _build/arnesi diff --git a/.gitmodules b/.gitmodules index 095ae1029fa..b2a493fdf98 100644 --- a/.gitmodules +++ b/.gitmodules @@ -594,3 +594,7 @@ path = _build/collectors url = https://github.com/AccelerationNet/collectors shallow = true +[submodule "_build/arnesi"] + path = _build/arnesi + url = https://github.com/AccelerationNet/arnesi + shallow = true diff --git a/_build/arnesi b/_build/arnesi new file mode 160000 index 00000000000..1e7dc4cb2ca --- /dev/null +++ b/_build/arnesi @@ -0,0 +1 @@ +Subproject commit 1e7dc4cb2cad8599113c7492c78f4925e839522e From 8515563b1be772971260076ba9ec1ef5ca562398 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 15:55:20 +0200 Subject: [PATCH 09/22] gitmodule: Add yason (for cl-ipfs-api2). --- .gitmodules | 4 ++++ _build/yason | 1 + 2 files changed, 5 insertions(+) create mode 160000 _build/yason diff --git a/.gitmodules b/.gitmodules index b2a493fdf98..fe7fb9c959e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -598,3 +598,7 @@ path = _build/arnesi url = https://github.com/AccelerationNet/arnesi shallow = true +[submodule "_build/yason"] + path = _build/yason + url = https://github.com/phmarek/yason + shallow = true diff --git a/_build/yason b/_build/yason new file mode 160000 index 00000000000..d7bce7ab729 --- /dev/null +++ b/_build/yason @@ -0,0 +1 @@ +Subproject commit d7bce7ab72914e9571ef6c7c2d7a7f96a826c4b8 From 7321bd001b7a2dfbad67874f97a03b78be98a2ea Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:03:17 +0200 Subject: [PATCH 10/22] gitmodule: Update cl-ipfs-api2 fork to 20220924. --- _build/cl-ipfs-api2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_build/cl-ipfs-api2 b/_build/cl-ipfs-api2 index a52676d5c3a..63ab3308687 160000 --- a/_build/cl-ipfs-api2 +++ b/_build/cl-ipfs-api2 @@ -1 +1 @@ -Subproject commit a52676d5c3a60adce297dd2c5a223aff36916849 +Subproject commit 63ab3308687649b0fb7c0882872d9167a9142266 From d1b875e0a0cca0f389dd7fdb5adfa88f970cb80a Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:24:42 +0200 Subject: [PATCH 11/22] mode/ipfs: Fix daemon-running-p. --- source/mode/ipfs.lisp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 0af72df92da..cba5cbc0731 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -6,7 +6,6 @@ ;; TODO: Add remove command. ;; TODO: Add pin / unpin command. Display 'pinned' property. - ;; Test data: ;; - https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu ;; - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/wiki/Vincent_van_Gogh.html @@ -71,9 +70,9 @@ to (defmethod daemon-running-p ((mode ipfs-mode)) ; Unused? Useful for debugging though. (= 0 - (nth 2 - (uiop:run-program (list (program mode) "diag" "cmds") - :ignore-error-status t)))) + (nth-value 2 + (uiop:run-program (list (program mode) "diag" "cmds") + :ignore-error-status t)))) (defmethod start-daemon ((mode ipfs-mode)) "Wait until IPFS daemon is started. From 89884e1afef86b44b2988178c5975861a0fae505 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:27:28 +0200 Subject: [PATCH 12/22] mode/ipfs: Fix all-uploads. --- source/mode/ipfs.lisp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index cba5cbc0731..02069d44149 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -200,11 +200,11 @@ Return immediately if already started." (when (daemon-running-p mode) (mapcar (lambda (entry) (make-instance 'ipfs-file - :name (alex:assoc-value entry "Name") - :file-type (alex:assoc-value entry "Type") - :size (alex:assoc-value entry "Size") - :hash (alex:assoc-value entry "Hash"))) - (ipfs:files-ls)))) + :name (alex:assoc-value entry "Name" :test 'string=) + :file-type (alex:assoc-value entry "Type" :test 'string=) + :size (alex:assoc-value entry "Size" :test 'string=) + :hash (alex:assoc-value entry "Hash" :test 'string=))) + (first (ipfs:files-ls))))) (defmethod add ((mode ipfs-mode) path) ; TODO: Report error when daemon is not running. "Add PATH to IPFS and the MFS so that it's listed by 'ipfs files ls'. From 42cbb4299233d4d7cae6e9f14c3fbc4877410d13 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:28:41 +0200 Subject: [PATCH 13/22] mode/ipfs: Fix add. --- source/mode/ipfs.lisp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 02069d44149..fad085bd81f 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -211,10 +211,10 @@ Return immediately if already started." See the help message of 'ipfs files'." (when (daemon-running-p mode) (dolist (result (ipfs:add path)) - (let ((hash (alex:assoc-value result "Hash")) - (name (alex:assoc-value result "Name"))) + (let ((hash (alex:assoc-value result "Hash" :test 'string=)) + (name (alex:assoc-value result "Name" :test 'string=))) (ipfs:files-cp (uiop:strcat "/ipfs/" hash) - (serapeum:ensure-prefix "/" name))) ))) + (serapeum:ensure-prefix "/" name)))))) (define-command upload ; TODO: ipfs-companion names it "import". (&key (paths (prompt From 667ed728176e8f28730913432fb3742db5a4d228 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:28:50 +0200 Subject: [PATCH 14/22] mode/ipfs: Report when daemon failed to start in upload. --- source/mode/ipfs.lisp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index fad085bd81f..072e2f5f29d 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -222,8 +222,13 @@ See the help message of 'ipfs files'." :sources (make-instance 'nyxt/file-manager-mode:file-source)))) "Import files and directories to the IPFS network." - (mapc (curry #'add (find-submode 'ipfs-mode)) paths) - (echo "Uploaded ~a" paths)) + (let ((mode (find-submode 'ipfs-mode))) + (start-daemon mode) + (if (daemon mode) + (progn + (mapc (curry #'add mode) paths) + (echo "Uploaded ~a" paths)) + (echo "Could not start IPFS daemon required for uploads.")))) (define-class uploads-source (prompter:source) ; TODO: Use in commands? ((prompter:name "IPFS uploads") From 0581db0bc2bfad9f401fa13091b4f2fd2799a333 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 16:41:22 +0200 Subject: [PATCH 15/22] mode/ipfs: Fix list-uploads. --- source/mode/ipfs.lisp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 072e2f5f29d..5c03a2685c8 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -236,20 +236,21 @@ See the help message of 'ipfs files'." (prompter:multi-selection-p t))) (define-internal-page-command-global list-uploads () - (ipfs-uploads-buffer "*IPFS uploads*") + (ipfs-uploads-buffer "*IPFS uploads*" 'ipfs-mode) "List all IPFS uploads in a new buffer." - (let* ((mode (find-submode 'bookmark-mode (current-buffer))) - (uploads (all-uploads))) - (spinneret:with-html-string - (:style (style mode)) - (:h1 "IPFS Uploads") - (cond - ((null uploads) - (:p "No IPFS uploads.")) - (t - (:ul (mapc - (lambda (upload) - (:div :class "upload" - (:li (name upload) - (:pre (hash upload))))) - uploads))))))) + (let ((mode (find-submode 'ipfs-mode (current-buffer)))) + (start-daemon mode) + (let ((uploads (all-uploads mode))) + (spinneret:with-html-string + (:style (style ipfs-uploads-buffer)) + (:h1 "IPFS Uploads") + (cond + ((null uploads) + (:p "No IPFS uploads. Make sure the IPFS daemon is installed.")) + (t + (:ul (mapc + (lambda (upload) + (:div :class "upload" + (:li (name upload) + (:pre (hash upload))))) + uploads)))))))) From b273c14033180228227705835864370916f40731 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 17:21:10 +0200 Subject: [PATCH 16/22] mode/ipfs: Use ipfs-getf. --- source/mode/ipfs.lisp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 5c03a2685c8..0eb6ab163fe 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -196,14 +196,17 @@ Return immediately if already started." ("Size" ,(size file)) ("Hash" ,(hash file)))) +(defun ipfs-getf (entry key) + (alex:assoc-value entry key :test 'string=)) + (defmethod all-uploads ((mode ipfs-mode)) ; TODO: Report error when daemon is not running. (when (daemon-running-p mode) (mapcar (lambda (entry) (make-instance 'ipfs-file - :name (alex:assoc-value entry "Name" :test 'string=) - :file-type (alex:assoc-value entry "Type" :test 'string=) - :size (alex:assoc-value entry "Size" :test 'string=) - :hash (alex:assoc-value entry "Hash" :test 'string=))) + :name (ipfs-getf entry "Name") + :file-type (ipfs-getf entry "Type") + :size (ipfs-getf entry "Size") + :hash (ipfs-getf entry "Hash"))) (first (ipfs:files-ls))))) (defmethod add ((mode ipfs-mode) path) ; TODO: Report error when daemon is not running. @@ -211,8 +214,8 @@ Return immediately if already started." See the help message of 'ipfs files'." (when (daemon-running-p mode) (dolist (result (ipfs:add path)) - (let ((hash (alex:assoc-value result "Hash" :test 'string=)) - (name (alex:assoc-value result "Name" :test 'string=))) + (let ((hash (ipfs-getf result "Hash")) + (name (ipfs-getf result "Name"))) (ipfs:files-cp (uiop:strcat "/ipfs/" hash) (serapeum:ensure-prefix "/" name)))))) From 87d7a387ddd61455bc20bd66931df0216328eb81 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Tue, 27 Sep 2022 17:27:29 +0200 Subject: [PATCH 17/22] mode/ipfs: Add delete-ipfs-file. --- source/mode/ipfs.lisp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 0eb6ab163fe..faee44df6c9 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -3,7 +3,6 @@ ;; TODO: Handle IPNS as well. -;; TODO: Add remove command. ;; TODO: Add pin / unpin command. Display 'pinned' property. ;; Test data: @@ -190,6 +189,10 @@ Return immediately if already started." (:export-class-name-p t) (:accessor-name-transformer (class*:make-name-transformer name))) +(defmethod print-object ((file ipfs-file) stream) + (print-unreadable-object (file stream :type t :identity t) + (format stream "~a" (name file)))) + (defmethod prompter:object-attributes ((file ipfs-file) (source prompter:source)) (declare (ignore source)) `(("Name" ,(name file)) @@ -238,6 +241,22 @@ See the help message of 'ipfs files'." (prompter:constructor (all-uploads (find-submode 'ipfs-mode (current-buffer)))) (prompter:multi-selection-p t))) +(defun ipfs-path (ipfs-file) + "Return path (also known as 'name') of IPFS-FILE. +It's prefixed with '/'." + (sera:ensure-prefix "/" (name ipfs-file))) + +(define-command delete-ipfs-file + (&key (files (prompt :sources 'uploads-source))) + "Delete IPFS file(s)." + (let ((mode (find-submode 'ipfs-mode (current-buffer)))) + (start-daemon mode) + (if (daemon mode) + (progn + (mapc (compose #'ipfs:files-rm #'ipfs-path) files) + (echo "Deleted IPFS files: ~a" (mapcar #'name files))) + (echo "Could not start IPFS daemon required to delete files.")))) + (define-internal-page-command-global list-uploads () (ipfs-uploads-buffer "*IPFS uploads*" 'ipfs-mode) "List all IPFS uploads in a new buffer." From 9e12c5e938f0ab7b157cacef0fa8812e87cfcde1 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 26 Oct 2022 11:34:13 +0200 Subject: [PATCH 18/22] build-scripts/nyxt: Use Drakma and cl-ipfs-api2 forks. --- build-scripts/nyxt.scm | 351 +++++++++++++++++++++++++---------------- 1 file changed, 211 insertions(+), 140 deletions(-) diff --git a/build-scripts/nyxt.scm b/build-scripts/nyxt.scm index 13474c15d0e..c224ef74adf 100644 --- a/build-scripts/nyxt.scm +++ b/build-scripts/nyxt.scm @@ -72,149 +72,220 @@ (close-pipe pipe) version)) +(define-public sbcl-drakma + (let ((commit "650f48095402cb2e25a069133b158823c920e4e7")) + (package + (name "sbcl-drakma") + (version (git-version "2.0.9" "1" commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/Ambrevar/drakma") + (commit commit))) + (file-name (git-file-name "cl-drakma" version)) + (sha256 + (base32 + "001w7nsmz0xqdg700gv38v630dv7h2ggqlm87gfrxmsqwpnswgyi")))) + (build-system asdf-build-system/sbcl) + (inputs + (list sbcl-puri + sbcl-cl-base64 + sbcl-chunga + sbcl-flexi-streams + sbcl-cl-ppcre + sbcl-chipz + sbcl-usocket + sbcl-cl+ssl)) + (native-inputs + (list sbcl-fiveam)) + (home-page "https://edicl.github.io/drakma/") + (synopsis "HTTP client written in Common Lisp") + (description + "Drakma is a full-featured HTTP client implemented in Common Lisp. It +knows how to handle HTTP/1.1 chunking, persistent connections, re-usable +sockets, SSL, continuable uploads, file uploads, cookies, and more.") + (license license:bsd-2)))) + +(define-public cl-drakma + (sbcl-package->cl-source-package sbcl-drakma)) + +(define-public sbcl-cl-ipfs-api2 + (let ((commit "63ab3308687649b0fb7c0882872d9167a9142266") + (revision "1")) + (package + (name "sbcl-cl-ipfs-api2") + (version (git-version "0.51" revision commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/Ambrevar/cl-ipfs-api2/") + (commit commit))) + (file-name (git-file-name "cl-ipfs-api2" version)) + (sha256 + (base32 "1b5ms1vx9x646c16hqwq0vqb49caj1n26vr0v25yf1y6gjjg0sla")))) + (build-system asdf-build-system/sbcl) + (arguments + '(#:tests? #f)) ; There are no tests. + (inputs + (list sbcl-arnesi + sbcl-drakma + sbcl-yason)) + (home-page "https://github.com/JadedCtrl/cl-ipfs-api2/") + (synopsis "Bindings for the IPFS HTTP API") + (description + "@code{cl-sbcl-cl-ipfs-api2} is a pretty simple set of IPFS bindings +for Common Lisp, using the HTTP API for (almost) everything, except for pubsub +(which uses the locally installed go-ipfs program).") + (license license:lgpl3)))) + +(define-public cl-ipfs-api2 + (sbcl-package->cl-source-package sbcl-cl-ipfs-api2)) + (define-public nyxt (package - (name "nyxt") - (version (nyxt-git-version)) - (source (local-file %source-dir #:recursive? #t #:select? (git-predicate %source-dir))) - (build-system gnu-build-system) ; TODO: Use glib-or-gtk-build-system instead? - (arguments - (list - #:make-flags #~(list "nyxt" "NYXT_SUBMODULES=false" - (string-append "DESTDIR=" #$output) - "PREFIX=") - #:strip-binaries? #f ; Stripping breaks SBCL binaries. - #:phases - #~(modify-phases %standard-phases - (delete 'configure) - (add-before 'build 'fix-common-lisp-cache-folder - (lambda _ - (setenv "HOME" "/tmp") - #t)) - (add-before 'check 'configure-tests - (lambda _ - (setenv "NYXT_TESTS_NO_NETWORK" "1") - (setenv "NYXT_TESTS_ERROR_ON_FAIL" "1") - #t)) - (add-after 'install 'wrap-program - (lambda _ - (let* ((bin (string-append #$output "/bin/nyxt")) - (libs (list #$(this-package-input "gsettings-desktop-schemas"))) - (path (string-join - (map (lambda (lib) - (string-append lib "/lib")) - libs) - ":")) - (gi-path (getenv "GI_TYPELIB_PATH")) - (xdg-path (string-join - (map (lambda (lib) - (string-append lib "/share")) - libs) - ":"))) - (wrap-program bin - `("GIO_EXTRA_MODULES" prefix - (,(string-append #$(this-package-input "glib-networking") "/lib/gio/modules"))) - `("GI_TYPELIB_PATH" prefix (,gi-path)) - `("LD_LIBRARY_PATH" ":" prefix (,path)) - `("XDG_DATA_DIRS" ":" prefix (,xdg-path))) - #t)))))) - ;; We use `cl-*' inputs and not `sbcl-*' ones so that CCL users can also use - ;; this Guix manifests. - ;; - ;; Another reason is to not fail when an input dependency is found in - ;; ~/common-lisp, which would trigger a rebuild of the SBCL input in the - ;; store, which is read-only and would thus fail. - ;; - ;; The official Guix package should use `sbcl-*' inputs though. - (native-inputs - (list cl-lisp-unit2 - sbcl - ;; Only for development, unneeded for the upstream Guix package: - cl-trivial-benchmark)) - (inputs - (list cl-alexandria - cl-bordeaux-threads - cl-base64 - cl-calispel - cl-containers - cl-css - cl-closer-mop - cl-clss - cl-cluffer - cl-custom-hash-table - cl-dexador - cl-dissect - cl-trivial-custom-debugger - cl-enchant - cl-flexi-streams - cl-fset - cl-gopher - cl-html-diff - cl-hu.dwim.defclass-star - cl-iolib - cl-ipfs-api2 - cl-json - cl-local-time - cl-lparallel - cl-log4cl - cl-mk-string-metrics - cl-moptilities - cl-named-readtables - cl-ndebug - cl-nfiles - cl-nhooks - cl-nkeymaps - cl-osicat ; Not needed for SBCL, remove it in Guix upstream package. - cl-ospm - cl-parenscript - cl-phos - cl-plump - cl-ppcre - cl-prevalence - cl-py-configparser - cl-qrencode - cl-quri - cl-serapeum - cl-spinneret - cl-str - cl-slime-swank - cl-slynk - cl-tld - cl-trivia - cl-trivial-clipboard - cl-trivial-features - cl-trivial-garbage - cl-trivial-package-local-nicknames - cl-trivial-types - cl-unix-opts - ;; System deps - gcc-toolchain ; Needed for cl-iolib - cl-cffi-gtk - cl-webkit - glib-networking - gsettings-desktop-schemas - cl-gobject-introspection - gtk+ ; For the main loop - webkitgtk ; Required when we use its typelib - gobject-introspection - pkg-config)) - (propagated-inputs - (list - ;; Useful for video playback in all-inclusive Guix profiles. - ;; For now upstream Guix does not include the GST plugins. - gst-libav - gst-plugins-bad - gst-plugins-base - gst-plugins-good - gst-plugins-ugly - ;; For spell-checking. Same, upstream Guix does not include it in the package. - aspell - aspell-dict-en)) - (synopsis "Extensible web browser in Common Lisp") - (home-page "https://nyxt.atlas.engineer") - (description "Nyxt is a keyboard-oriented, extensible web browser + (name "nyxt") + (version (nyxt-git-version)) + (source (local-file %source-dir #:recursive? #t #:select? (git-predicate %source-dir))) + (build-system gnu-build-system) ; TODO: Use glib-or-gtk-build-system instead? + (arguments + (list + #:make-flags #~(list "nyxt" "NYXT_SUBMODULES=false" + (string-append "DESTDIR=" #$output) + "PREFIX=") + #:strip-binaries? #f ; Stripping breaks SBCL binaries. + #:phases + #~(modify-phases %standard-phases + (delete 'configure) + (add-before 'build 'fix-common-lisp-cache-folder + (lambda _ + (setenv "HOME" "/tmp") + #t)) + (add-before 'check 'configure-tests + (lambda _ + (setenv "NYXT_TESTS_NO_NETWORK" "1") + (setenv "NYXT_TESTS_ERROR_ON_FAIL" "1") + #t)) + (add-after 'install 'wrap-program + (lambda _ + (let* ((bin (string-append #$output "/bin/nyxt")) + (libs (list #$(this-package-input "gsettings-desktop-schemas"))) + (path (string-join + (map (lambda (lib) + (string-append lib "/lib")) + libs) + ":")) + (gi-path (getenv "GI_TYPELIB_PATH")) + (xdg-path (string-join + (map (lambda (lib) + (string-append lib "/share")) + libs) + ":"))) + (wrap-program bin + `("GIO_EXTRA_MODULES" prefix + (,(string-append #$(this-package-input "glib-networking") "/lib/gio/modules"))) + `("GI_TYPELIB_PATH" prefix (,gi-path)) + `("LD_LIBRARY_PATH" ":" prefix (,path)) + `("XDG_DATA_DIRS" ":" prefix (,xdg-path))) + #t)))))) + ;; We use `cl-*' inputs and not `sbcl-*' ones so that CCL users can also use + ;; this Guix manifests. + ;; + ;; Another reason is to not fail when an input dependency is found in + ;; ~/common-lisp, which would trigger a rebuild of the SBCL input in the + ;; store, which is read-only and would thus fail. + ;; + ;; The official Guix package should use `sbcl-*' inputs though. + (native-inputs + (list cl-lisp-unit2 + sbcl + ;; Only for development, unneeded for the upstream Guix package: + cl-trivial-benchmark)) + (inputs + (list cl-alexandria + cl-bordeaux-threads + cl-base64 + cl-calispel + cl-containers + cl-css + cl-closer-mop + cl-clss + cl-cluffer + cl-custom-hash-table + cl-dexador + cl-dissect + cl-trivial-custom-debugger + cl-enchant + cl-flexi-streams + cl-fset + cl-gopher + cl-html-diff + cl-hu.dwim.defclass-star + cl-iolib + cl-ipfs-api2 + cl-json + cl-local-time + cl-lparallel + cl-log4cl + cl-mk-string-metrics + cl-moptilities + cl-named-readtables + cl-ndebug + cl-nfiles + cl-nhooks + cl-nkeymaps + cl-osicat ; Not needed for SBCL, remove it in Guix upstream package. + cl-ospm + cl-parenscript + cl-phos + cl-plump + cl-ppcre + cl-prevalence + cl-py-configparser + cl-qrencode + cl-quri + cl-serapeum + cl-spinneret + cl-str + cl-slime-swank + cl-slynk + cl-tld + cl-trivia + cl-trivial-clipboard + cl-trivial-features + cl-trivial-garbage + cl-trivial-package-local-nicknames + cl-trivial-types + cl-unix-opts + ;; System deps + gcc-toolchain ; Needed for cl-iolib + cl-cffi-gtk + cl-webkit + glib-networking + gsettings-desktop-schemas + cl-gobject-introspection + gtk+ ; For the main loop + webkitgtk ; Required when we use its typelib + gobject-introspection + pkg-config)) + (propagated-inputs + (list + ;; Useful for video playback in all-inclusive Guix profiles. + ;; For now upstream Guix does not include the GST plugins. + gst-libav + gst-plugins-bad + gst-plugins-base + gst-plugins-good + gst-plugins-ugly + ;; For spell-checking. Same, upstream Guix does not include it in the package. + aspell + aspell-dict-en)) + (synopsis "Extensible web browser in Common Lisp") + (home-page "https://nyxt.atlas.engineer") + (description "Nyxt is a keyboard-oriented, extensible web browser designed for power users. The application has familiar Emacs and VI keybindings and is fully configurable and extensible in Common Lisp.") - (license license:bsd-3))) + (license license:bsd-3))) nyxt From cc91e7bbbde76e1d800f9c586d03688433630ae9 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 26 Oct 2022 15:55:25 +0200 Subject: [PATCH 19/22] mode/ipfs: Return non-nil if daemon is started externally. --- source/mode/ipfs.lisp | 53 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index faee44df6c9..15e8e2c7c76 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -75,32 +75,35 @@ to (defmethod start-daemon ((mode ipfs-mode)) "Wait until IPFS daemon is started. -Return immediately if already started." +Return non-nil immediately if already started." (sera:synchronized ((daemon mode)) - (when (and (not (daemon mode)) - (sera:resolve-executable (program mode))) - (let ((p-i (uiop:launch-program (append (list (program mode) "daemon") - (arguments mode)) - :output :stream - :error-output :stream))) - (handler-case - (progn - (bt:with-timeout ((daemon-timeout mode)) - (loop - (unless (uiop:process-alive-p p-i) - (error "Already started?")) - (when (and (uiop:process-alive-p p-i) - (string= "Daemon is ready" (read-line (uiop:process-info-output p-i)))) - (return 'ready)))) - (setf (daemon mode) p-i)) - (bt:timeout () - (uiop:terminate-process p-i :urgent t) - (error "IPFS daemon timed out before it could start:~&~a" - (uiop:slurp-stream-string (uiop:process-info-output p-i)))) - (t () - (uiop:terminate-process p-i :urgent t) - (error "IPFS daemon failed to start:~&~a" - (uiop:slurp-stream-string (uiop:process-info-error-output p-i))))))))) + (or (daemon mode) + (daemon-running-p mode) + (when (and (not (daemon mode)) + (sera:resolve-executable (program mode))) + (let ((p-i (uiop:launch-program (append (list (program mode) "daemon") + (arguments mode)) + :output :stream + :error-output :stream))) + (handler-case + (progn + (bt:with-timeout ((daemon-timeout mode)) + (loop + (unless (uiop:process-alive-p p-i) + (error "Already started?")) + (when (and (uiop:process-alive-p p-i) + (string= "Daemon is ready" (read-line (uiop:process-info-output p-i)))) + (return 'ready)))) + (setf (daemon mode) p-i)) + (bt:timeout () + (uiop:terminate-process p-i :urgent t) + + (log:error "IPFS daemon timed out before it could start:~&~a" + (uiop:slurp-stream-string (uiop:process-info-output p-i)))) + (t () + (uiop:terminate-process p-i :urgent t) + (log:error "IPFS daemon failed to start:~&~a" + (uiop:slurp-stream-string (uiop:process-info-error-output p-i)))))))))) (defmethod quit-daemon ((mode ipfs-mode)) (when (daemon mode) From 5433990fa2d77e4bcaed195fc27fade40c40b383 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 26 Oct 2022 15:57:51 +0200 Subject: [PATCH 20/22] mode/ipfs: Refactor start-daemon / daemon-running-p calls. --- source/mode/ipfs.lisp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 15e8e2c7c76..2a2d3a3ba6d 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -145,9 +145,7 @@ Return non-nil immediately if already started." :host (uiop:strcat cid ".ipfs." (quri:uri-host (gateway mode)))))) (defmethod fetch ((mode ipfs-mode) url) - ;; TODO: Fallback to public gateway. - (start-daemon mode) - (if (daemon mode) + (if (start-daemon mode) (let ((mime (or (mimes:mime (pathname (quri:uri-path url))) "text/html;charset=utf8"))) ;; WARNING: Need byte-vector here because strings CL encoding may confuse the browser. @@ -206,7 +204,7 @@ Return non-nil immediately if already started." (alex:assoc-value entry key :test 'string=)) (defmethod all-uploads ((mode ipfs-mode)) ; TODO: Report error when daemon is not running. - (when (daemon-running-p mode) + (when (start-daemon mode) (mapcar (lambda (entry) (make-instance 'ipfs-file :name (ipfs-getf entry "Name") @@ -218,7 +216,7 @@ Return non-nil immediately if already started." (defmethod add ((mode ipfs-mode) path) ; TODO: Report error when daemon is not running. "Add PATH to IPFS and the MFS so that it's listed by 'ipfs files ls'. See the help message of 'ipfs files'." - (when (daemon-running-p mode) + (when (start-daemon mode) (dolist (result (ipfs:add path)) (let ((hash (ipfs-getf result "Hash")) (name (ipfs-getf result "Name"))) @@ -232,8 +230,7 @@ See the help message of 'ipfs files'." 'nyxt/file-manager-mode:file-source)))) "Import files and directories to the IPFS network." (let ((mode (find-submode 'ipfs-mode))) - (start-daemon mode) - (if (daemon mode) + (if (start-daemon mode) (progn (mapc (curry #'add mode) paths) (echo "Uploaded ~a" paths)) @@ -253,8 +250,7 @@ It's prefixed with '/'." (&key (files (prompt :sources 'uploads-source))) "Delete IPFS file(s)." (let ((mode (find-submode 'ipfs-mode (current-buffer)))) - (start-daemon mode) - (if (daemon mode) + (if (start-daemon mode) (progn (mapc (compose #'ipfs:files-rm #'ipfs-path) files) (echo "Deleted IPFS files: ~a" (mapcar #'name files))) From 80a29a7f70a2db2408d54f1f013834c93bcdb32b Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 26 Oct 2022 16:02:13 +0200 Subject: [PATCH 21/22] mode/ipfs: Add and call ipfs-init. --- source/mode/ipfs.lisp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 2a2d3a3ba6d..49f79e62eac 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -73,6 +73,14 @@ to (uiop:run-program (list (program mode) "diag" "cmds") :ignore-error-status t)))) +(defmethod ipfs-init ((mode ipfs-mode)) + (unless (uiop:directory-exists-p (or (uiop:getenv "IPFS_PATH") + (nfiles:join (user-homedir-pathname) ".ipfs"))) + (handler-case (uiop:run-program (append (list (program mode) "init")) + :output t :error-output t) + (t (c) + (log:error "Error while initializing IPFS: ~a" c))))) + (defmethod start-daemon ((mode ipfs-mode)) "Wait until IPFS daemon is started. Return non-nil immediately if already started." @@ -81,6 +89,7 @@ Return non-nil immediately if already started." (daemon-running-p mode) (when (and (not (daemon mode)) (sera:resolve-executable (program mode))) + (ipfs-init mode) ; TODO: Handle errors gracefully. (let ((p-i (uiop:launch-program (append (list (program mode) "daemon") (arguments mode)) :output :stream From 4bb657835df03dec41a77e8441316a121969df67 Mon Sep 17 00:00:00 2001 From: Pierre Neidhardt Date: Wed, 26 Oct 2022 16:08:20 +0200 Subject: [PATCH 22/22] mode/ipfs: Kill IPFS on exit. --- source/mode/ipfs.lisp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/mode/ipfs.lisp b/source/mode/ipfs.lisp index 49f79e62eac..5f661894ce4 100644 --- a/source/mode/ipfs.lisp +++ b/source/mode/ipfs.lisp @@ -103,7 +103,13 @@ Return non-nil immediately if already started." (when (and (uiop:process-alive-p p-i) (string= "Daemon is ready" (read-line (uiop:process-info-output p-i)))) (return 'ready)))) - (setf (daemon mode) p-i)) + (setf (daemon mode) p-i) + ;; Terminate daemon on exit since it's an expensive operation. + (hooks:add-hook + (before-exit-hook *browser*) + (make-instance 'hooks:handler + :fn (lambda () (quit-daemon mode)) + :name 'kill-ipfs))) (bt:timeout () (uiop:terminate-process p-i :urgent t) @@ -139,8 +145,6 @@ Return non-nil immediately if already started." :name 'ipfs-mode-disable))) (defmethod disable ((mode ipfs-mode) &key) - ;; TODO: Terminate daemon here? Maybe not, since it's an expensive operation. - ;; But when then? In (before-exit-hook *browser*)? (hooks:remove-hook (pre-request-hook (buffer mode)) 'ipfs-mode-disable))