Version bump.
[elpher.git] / elpher.el
index 8d4aa0e..608a3d3 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,9 +4,9 @@
 
 ;; Author: Tim Vaughan <tgvaughan@gmail.com>
 ;; Created: 11 April 2019
-;; Version: 2.3.6
+;; Version: 2.4.4
 ;; Keywords: comm gopher
-;; Homepage: https://github.com/tgvaughan/elpher
+;; Homepage: http://thelambdalab.xyz/elpher
 ;; Package-Requires: ((emacs "26"))
 
 ;; This file is not part of GNU Emacs.
@@ -66,7 +66,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "2.3.6"
+(defconst elpher-version "2.4.4"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
@@ -482,6 +482,9 @@ ERROR can be either an error object or a string."
 ;;; Gopher selector retrieval
 ;;
 
+(defvar elpher-network-timer nil
+  "Timer used for network connections.")
+
 (defun elpher-process-cleanup ()
   "Immediately shut down any extant elpher process and timers."
   (let ((p (get-process "elpher-process")))
@@ -492,8 +495,6 @@ ERROR can be either an error object or a string."
 (defvar elpher-use-tls nil
   "If non-nil, use TLS to communicate with gopher servers.")
 
-(defvar elpher-network-timer)
-
 (defun elpher-get-selector (address renderer &optional force-ipv4)
   "Retrieve selector specified by ADDRESS, then render it using RENDERER.
 If FORCE-IPV4 is non-nil, explicitly look up and use IPv4 address corresponding
@@ -503,64 +504,66 @@ to ADDRESS."
         (when (not elpher-use-tls)
           (setq elpher-use-tls t)
           (message "Engaging TLS gopher mode."))
-      (elpher-network-error "Cannot retrieve TLS gopher selector: GnuTLS not available")))
+      (error "Cannot retrieve TLS gopher selector: GnuTLS not available")))
   (unless (< (elpher-address-port address) 65536)
-    (elpher-network-error "Cannot retrieve gopher selector: port number > 65536"))
-  (let* ((kill-buffer-query-functions nil)
-         (port (elpher-address-port address))
-         (host (elpher-address-host address))
-         (selector-string "")
-         (proc (open-network-stream "elpher-process"
-                                    nil
-                                    (if force-ipv4 (dns-query host) host)
-                                    (if (> port 0) port 70)
-                                    :type (if elpher-use-tls 'tls 'plain)
-                                    :nowait t))
-         (timer (run-at-time elpher-connection-timeout
-                             nil
-                             (lambda ()
-                               (pcase (process-status proc)
-                                 ('failed
-                                  (if (and (not (equal (elpher-address-protocol address)
-                                                       "gophers"))
-                                           elpher-use-tls
-                                           (or elpher-auto-disengage-TLS
-                                               (yes-or-no-p "Could not establish encrypted connection.  Disable TLS mode?")))
-                                      (progn
-                                        (message "Disabling TLS mode.")
-                                        (setq elpher-use-tls nil)
-                                        (elpher-get-selector address renderer))
-                                    (elpher-network-error "Could not establish encrypted connection.")))
-                                 ('connect
-                                  (elpher-process-cleanup)
-                                  (unless force-ipv4
-                                    (message "Connection timed out. Retrying with IPv4 address.")
-                                    (elpher-get-selector address 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 (elpher-gopher-address-selector address)
-                                             "\r\n"))))
-                                 (t
-                                  (cancel-timer timer)
-                                  (funcall renderer selector-string)
-                                  (elpher-restore-pos)))
-                              (error
-                               (elpher-network-error address
-                                                     (error-message-string the-error))))))))
+    (error "Cannot retrieve gopher selector: port number > 65536"))
+  (condition-case nil
+      (let* ((kill-buffer-query-functions nil)
+             (port (elpher-address-port address))
+             (host (elpher-address-host address))
+             (selector-string "")
+             (proc (open-network-stream "elpher-process"
+                                        nil
+                                        (if force-ipv4 (dns-query host) host)
+                                        (if (> port 0) port 70)
+                                        :type (if elpher-use-tls 'tls 'plain)
+                                        :nowait t))
+             (timer (run-at-time elpher-connection-timeout
+                                 nil
+                                 (lambda ()
+                                   (pcase (process-status proc)
+                                     ('failed
+                                      (if (and (not (equal (elpher-address-protocol address)
+                                                           "gophers"))
+                                               elpher-use-tls
+                                               (or elpher-auto-disengage-TLS
+                                                   (yes-or-no-p "Could not establish encrypted connection.  Disable TLS mode?")))
+                                          (progn
+                                            (message "Disabling TLS mode.")
+                                            (setq elpher-use-tls nil)
+                                            (elpher-get-selector address renderer))
+                                        (elpher-network-error address "Could not establish encrypted connection")))
+                                     ('connect
+                                      (elpher-process-cleanup)
+                                      (unless force-ipv4
+                                        (message "Connection timed out. Retrying with IPv4 address.")
+                                        (elpher-get-selector address 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 (elpher-gopher-address-selector address)
+                                                 "\r\n"))))
+                                     (t
+                                      (cancel-timer timer)
+                                      (funcall renderer selector-string)
+                                      (elpher-restore-pos)))
+                                  (error
+                                   (elpher-network-error address the-error))))))
+    (error
+     (error "Error initiating connection to server"))))
 
 (defun elpher-get-gopher-node (renderer)
   "Getter function for gopher nodes.
@@ -574,7 +577,10 @@ once they are retrieved from the gopher server."
          (elpher-restore-pos))
       (elpher-with-clean-buffer
        (insert "LOADING... (use 'u' to cancel)"))
