Pop3 Over SSL/TLS in Common Lisp -
can point me common lisp library (specifically sbcl on linux) pulling pop3 email on ssl/tls? cl-pop seems fine, doesn't seem support ssl , i'm not sure how wrap cl+ssl (assuming it's possible). have suggestions short of rolling own?
you can redefine usocket-connect function yield stream type returned ssl library. can define methods send , receive data on stream using regular strings (the ssl library supports binary default, cl-pop assumes strings can sent). you'll need depend on flexi-streams library convert between text , binary. (ql:quickload :flexi-streams)
the following code make change , define needed methods. since usocket-connect replaced, provide :unencrypted keyword create regular socket.
the code made more efficient.
the string-to-octets , octets-to-string functions support :external-format argument allows them encode/decode many character encoding schemes, including utf-8, iso-8859-*, , others. full list of supported encodings documented here. didn't use :external-format in answer, defaults :latin-1.
the code written against old version of cl+ssl seems have been installed on system debian package manager. current versions of make-ssl-client-stream , make-ssl-server-stream support several more keyword arguments supported version on machine. doesn't matter, however, because cl-pop use none of these keyword arguments.
(defpackage :ssl-pop (:use :common-lisp :cl+ssl :usocket :flexi-streams)) (in-package :ssl-pop) (let ((old-connect (symbol-function 'socket-connect))) (defun socket-connect (host port &key (protocol :stream) external-format certificate key crypto-password (clientp t) close-callback unencrypted (unwrap-streams-p t) crypto-hostname (element-type '(unsigned-byte 8)) timeout deadline (nodelay t nodelay-specified) local-host local-port) (let* ((old-connect-args `(,host ,port :protocol ,protocol :element-type ,element-type :timeout ,timeout :deadline ,deadline ,@(if nodelay-specified `(:nodelay ,nodelay)) :local-host ,local-host :local-port ,local-port)) (plain-socket (apply old-connect old-connect-args))) (if unencrypted plain-socket (let ((socket-stream (socket-stream plain-socket))) (assert (streamp socket-stream)) (if clientp (make-ssl-client-stream socket-stream :external-format external-format :certificate certificate :key key :close-callback close-callback) (make-ssl-server-stream socket-stream :external-format external-format :certificate certificate :key key))))))) (defmethod socket-stream ((object cl+ssl::ssl-stream)) object) (defmethod socket-receive ((socket cl+ssl::ssl-stream) buffer length &key (element-type '(unsigned-byte 8))) (let ((buffer (or buffer (make-array length :element-type element-type)))) (loop ix 0 below length (restart-case (setf (aref buffer ix) (read-byte socket)) (thats-ok () :report "return bytes read" (return-from socket-receive (subseq buffer 0 ix))))) buffer)) (defmethod socket-send ((socket cl+ssl::ssl-stream) buffer length &key host port) (declare (ignore host port)) ;; they're udp (loop byte across buffer (write-byte byte socket))) (defmethod sb-gray:stream-read-line ((socket cl+ssl::ssl-stream)) (let ((result (make-array 0 :adjustable t :fill-pointer t :element-type '(unsigned-byte 8)))) (loop next-byte = (read-byte socket) until (and (>= (length result) 1) (= next-byte 10) (= (aref result (- (length result) 1)) 13)) (vector-push-extend next-byte result)) (octets-to-string (concatenate 'vector (subseq result 0 (- (length result) 1)))))) (defmethod trivial-gray-streams:stream-write-sequence ((stream cl+ssl::ssl-stream) (sequence string) start end &key &allow-other-keys) (trivial-gray-streams:stream-write-sequence stream (string-to-octets sequence) start end)) (defmethod sb-gray:stream-write-char ((stream cl+ssl::ssl-stream) (char character)) (let ((string (make-string 1 :initial-element char))) (write-sequence (string-to-octets string) stream))) (defmethod socket-close ((socket cl+ssl::ssl-stream)) (close socket))
Comments
Post a Comment