1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
(in-package :araneida)
;;;; This assumes an ASCII-based character set. It's not the fastest
;;;; base64 decoder in the world either, I expect - but presently it's
;;;; only used for http basic authentication header decoding
(defun base64-decode-quad (q)
(declare (optimize (speed 3)))
(labels ((lookup (c)
(or
(position c
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") 0)))
(destructuring-bind (q1 q2 q3 q4 &rest ignored) q
(declare (ignore ignored))
(let ((val (coerce 0 '(unsigned-byte 32))))
(setf (ldb (byte 6 0) val) (lookup q4))
(setf (ldb (byte 6 6) val) (lookup q3))
(setf (ldb (byte 6 12) val) (lookup q2))
(setf (ldb (byte 6 18) val) (lookup q1))
(let ((v1 (ldb (byte 8 16) val))
(v2 (ldb (byte 8 8) val))
(v3 (ldb (byte 8 0) val)))
(list (code-char v1) (code-char v2) (code-char v3)))))))
(defun base64-decode (string)
"Decode the MIME Base64-encoded data in STRING, as per RFC1521 section 5.2"
;; (1) Delete all the characters not in the set [A-Za-z0-9+=]
;; (2) assert string length is a multiple of 4
;; (3) strip all #\= from end of string
;; (4) assign each byte a number 0-63 based on its position in the
;; set A-Za-z0-9+/
;; (5) iterate over the string in 4-character chunks. For each
;; chunk, output 3 characters
(let ((len (truncate (* (or (position #\= string) (length string)) .75))))
(subseq
(coerce
(loop for q on (coerce string 'list) by #'cddddr
for out-chunk = (base64-decode-quad q)
append out-chunk)
'string)
0 len)))
|