+ "Preprocess text selector response contained in STRING.
+This involes decoding the character representation, and clearing
+away CRs and any terminating period."
+ (elpher-decode (replace-regexp-in-string "\n\.\n$" "\n"
+ (replace-regexp-in-string "\r" "" string))))
+
+
+;;; Network error reporting
+;;
+
+(defun elpher-network-error (address error)
+ "Display ERROR message following unsuccessful negotiation with ADDRESS."
+ (elpher-with-clean-buffer
+ (insert (propertize "\n---- ERROR -----\n\n" 'face 'error)
+ "When attempting to retrieve " (elpher-address-to-url address) ":\n"
+ (error-message-string error) ".\n"
+ (propertize "\n----------------\n\n" 'face 'error)
+ "Press 'u' to return to the previous page.")))
+
+
+;;; Gopher selector retrieval
+;;
+
+(defun elpher-process-cleanup ()
+ "Immediately shut down any extant elpher process."
+ (let ((p (get-process "elpher-process")))
+ (if p (delete-process p))))
+
+(defvar elpher-use-tls nil
+ "If non-nil, use TLS to communicate with gopher servers.")
+
+(defvar elpher-selector-string)
+
+(defun elpher-get-selector (address after &optional propagate-error)
+ "Retrieve selector specified by ADDRESS, then execute AFTER.
+The result is stored as a string in the variable ‘elpher-selector-string’.
+
+Usually errors result in an error page being displayed. This is only
+appropriate if the selector is to be directly viewed. If PROPAGATE-ERROR
+is non-nil, this message is not displayed. Instead, the error propagates
+up to the calling function."
+ (setq elpher-selector-string "")
+ (when (equal (elpher-address-protocol address) "gophers")
+ (if (gnutls-available-p)
+ (when (not elpher-use-tls)
+ (setq elpher-use-tls t)
+ (message "Engaging TLS gopher mode."))
+ (error "Cannot retrieve TLS gopher selector: GnuTLS not available")))
+ (condition-case the-error
+ (let* ((kill-buffer-query-functions nil)
+ (proc (open-network-stream "elpher-process"
+ nil
+ (elpher-address-host address)
+ (if (> (elpher-address-port address) 0)
+ (elpher-address-port address)
+ 70)
+ :type (if elpher-use-tls 'tls 'plain))))
+ (set-process-coding-system proc 'binary)
+ (set-process-filter proc
+ (lambda (_proc string)
+ (setq elpher-selector-string
+ (concat elpher-selector-string string))))
+ (set-process-sentinel proc after)
+ (process-send-string proc
+ (concat (elpher-gopher-address-selector address) "\n")))
+ (error
+ (if (and (consp the-error)
+ (eq (car the-error) 'gnutls-error)
+ (not (equal (elpher-address-protocol address) "gophers"))
+ (or elpher-auto-disengage-TLS
+ (yes-or-no-p "Could not establish encrypted connection. Disable TLS mode? ")))
+ (progn
+ (message "Disengaging TLS gopher mode.")
+ (setq elpher-use-tls nil)
+ (elpher-get-selector address after))
+ (elpher-process-cleanup)
+ (if propagate-error
+ (error the-error)
+ (elpher-with-clean-buffer
+ (insert (propertize "\n---- ERROR -----\n\n" 'face 'error)
+ "Failed to connect to " (elpher-address-to-url address) ".\n"
+ (propertize "\n----------------\n\n" 'face 'error)
+ "Press 'u' to return to the previous page.")))))))
+
+(defun elpher-get-gopher-node (renderer)
+ "Getter function for gopher nodes.
+The RENDERER procedure is used to display the contents of the node
+once they are retrieved from the gopher server."
+ (let* ((address (elpher-node-address elpher-current-node))
+ (content (elpher-get-cached-content address)))
+ (if (and content (funcall renderer nil))
+ (elpher-with-clean-buffer
+ (insert content)
+ (elpher-restore-pos))
+ (elpher-with-clean-buffer
+ (insert "LOADING... (use 'u' to cancel)"))
+ (elpher-get-selector address
+ (lambda (_proc event)
+ (unless (string-prefix-p "deleted" event)
+ (funcall renderer elpher-selector-string)
+ (elpher-restore-pos)))))))
+
+;; Index rendering