- (error "Cannot retrieve TLS selector: GnuTLS not available")
- (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)
- 1965)
- :type 'tls)))
- (set-process-coding-system proc 'binary)
- (set-process-filter proc
- (lambda (_proc string)
- (setq elpher-gemini-response
- (concat elpher-gemini-response string))))
- (set-process-sentinel proc after)
- (process-send-string proc
- (concat (elpher-address-to-url address) "\r\n")))))
+ (error "Cannot establish gemini connection: GnuTLS not available")
+ (condition-case the-error
+ (let* ((kill-buffer-query-functions nil)
+ (network-security-level 'medium)
+ (port (elpher-address-port address))
+ (host (elpher-address-host address))
+ (proc (open-network-stream "elpher-process"
+ nil
+ (if force-ipv4 (dns-query host) host)
+ (if (> port 0) port 1965)
+ :type 'tls
+ :nowait t))
+ (timer (run-at-time elpher-connection-timeout nil
+ (lambda ()
+ (elpher-process-cleanup)
+ (unless force-ipv4
+ ; Try again with IPv4
+ (elpher-get-gemini-response address renderer t))))))
+ (set-process-coding-system proc 'binary)
+ (set-process-filter proc
+ (lambda (_proc string)
+ (cancel-timer timer)
+ (setq elpher-gemini-response
+ (concat elpher-gemini-response string))))
+ (set-process-sentinel proc
+ (lambda (proc event)
+ (cond
+ ((string-prefix-p "open" event) ; request URL
+ (let ((inhibit-eol-conversion t))
+ (process-send-string
+ proc
+ (concat (elpher-address-to-url address)
+ "\r\n"))))
+ ((string-prefix-p "deleted" event)) ; do nothing
+ ((and (string-empty-p elpher-gemini-response)
+ (not force-ipv4))
+ ; Try again with IPv4
+ (cancel-timer timer)
+ (elpher-get-gemini-response address renderer t))
+ (t
+ (funcall #'elpher-process-gemini-response
+ renderer)
+ (elpher-restore-pos))))))
+ (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
+that the response was malformed."
+ (let ((header-end-idx (string-match "\r\n" response)))
+ (if header-end-idx
+ (let ((header (string-trim (substring response 0 header-end-idx)))
+ (body (substring response (+ header-end-idx 2))))
+ (if (>= (length header) 2)
+ (let ((code (substring header 0 2))
+ (meta (string-trim (substring header 2))))
+ (list code meta body))
+ (error "Malformed response: No response status found in header %s" header)))
+ (error "Malformed response: No CRLF-delimited header found"))))