about summary refs log tree commit diff
path: root/third_party/lisp/mime4cl/endec.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/lisp/mime4cl/endec.lisp')
-rw-r--r--third_party/lisp/mime4cl/endec.lisp136
1 files changed, 51 insertions, 85 deletions
diff --git a/third_party/lisp/mime4cl/endec.lisp b/third_party/lisp/mime4cl/endec.lisp
index 020c212e5e..2e282c2378 100644
--- a/third_party/lisp/mime4cl/endec.lisp
+++ b/third_party/lisp/mime4cl/endec.lisp
@@ -1,6 +1,7 @@
 ;;;  endec.lisp --- encoder/decoder functions
 
 ;;;  Copyright (C) 2005-2008, 2010 by Walter C. Pelissero
+;;;  Copyright (C) 2023 by The TVL Authors
 
 ;;;  Author: Walter C. Pelissero <walter@pelissero.de>
 ;;;  Project: mime4cl
@@ -21,19 +22,21 @@
 
 (in-package :mime4cl)
 
+(defun redirect-stream (in out &key (buffer-size 4096))
+  "Consume input stream IN and write all its content to output stream OUT.
+The streams' element types need to match."
+  (let ((buf (make-array buffer-size :element-type (stream-element-type in))))
+    (loop for pos = (read-sequence buf in)
+          while (> pos 0)
+          do (write-sequence buf out :end pos))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Thank you SBCL for rendering constants totally useless!
 (defparameter +base64-encode-table+
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
 
-(defparameter +base64-decode-table+
-  (let ((da (make-array 256 :element-type '(unsigned-byte 8) :initial-element 65)))
-    (dotimes (i 64)
-      (setf (aref da (char-code (char +base64-encode-table+ i))) i))
-    da))
-
-(declaim (type (simple-array (unsigned-byte 8)) +base64-decode-table+)
-         (type simple-string +base64-encode-table+))
+(declaim (type simple-string +base64-encode-table+))
 
 (defvar *base64-line-length* 76
   "Maximum length of the encoded base64 line.  NIL means it can
@@ -161,7 +164,7 @@ It should expect a character as its only argument."))
        for byte = (decoder-read-byte decoder)
        unless byte
        do (return-from decoder-read-line nil)
-       do (be c (code-char byte)
+       do (let ((c (code-char byte)))
             (cond ((char= c #\return)
                    ;; skip the newline
                    (decoder-read-byte decoder)
@@ -198,7 +201,7 @@ value."
              (save (c)
                (saveb (char-code c)))
              (push-next ()
-               (be c (funcall input-function)
+               (let ((c (funcall input-function)))
                  (declare (type (or null character) c))
                  (cond ((not c))
                        ((or (char= c #\space)
@@ -206,7 +209,7 @@ value."
                         (save c)
                         (push-next))
                        ((char= c #\=)
-                        (be c1 (funcall input-function)
+                        (let ((c1 (funcall input-function)))
                           (cond ((not c1)
                                  (save #\=))
                                 ((char= c1 #\return)
@@ -221,7 +224,7 @@ value."
                                  (push-next))
                                 (t
                                  ;; hexadecimal sequence: get the 2nd digit
-                                 (be c2 (funcall input-function)
+                                 (let ((c2 (funcall input-function)))
                                    (if c2
                                        (aif (parse-hex c1 c2)
                                             (saveb it)
@@ -271,10 +274,10 @@ binary output OUT the decoded stream of bytes."
 (defmacro make-stream-to-sequence-decoder (decoder-class input-form &key parser-errors)
   "Decode the character stream STREAM and return a sequence of bytes."
   (with-gensyms (output-sequence)
-    `(be ,output-sequence (make-array 0
-                                      :element-type '(unsigned-byte 8)
-                                      :fill-pointer 0
-                                      :adjustable t)
+    `(let ((,output-sequence (make-array 0
+                                         :element-type '(unsigned-byte 8)
+                                         :fill-pointer 0
+                                         :adjustable t)))
        (make-decoder-loop ,decoder-class ,input-form
                           (vector-push-extend byte ,output-sequence)
                           :parser-errors ,parser-errors)
@@ -377,7 +380,7 @@ characters quoted printables encoded."
 (defun encode-quoted-printable-sequence-to-stream (sequence stream &key (start 0) (end (length sequence)))
   "Encode the sequence of bytes SEQUENCE and write to STREAM a
 quoted printable sequence of characters."
-  (be i start
+  (let ((i start))
     (make-encoder-loop quoted-printable-encoder
      (when (< i end)
        (prog1 (elt sequence i)
@@ -470,7 +473,7 @@ character stream."
 (defun encode-base64-sequence-to-stream (sequence stream &key (start 0) (end (length sequence)))
   "Encode the sequence of bytes SEQUENCE and write to STREAM the
 Base64 character sequence."
-  (be i start
+  (let ((i start))
     (make-encoder-loop base64-encoder
                        (when (< i end)
                          (prog1 (elt sequence i)
@@ -483,60 +486,34 @@ return it."
   (with-output-to-string (out)
     (encode-base64-sequence-to-stream sequence out :start start :end end)))
 
-(defclass base64-decoder (parsing-decoder)
-  ((bitstore :initform 0
-             :type fixnum)
-   (bytecount :initform 0 :type fixnum))
-  (:documentation
-   "Class for Base64 decoder input streams."))
-
-(defmethod decoder-read-byte ((decoder base64-decoder))
-  (declare (optimize (speed 3) (safety 0) (debug 0)))
-  (with-slots (bitstore bytecount input-function) decoder
-    (declare (type fixnum bitstore bytecount)
-             (type function input-function))
-    (labels ((in6 ()
-               (loop
-                  for c = (funcall input-function)
-                  when (or (not c) (char= #\= c))
-                  do (return-from decoder-read-byte nil)
-                  do (be sextet (aref +base64-decode-table+ (char-code c))
-                       (unless (= sextet 65) ; ignore unrecognised characters
-                         (return sextet)))))
-             (push6 (sextet)
-               (declare (type fixnum sextet))
-               (setf bitstore
-                     (logior sextet (the fixnum (ash bitstore 6))))))
-      (case bytecount
-        (0
-         (setf bitstore (in6))
-         (push6 (in6))
-         (setf bytecount 1)
-         (ash bitstore -4))
-        (1
-         (push6 (in6))
-         (setf bytecount 2)
-         (logand #xFF (ash bitstore -2)))
-        (2
-         (push6 (in6))
-         (setf bytecount 0)
-         (logand #xFF bitstore))))))
-
 (defun decode-base64-stream (in out &key parser-errors)
   "Read from IN a stream of characters Base64 encoded and write
 to OUT a stream of decoded bytes."
-  (make-decoder-loop base64-decoder
-                     (read-byte in nil) (write-byte byte out)
-                     :parser-errors parser-errors))
+  ;; parser-errors are ignored for base64
+  (declare (ignore parser-errors))
+  (redirect-stream (make-instance 'qbase64:decode-stream
+                                  :underlying-stream in)
+                   out))
 
 (defun decode-base64-stream-to-sequence (stream &key parser-errors)
-  (make-stream-to-sequence-decoder base64-decoder
-                                   (read-char stream nil)
-                                   :parser-errors parser-errors))
-
-(defun decode-base64-string (string &key (start 0) (end (length string)) parser-errors)
-  (with-input-from-string (in string :start start :end end)
-    (decode-base64-stream-to-sequence in :parser-errors parser-errors)))
+  "Read Base64 characters from STREAM and return result of decoding them as a
+binary sequence."
+  ;; parser-errors are ignored for base64
+  (declare (ignore parser-errors))
+  (let* ((buffered-size 4096)
+         (dstream (make-instance 'qbase64:decode-stream
+                                 :underlying-stream stream))
+         (output-seq (make-array buffered-size
+                                 :element-type '(unsigned-byte 8)
+                                 :adjustable t)))
+    (loop for cap = (array-dimension output-seq 0)
+          for pos = (read-sequence output-seq dstream :start (or pos 0))
+          if (>= pos cap)
+            do (adjust-array output-seq (+ cap buffered-size))
+          else
+            do (progn
+                 (adjust-array output-seq pos)
+                 (return output-seq)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -547,25 +524,14 @@ to OUT a stream of decoded bytes."
      while c
      do (write-byte (char-code c) out)))
 
-(defun decode-stream (in out encoding &key parser-errors-p)
-  (gcase (encoding string-equal)
-    (:quoted-printable
-     (decode-quoted-printable-stream in out
-                                     :parser-errors parser-errors-p))
-    (:base64
-     (decode-base64-stream in out
-                           :parser-errors parser-errors-p))
-    (otherwise
-     (dump-stream-binary in out))))
-
 (defun decode-string (string encoding &key parser-errors-p)
   (gcase (encoding string-equal)
     (:quoted-printable
      (decode-quoted-printable-string string
                                      :parser-errors parser-errors-p))
     (:base64
-     (decode-base64-string string
-                           :parser-errors parser-errors-p))
+     ;; parser-errors-p is unused in base64
+     (qbase64:decode-string string))
     (otherwise
      (map '(vector (unsigned-byte 8)) #'char-code string))))
 
@@ -649,7 +615,7 @@ method of RFC2047 and return a sequence of bytes."
 bytes."
   (gcase (encoding string-equal)
     ("Q" (decode-quoted-printable-RFC2047-string string :start start :end end))
-    ("B" (decode-base64-string string :start start :end end))
+    ("B" (qbase64:decode-string (subseq string start end)))
     (t string)))
 
 (defun parse-RFC2047-text (text)
@@ -684,13 +650,13 @@ sequence, a charset string indicating the original coding."
 
 (defun decode-RFC2047 (text)
   "Decode TEXT into a fully decoded string. Whenever a non ASCII part is
-  encountered, try to decode it using babel, otherwise signal an error."
+  encountered, try to decode it using flexi-streams, otherwise signal an error."
   (flet ((decode-part (part)
            (etypecase part
-             (cons (babel:octets-to-string
+             (cons (flexi-streams:octets-to-string
                     (car part)
-                    :encoding (babel-encodings:get-character-encoding
-                               (intern (string-upcase (cdr part)) 'keyword))))
+                    :external-format (flexi-streams:make-external-format
+                                      (intern (string-upcase (cdr part)) 'keyword))))
              (string part))))
     (apply #'concatenate
            (cons 'string