1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* BCP and TBCP filters */
18 #include "stdio_.h"
19 #include "strimpl.h"
20 #include "sbcp.h"
21 
22 #define CtrlA 0x01
23 #define CtrlC 0x03
24 #define CtrlD 0x04
25 #define CtrlE 0x05
26 #define CtrlQ 0x11
27 #define CtrlS 0x13
28 #define CtrlT 0x14
29 #define ESC 0x1b
30 #define CtrlBksl 0x1c
31 
32 /* The following is not used yet. */
33 /*static const char *TBCP_end_protocol_string = "\033%-12345X";*/
34 
35 /* ------ BCPEncode and TBCPEncode ------ */
36 
37 /* Process a buffer */
38 static int
s_xBCPE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last,const byte * escaped)39 s_xBCPE_process(stream_state * st, stream_cursor_read * pr,
40                 stream_cursor_write * pw, bool last, const byte * escaped)
41 {
42     const byte *p = pr->ptr;
43     const byte *rlimit = pr->limit;
44     uint rcount = rlimit - p;
45     byte *q = pw->ptr;
46     uint wcount = pw->limit - q;
47     const byte *end = p + min(rcount, wcount);
48 
49     while (p < end) {
50         byte ch = *++p;
51 
52         if (ch <= 31 && escaped[ch]) {
53             if (p == rlimit) {
54                 p--;
55                 break;
56             }
57             *++q = CtrlA;
58             ch ^= 0x40;
59             if (--wcount < rcount)
60                 end--;
61         }
62         *++q = ch;
63     }
64     pr->ptr = p;
65     pw->ptr = q;
66     return (p == rlimit ? 0 : 1);
67 }
68 
69 /* Actual process procedures */
70 static int
s_BCPE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)71 s_BCPE_process(stream_state * st, stream_cursor_read * pr,
72                stream_cursor_write * pw, bool last)
73 {
74     static const byte escaped[32] =
75     {
76         0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77         0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0
78     };
79 
80     return s_xBCPE_process(st, pr, pw, last, escaped);
81 }
82 static int
s_TBCPE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)83 s_TBCPE_process(stream_state * st, stream_cursor_read * pr,
84                 stream_cursor_write * pw, bool last)
85 {
86     static const byte escaped[32] =
87     {
88         0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89         0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0
90     };
91 
92     return s_xBCPE_process(st, pr, pw, last, escaped);
93 }
94 
95 /* Stream templates */
96 const stream_template s_BCPE_template =
97 {&st_stream_state, NULL, s_BCPE_process, 1, 2
98 };
99 const stream_template s_TBCPE_template =
100 {&st_stream_state, NULL, s_TBCPE_process, 1, 2
101 };
102 
103 /* ------ BCPDecode and TBCPDecode ------ */
104 
105 private_st_BCPD_state();
106 
107 /* Initialize the state */
108 static int
s_BCPD_init(stream_state * st)109 s_BCPD_init(stream_state * st)
110 {
111     stream_BCPD_state *const ss = (stream_BCPD_state *) st;
112 
113     ss->escaped = 0;
114     ss->matched = ss->copy_count = 0;
115     return 0;
116 }
117 
118 /* Process a buffer */
119 static int
s_xBCPD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last,bool tagged)120 s_xBCPD_process(stream_state * st, stream_cursor_read * pr,
121                 stream_cursor_write * pw, bool last, bool tagged)
122 {
123     stream_BCPD_state *const ss = (stream_BCPD_state *) st;
124     const byte *p = pr->ptr;
125     const byte *rlimit = pr->limit;
126     byte *q = pw->ptr;
127     byte *wlimit = pw->limit;
128     int copy_count = ss->copy_count;
129     int status;
130     bool escaped = ss->escaped;
131 
132     for (;;) {
133         byte ch;
134 
135         if (copy_count) {
136             if (q == wlimit) {
137                 status = (p < rlimit ? 1 : 0);
138                 break;
139             }
140             *++q = *++(ss->copy_ptr);
141             copy_count--;
142             continue;
143         }
144         if (p == rlimit) {
145             status = 0;
146             break;
147         }
148         ch = *++p;
149         if (ch <= 31)
150             switch (ch) {
151                 case CtrlA:
152                     if (escaped) {
153                         status = ERRC;
154                         goto out;
155                     }
156                     escaped = true;
157                     continue;
158                 case CtrlC:
159                     status = (*ss->signal_interrupt) (st);
160                     if (status < 0)
161                         goto out;
162                     continue;
163                 case CtrlD:
164                     if (escaped) {
165                         status = ERRC;
166                         goto out;
167                     }
168                     status = EOFC;
169                     goto out;
170                 case CtrlE:
171                     continue;
172                 case CtrlQ:
173                     continue;
174                 case CtrlS:
175                     continue;
176                 case CtrlT:
177                     status = (*ss->request_status) (st);
178                     if (status < 0)
179                         goto out;
180                     continue;
181                 case CtrlBksl:
182                     continue;
183             }
184         if (q == wlimit) {
185             p--;
186             status = 1;
187             break;
188         }
189         if (escaped) {
190             escaped = false;
191             switch (ch) {
192                 case '[':
193                     if (!tagged) {
194                         status = ERRC;
195                         goto out;
196                     }
197                     /* falls through */
198                 case 'A':
199                 case 'C':
200                 case 'D':
201                 case 'E':
202                 case 'Q':
203                 case 'S':
204                 case 'T':
205                 case '\\':
206                     ch ^= 0x40;
207                     break;
208                 case 'M':
209                     if (!tagged) {
210                         status = ERRC;
211                         goto out;
212                     }
213                     continue;
214                 default:
215                     status = ERRC;
216                     goto out;
217             }
218         }
219         *++q = ch;
220     }
221   out:ss->copy_count = copy_count;
222     ss->escaped = escaped;
223     pr->ptr = p;
224     pw->ptr = q;
225     return status;
226 }
227 
228 /* Actual process procedures */
229 static int
s_BCPD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)230 s_BCPD_process(stream_state * st, stream_cursor_read * pr,
231                stream_cursor_write * pw, bool last)
232 {
233     return s_xBCPD_process(st, pr, pw, last, false);
234 }
235 static int
s_TBCPD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)236 s_TBCPD_process(stream_state * st, stream_cursor_read * pr,
237                 stream_cursor_write * pw, bool last)
238 {
239     return s_xBCPD_process(st, pr, pw, last, true);
240 }
241 
242 /* Stream templates */
243 const stream_template s_BCPD_template =
244 {&st_BCPD_state, s_BCPD_init, s_BCPD_process, 1, 1,
245  NULL, NULL, s_BCPD_init
246 };
247 const stream_template s_TBCPD_template =
248 {&st_BCPD_state, s_BCPD_init, s_TBCPD_process, 1, 1,
249  NULL, NULL, s_BCPD_init
250 };
251