-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquoted-printable.scm
More file actions
159 lines (141 loc) · 6.55 KB
/
quoted-printable.scm
File metadata and controls
159 lines (141 loc) · 6.55 KB
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
;;;; quoted-printable.scm -- RFC2045 implementation
;;
;; Copyright (c) 2005-2008 Alex Shinn. All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt
;; Procedure: quoted-printable-encode-string str [start-col max-col]
;; Return a quoted-printable encoded representation of string
;; according to the official standard as described in RFC2045.
;;
;; ? and _ are always encoded for compatibility with RFC1522 encoding,
;; and soft newlines are inserted as necessary to keep each lines
;; length less than MAX-COL (default 76). The starting column may be
;; overridden with START-COL (default 0).
;; Procedure: quoted-printable-decode-string str [mime?]
;; Return a quoted-printable decoded representation of string. If
;; MIME? is specified and true, _ will be decoded as as space in
;; accordance with RFC1522. No errors will be raised on invalid
;; input.
;; Procedure: quoted-printable-encode [port start-col max-col]
;; Procedure: quoted-printable-decode [port start-col max-col]
;; Variations of the above which read and write to ports.
;; Procedure: quoted-printable-encode-header enc str [start-col max-col]
;; Return a quoted-printable encoded representation of string as
;; above, wrapped in =?ENC?Q?...?= as per RFC1522, split across
;; multiple MIME-header lines as needed to keep each lines length less
;; than MAX-COL. The string is encoded as is, and the encoding ENC is
;; just used for the prefix, i.e. you are responsible for ensuring STR
;; is already encoded according to ENC.
;; Example:
;; (define (mime-encode-header header value charset)
;; (let ((prefix (string-append header ": "))
;; (str (ces-convert value "UTF8" charset)))
;; (string-append
;; prefix
;; (quoted-printable-encode-header charset str (string-length prefix)))))
;; This API is backwards compatible with the Gauche library
;; rfc.quoted-printable.
(declare (fixnum))
(module quoted-printable
(quoted-printable-encode quoted-printable-encode-string
quoted-printable-encode-header
quoted-printable-decode quoted-printable-decode-string)
(import scheme chicken extras ports data-structures)
(define-constant *default-max-col* 76)
;; Allow for RFC1522 quoting for headers by always escaping ? and _
(define (qp-encode str start-col max-col separator)
(define (hex i) (integer->char (+ i (if (<= i 9) 48 55))))
(let ((end (string-length str))
(buf (make-string max-col)))
(let lp ((i 0) (col start-col) (res '()))
(cond
((= i end)
(if (pair? res)
(string-intersperse (reverse (cons (substring buf 0 col) res))
separator)
(substring buf start-col col)))
((>= col (- max-col 3))
(lp i 0 (cons (substring buf (if (pair? res) 0 start-col) col) res)))
(else
(let ((c (char->integer (string-ref str i))))
(cond
((and (<= 33 c 126) (not (memq c '(61 63 95))))
(string-set! buf col (integer->char c))
(lp (+ i 1) (+ col 1) res))
(else
(string-set! buf col #\=)
(string-set! buf (+ col 1) (hex (fxshr c 4)))
(string-set! buf (+ col 2) (hex (fxand c #b1111)))
(lp (+ i 1) (+ col 3) res)))))))))
(define (quoted-printable-encode-string . o)
(let-optionals* o ((src (current-input-port))
(start-col 0)
(max-col *default-max-col*))
(qp-encode (if (string? src) src (read-string #f src))
start-col max-col "=\r\n")))
(define (quoted-printable-encode . o)
(display (apply quoted-printable-encode-string o)))
(define (quoted-printable-encode-header encoding . o)
(let-optionals* o ((src (current-input-port))
(start-col 0)
(max-col *default-max-col*)
(nl "\r\n"))
(let* ((prefix (string-append "=?" encoding "?Q?"))
(prefix-length (+ 2 (string-length prefix)))
(separator (string-append "?=" nl "\t" prefix))
(effective-max-col (- max-col prefix-length)))
(string-append prefix
(qp-encode (if (string? src) src (read-string #f src))
start-col effective-max-col separator)
"?="))))
(define (quoted-printable-decode-string . o)
(define (hex? c) (or (char-numeric? c) (<= 65 (char->integer c) 70)))
(define (unhex1 c)
(let ((i (char->integer c))) (if (>= i 65) (- i 55) (- i 48))))
(define (unhex c1 c2)
(integer->char (+ (fxshl (unhex1 c1) 4) (unhex1 c2))))
(let-optionals* o ((src (current-input-port))
(mime-header? #f))
(let* ((str (if (string? src) src (read-string #f src)))
(end (string-length str)))
(with-output-to-string
(lambda ()
(let lp ((i 0))
(unless (>= i end)
(let ((c (string-ref str i)))
(case c
((#\=) ; = escapes
(unless (>= (+ i 2) end)
(let ((c2 (string-ref str (+ i 1))))
(cond
((eq? c2 #\newline) (lp (+ i 2)))
((eq? c2 #\return)
(lp (if (eq? (string-ref str (+ i 2)) #\newline)
(+ i 3)
(+ i 2))))
((hex? c2)
(let ((c3 (string-ref str (+ i 2))))
(if (hex? c3) (write-char (unhex c2 c3)))
(lp (+ i 3))))
(else (lp (+ i 3)))))))
((#\_) ; maybe translate _ to space
(write-char (if mime-header? #\space c))
(lp (+ i 1)))
((#\space #\tab) ; strip trailing whitespace
(let lp2 ((j (+ i 1)))
(unless (= j end)
(case (string-ref str j)
((#\space #\tab) (lp2 (+ j 1)))
((#\newline)
(lp (+ j 1)))
((#\return)
(let ((k (+ j 1)))
(lp (if (and (< k end)
(eqv? #\newline (string-ref str k)))
(+ k 1) k))))
(else (display (substring str i j)) (lp j))))))
(else ; a literal char
(write-char c)
(lp (+ i 1))))))))))))
(define (quoted-printable-decode . o)
(display (apply quoted-printable-decode-string o)))
)