;; Author: Tim Vaughan <timv@ughan.xyz>
;; Created: 11 April 2019
-;; Version: 2.5.2
+;; Version: 2.6.1
;; Keywords: comm gopher
;; Homepage: http://thelambdalab.xyz/elpher
;; Package-Requires: ((emacs "26"))
;; - direct visualisation of image files,
;; - a simple bookmark management system,
;; - connections using TLS encryption,
-;; - the fledgling Gemini protocol.
+;; - the fledgling Gemini protocol,
+;; - the greybeard Finger protocol.
;; To launch Elpher, simply use 'M-x elpher'. This will open a start
;; page containing information on key bindings and suggested starting
;;; Global constants
;;
-(defconst elpher-version "2.5.2"
+(defconst elpher-version "2.6.1"
"Current version of elpher.")
(defconst elpher-margin-width 6
'gemini)
((equal protocol "telnet")
'telnet)
+ ((equal protocol "finger")
+ 'finger)
(t 'other-url)))))
(defun elpher-address-protocol (address)
"Retrieve host from ADDRESS object."
(url-host address))
+(defun elpher-address-user (address)
+ "Retrieve user from ADDRESS object."
+ (url-user address))
+
(defun elpher-address-port (address)
"Retrieve port from ADDRESS object.
If no address is defined, returns 0. (This is for compatibility with the URL library.)"
(let* ((kill-buffer-query-functions nil)
(port (elpher-address-port address))
(host (elpher-address-host address))
- (response-string "")
+ (response-string-parts nil)
(proc (open-network-stream "elpher-process"
nil
(if force-ipv4 (dns-query host) host)
(when timer
(cancel-timer timer)
(setq timer nil))
- (setq response-string
- (concat response-string string))))
+ (setq response-string-parts
+ (cons string response-string-parts))))
(set-process-sentinel proc
(lambda (proc event)
(condition-case the-error
(concat (elpher-address-to-url address)
"\r\n"))))
((string-prefix-p "deleted" event)) ; do nothing
- ((and (string-empty-p response-string)
+ ((and (not response-string-parts)
(not force-ipv4))
; Try again with IPv4
(message "Connection failed. Retrying with IPv4.")
(elpher-get-gemini-response address renderer t))
(t
(funcall #'elpher-process-gemini-response
- response-string
+ (apply #'concat (reverse response-string-parts))
renderer)
(elpher-restore-pos)))
(error
;; Finger page connection
-(defun elpher-get-finger-page (renderer)
+(defun elpher-get-finger-page (renderer &optional force-ipv4)
"Opens a finger connection to the current page address and renders it using RENDERER."
(let* ((address (elpher-page-address elpher-current-page))
- (host (elpher-address-host address))
- (port (elpher-address-port address))
(content (elpher-get-cached-content address)))
(if (and content (funcall renderer nil))
(elpher-with-clean-buffer
(elpher-with-clean-buffer
(insert "LOADING... (use 'u' to cancel)"))
(condition-case the-error
- (elpher-get-selector address renderer)
+ (let* ((kill-buffer-query-functions nil)
+ (user (let ((filename (elpher-address-filename address)))
+ (if (> (length filename) 1)
+ (substring filename 1)
+ (elpher-address-user address))))
+ (port (let ((given-port (elpher-address-port address)))
+ (if (> given-port 0) given-port 79)))
+ (host (elpher-address-host address))
+ (selector-string "")
+ (proc (open-network-stream "elpher-process"
+ nil
+ (if force-ipv4 (dns-query host) host)
+ port
+ :type 'plain
+ :nowait t))
+ (timer (run-at-time elpher-connection-timeout
+ nil
+ (lambda ()
+ (pcase (process-status proc)
+ ('connect
+ (elpher-process-cleanup)
+ (unless force-ipv4
+ (message "Connection timed out. Retrying with IPv4 address.")
+ (elpher-get-finger-page renderer t))))))))
+ (setq elpher-network-timer timer)
+ (set-process-coding-system proc 'binary)
+ (set-process-filter proc
+ (lambda (_proc string)
+ (cancel-timer timer)
+ (setq selector-string
+ (concat selector-string string))))
+ (set-process-sentinel proc
+ (lambda (_proc event)
+ (condition-case the-error
+ (cond
+ ((string-prefix-p "deleted" event))
+ ((string-prefix-p "open" event)
+ (let ((inhibit-eol-conversion t))
+ (process-send-string
+ proc
+ (concat user "\r\n"))))
+ (t
+ (cancel-timer timer)
+ (funcall renderer selector-string)
+ (elpher-restore-pos)))))))
(error
(elpher-network-error address the-error))))))
(let ((address-copy (elpher-address-from-url
(elpher-address-to-url address))))
(setf (url-filename address-copy) "")
- (elpher-visit-page
- (elpher-make-page (elpher-address-to-url address-copy)
- address-copy))))
+ (elpher-go (elpher-address-to-url address-copy))))
(error "Command invalid for %s" (elpher-page-display-string elpher-current-page)))))
(defun elpher-bookmarks-current-p ()