-(defun elpher-render-gemini-response (mime-type-raw)
- (let* ((mime-type-full (if (string-empty-p mime-type-raw)
- "text/gemini; charset=utf-8"
- mime-type-raw))
- (mime-type-split (split-string mime-type-full ";"))
- (mime-type (string-trim (elt mime-type-split 0)))
- (parameters (if (> (length mime-type-split) 1)
- (string-trim (elt mime-type-split 1))
- "")))
- ;; (message "MIME type %S with parameters %S" mime-type parameters)
- (pcase mime-type
- ((or "text/gemini" "")
- (elpher-render--mimetype-text/gemini elpher-gemini-response parameters))
- ((pred (string-prefix-p "text/"))
- (elpher-render--mimetype-text/plain elpher-gemini-response parameters))
- ((pred (string-prefix-p "image/"))
- (elpher-render--mimetype-image/* elpher-gemini-response parameters))
- (other
- (error "Unsupported MIME type %S" mime-type)))))
+
+(defun elpher-process-gemini-response (renderer)
+ "Process the gemini response and pass the result to RENDERER.
+The response is assumed to be in the variable `elpher-gemini-response'."
+ (condition-case the-error
+ (let* ((response-header (car (split-string elpher-gemini-response "\r\n")))
+ (response-body (substring elpher-gemini-response
+ (+ (string-match "\r\n" elpher-gemini-response) 2)))
+ (response-code (car (split-string response-header)))
+ (response-meta (string-trim
+ (substring response-header
+ (string-match "[ \t]+" response-header)))))
+ (pcase (elt response-code 0)
+ (?1 ; Input required
+ (elpher-with-clean-buffer
+ (insert "Gemini server is requesting input."))
+ (let* ((query-string (read-string (concat response-meta ": ")))
+ (url (elpher-address-to-url (elpher-node-address elpher-current-node)))
+ (query-address (elpher-address-from-url (concat url "?" query-string))))
+ (elpher-get-gemini-response query-address
+ (lambda (_proc event)
+ (unless (string-prefix-p "deleted" event)
+ (funcall #'elpher-process-gemini-response
+ renderer)
+ (elpher-restore-pos))))))
+ (?2 ; Normal response
+ ;; (message response-header)
+ (funcall renderer response-body response-meta))
+ (?3 ; Redirect
+ (message "Following redirect to %s" response-meta)
+ (let ((redirect-address (elpher-address-from-gemini-url response-meta)))
+ (elpher-get-gemini-response redirect-address
+ (lambda (_proc event)
+ (unless (string-prefix-p "deleted" event)
+ (funcall #'elpher-process-gemini-response
+ renderer)
+ (elpher-restore-pos))))))
+ (?4 ; Temporary failure
+ (error "Gemini server reports TEMPORARY FAILURE for this request"))
+ (?5 ; Permanent failure
+ (error "Gemini server reports PERMANENT FAILURE for this request"))
+ (?6 ; Client certificate required
+ (error "Gemini server requires client certificate (unsupported at this time)"))
+ (_other
+ (error "Gemini server responded with unknown response code %S"
+ response-code))))
+ (error
+ (elpher-network-error (elpher-node-address elpher-current-node) the-error))))
+
+(defun elpher-get-gemini-node (renderer)
+ "Getter which retrieves and renders a Gemini node and renders it using RENDERER."
+ (let* ((address (elpher-node-address elpher-current-node))
+ (content (elpher-get-cached-content address)))
+ (condition-case the-error
+ (if (and content (funcall renderer nil))
+ (elpher-with-clean-buffer
+ (insert content)
+ (elpher-restore-pos))
+ (elpher-with-clean-buffer
+ (insert "LOADING GEMINI... (use 'u' to cancel)"))
+ (elpher-get-gemini-response address
+ (lambda (_proc event)
+ (unless (string-prefix-p "deleted" event)
+ (funcall #'elpher-process-gemini-response
+ renderer)
+ (elpher-restore-pos)))))
+ (error
+ (elpher-network-error address the-error)))))
+
+
+(defun elpher-render-gemini (body &optional mime-type-string)
+ "Render gemini response BODY with rendering MIME-TYPE-STRING."
+ (if (not body)
+ t
+ (let* ((mime-type-string* (if (or (not mime-type-string)
+ (string-empty-p mime-type-string))
+ "text/gemini; charset=utf-8"
+ mime-type-string))
+ (mime-type-split (split-string mime-type-string* ";" t))
+ (mime-type (string-trim (car mime-type-split)))
+ (parameters (mapcar (lambda (s)
+ (let ((key-val (split-string s "=")))
+ (list (downcase (string-trim (car key-val)))
+ (downcase (string-trim (cadr key-val))))))
+ (cdr mime-type-split))))
+ (if (and (equal "text/gemini" mime-type)
+ (not (assoc "charset" parameters)))
+ (setq parameters (cons (list "charset" "utf-8") parameters)))
+ (when (string-prefix-p "text/" mime-type)
+ (if (assoc "charset" parameters)
+ (setq body (decode-coding-string body
+ (intern (cadr (assoc "charset" parameters))))))
+ (setq body (replace-regexp-in-string "\r" "" body)))
+ (pcase mime-type
+ ((or "text/gemini" "")
+ (elpher-render-gemini-map body parameters))
+ ((pred (string-prefix-p "text/"))
+ (elpher-render-gemini-plain-text body parameters))
+ ((pred (string-prefix-p "image/"))
+ (elpher-render-image body))
+ (_other
+ (error "Unsupported MIME type %S" mime-type))))))