-      (elpher-get-selector address renderer))))
+      (condition-case the-error
+          (elpher-get-selector address renderer)
+        (error
+         (elpher-network-error address the-error))))))
 
 ;; Index rendering
 
@@ -665,7 +671,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
 ;; Text rendering
 
 (defconst elpher-url-regex
-  "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]*[a-zA-Z0-9\-]\\|\[[a-zA-Z0-9:]+\]\\)\\(:[0-9]+\\)?\\(/\\([0-9a-zA-Z\-_~?/@|:.]*[0-9a-zA-Z\-_~?/@|]\\)?\\)?"
+  "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]*[a-zA-Z0-9\-]\\|\[[a-zA-Z0-9:]+\]\\)\\(:[0-9]+\\)?\\(/\\([0-9a-zA-Z\-_~?/@|:.%#]*[0-9a-zA-Z\-_~?/@|#]\\)?\\)?"
   "Regexp used to locate and buttniofy URLs in text files loaded by elpher.")
 
 (defun elpher-buttonify-urls (string)
@@ -797,9 +803,8 @@ to ADDRESS."
       (error "Cannot establish gemini connection: GnuTLS not available")
     (unless (< (elpher-address-port address) 65536)
       (error "Cannot establish gemini connection: port number > 65536"))
-    (condition-case the-error
+    (condition-case nil
         (let* ((kill-buffer-query-functions nil)
-               (network-security-level 'medium)
                (port (elpher-address-port address))
                (host (elpher-address-host address))
                (response-string "")
@@ -840,19 +845,19 @@ to ADDRESS."
                                         (message "Connection failed. Retrying with IPv4.")
                                         (cancel-timer timer)
                                         (elpher-get-gemini-response address renderer t))
-                                       (t 
+                                       (t
                                         (funcall #'elpher-process-gemini-response
                                                  response-string
                                                  renderer)
                                         (elpher-restore-pos)))
-                                    (error the-error
+                                    (error
                                            (elpher-network-error address the-error))))))
       (error
        (error "Error initiating connection to server")))))
 
 (defun elpher-parse-gemini-response (response)
-  "Parse the RESPONSE string and return a list of components
-The list is of the form (code meta body). A response of nil implies
+  "Parse the RESPONSE string and return a list of components.
+The list is of the form (code meta body).  A response of nil implies
 that the response was malformed."
   (let ((header-end-idx (string-match "\r\n" response)))
     (if header-end-idx
@@ -987,7 +992,9 @@ For instance, the filename /a/b/../c/./d will reduce to /a/c/d"
   (let ((address (url-generic-parse-url url)))
     (unless (and (url-type address) (not (url-fullness address))) ;avoid mangling mailto: urls
       (setf (url-fullness address) t)
-      (unless (url-host address) ;if there is an explicit host, filenames are absolute
+      (if (url-host address) ;if there is an explicit host, filenames are absolute
+          (if (string-empty-p (url-filename address))
+              (setf (url-filename address) "/")) ;ensure empty filename is marked as absolute
         (setf (url-host address) (url-host (elpher-node-address elpher-current-node)))
         (unless (string-prefix-p "/" (url-filename address)) ;deal with relative links
           (setf (url-filename address)
@@ -1099,6 +1106,13 @@ For instance, the filename /a/b/../c/./d will reduce to /a/c/d"
    (insert "\n"
            "This page contains your bookmarked sites (also visit with B):\n")
    (elpher-insert-index-record "Your Bookmarks" 'bookmarks)
+   (insert "\n"
+           "For Elpher release news or to leave feedback, visit:\n")
+   (elpher-insert-index-record "The Elpher Project Page"
+                               (elpher-make-gopher-address ?1
+                                                           "/projects/elpher/"
+                                                           "thelambdalab.xyz"
+                                                           70))
    (insert "\n"
            "** Refer to the ")
    (let ((help-string "RET,mouse-1: Open Elpher info manual (if available)"))
@@ -1112,7 +1126,7 @@ For instance, the filename /a/b/../c/./d will reduce to /a/c/d"
    (insert " for the full documentation. **\n")
    (insert (propertize
             (concat "  (This should be available if you have installed Elpher using\n"
-                    "   MELPA. Otherwise you will have to install the manual yourself.)")
+                    "   MELPA. Otherwise you will have to install the manual yourself.)\n")
             'face 'shadow))
    (elpher-restore-pos)))