Commit ba5f7a3e ba5f7a3e5ed31151a7097fff50871621652e752c by Alain Magloire

attachment.c file_stream.c trans_stream.c

misc function done by D.I.
1 parent bee00121
1 /* GNU mailutils - a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Library Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18 #include <message.h>
19 #include <io.h>
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #define MAX_HDR_LEN 256
27 #define BUF_SIZE 2048
28
29 struct _msg_info {
30 char *buf;
31 size_t nbytes;
32 char *header_buf;
33 int header_len;
34 int header_size;
35 header_t hdr;
36 message_t msg;
37 int ioffset;
38 int ooffset;
39 char line[MAX_HDR_LEN];
40 int line_ndx;
41 };
42
43 #define MSG_HDR "Content-Type: %s\nContent-Transfer-Encoding: %s\n\n"
44
45 int message_create_attachment(const char *content_type, const char *encoding, const char *filename, message_t *newmsg)
46 {
47 header_t hdr;
48 body_t body;
49 stream_t fstream = NULL, tstream = NULL;
50 char *header;
51 int ret;
52
53 if ( ( ret = message_create(newmsg, NULL) ) == 0 ) {
54 if ( content_type == NULL )
55 content_type = "text/plain";
56 if ( encoding == NULL )
57 encoding = "7bit";
58 if ( ( header = alloca(strlen(MSG_HDR) + strlen(content_type) + strlen(encoding)) ) == NULL )
59 ret = ENOMEM;
60 else {
61 sprintf(header, MSG_HDR, content_type, encoding);
62 if ( ( ret = header_create( &hdr, header, strlen(header), *newmsg ) ) == 0 ) {
63 message_get_body(*newmsg, &body);
64 if ( ( ret = file_stream_create(&fstream, filename, MU_STREAM_READ) ) == 0 ) {
65 if ( ( ret = encoder_stream_create(&tstream, fstream, encoding) ) == 0 ) {
66 body_set_stream(body, tstream, *newmsg);
67 message_set_header(*newmsg, hdr, NULL);
68 }
69 }
70 }
71 }
72 }
73 if ( ret ) {
74 if ( *newmsg )
75 message_destroy(newmsg, NULL);
76 if ( hdr )
77 header_destroy(&hdr, NULL);
78 if ( fstream )
79 stream_destroy(&fstream, NULL);
80 }
81 return ret;
82 }
83
84
85 static int _attachment_setup(struct _msg_info **info, message_t msg, stream_t *stream, void **data)
86 {
87 int sfl, ret;
88 body_t body;
89
90 if ( ( ret = message_get_body(msg, &body) ) != 0 ||
91 ( ret = body_get_stream(body, stream) ) != 0 )
92 return ret;
93 stream_get_flags(*stream, &sfl);
94 if ( data == NULL && (sfl & MU_STREAM_NONBLOCK) )
95 return EINVAL;
96 if ( data )
97 *info = *data;
98 if ( *info == NULL ) {
99 if ( ( *info = calloc(1, sizeof(struct _msg_info)) ) == NULL )
100 return ENOMEM;
101 }
102 if ( ( (*info)->buf = malloc(BUF_SIZE) ) == NULL ) {
103 free(*info);
104 return ENOMEM;
105 }
106 return 0;
107 }
108
109 static void _attachment_free(struct _msg_info *info, int free_message) {
110 if ( info->buf )
111 free(info->buf);
112 if ( info->header_buf )
113 free(info->header_buf);
114 if ( free_message ) {
115 if ( info->msg )
116 message_destroy(&(info->msg), NULL);
117 else if ( info->hdr )
118 header_destroy(&(info->hdr), NULL);
119 }
120 free(info);
121 }
122
123 int message_save_attachment(message_t msg, const char *filename, void **data)
124 {
125 stream_t stream;
126 header_t hdr;
127 struct _msg_info *info = NULL;
128 int ret;
129 size_t size;
130 char *content_encoding;
131
132 if ( msg == NULL || filename == NULL)
133 return EINVAL;
134
135 if ( ( data == NULL || *data == NULL) && ( ret = message_get_header(msg, &hdr) ) == 0 ) {
136 header_get_value(hdr, "Content-Transfer-Encoding", NULL, 0, &size);
137 if ( size ) {
138 if ( ( content_encoding = alloca(size+1) ) == NULL )
139 ret = ENOMEM;
140 header_get_value(hdr, "Content-Transfer-Encoding", content_encoding, size+1, 0);
141 }
142 }
143 if ( ret == 0 && ( ret = _attachment_setup( &info, msg, &stream, data) ) != 0 )
144 return ret;
145
146 if ( ret != EAGAIN && info )
147 _attachment_free(info, ret);
148 return ret;
149 }
150
151 #if 0
152 int message_encapsulate(message_t msg, message_t *newmsg, void **data)
153 {
154 stream_t stream;
155 char *header;
156 struct _msg_info *info = NULL;
157 int ret;
158
159 if ( msg == NULL || newmsg == NULL)
160 return EINVAL;
161
162 if ( ( ret = message_create(&(info->msg), NULL) ) == 0 ) {
163 header = "Content-Type: message/rfc822\nContent-Transfer-Encoding: 7bit\n\n";
164 if ( ( ret = header_create( &(info->hdr), header, strlen(header), msg ) ) == 0 ) {
165 message_set_header(info->msg, info->hdr, NULL);
166 }
167 }
168 return ret;
169 }
170 #endif
171
172 /* If the message interface parsed headers on write this would be easy */
173
174 int message_unencapsulate(message_t msg, message_t *newmsg, void **data)
175 {
176 size_t size, nbytes;
177 int ret = 0, header_done = 0;
178 char *content_type, *cp;
179 header_t hdr;
180 body_t body;
181 stream_t istream, ostream;
182 struct _msg_info *info = NULL;
183
184 if ( msg == NULL || newmsg == NULL)
185 return EINVAL;
186
187 if ( (data == NULL || *data == NULL ) && ( ret = message_get_header(msg, &hdr) ) == 0 ) {
188 header_get_value(hdr, "Content-Type", NULL, 0, &size);
189 if ( size ) {
190 if ( ( content_type = alloca(size+1) ) == NULL )
191 ret = ENOMEM;
192 header_get_value(hdr, "Content-Type", content_type, size+1, 0);
193 if ( strncasecmp(content_type, "message/rfc822", strlen(content_type)) != 0 )
194 ret = EINVAL;
195 } else
196 return EINVAL;
197 }
198 if ( ret == 0 && ( ret = _attachment_setup( &info, msg, &istream, data) ) != 0 )
199 return ret;
200
201 if ( ret == 0 && info->hdr == NULL ) {
202 while ( !header_done && ( ret = stream_read(istream, info->buf, BUF_SIZE, info->ioffset, &info->nbytes) ) == 0 && info->nbytes ) {
203 cp = info->buf;
204 while ( info->nbytes && !header_done ) {
205 info->line[info->line_ndx] = *cp;
206 info->line_ndx++;
207 if ( *cp == '\n' ) {
208 if ( info->header_len + info->line_ndx > info->header_size) {
209 char *nhb;
210 if ( ( nhb = realloc( info->header_buf, info->header_len + info->line_ndx + 128 ) ) == NULL ) {
211 header_done = 1;
212 ret = ENOMEM;
213 break;
214 }
215 info->header_buf = nhb;
216 info->header_size = info->header_len + info->line_ndx + 128;
217 }
218 info->header_len += info->line_ndx;
219 memcpy(info->header_buf, info->line, info->line_ndx);
220 if ( info->line_ndx == 1 ) {
221 header_done = 1;
222 break;
223 }
224 info->line_ndx = 0;
225 }
226 info->ioffset++;
227 info->nbytes--;
228 cp++;
229 }
230 }
231 }
232 if ( ret == 0 && info->msg == NULL ) {
233 if ( ( ret = message_create(&(info->msg), NULL) ) == 0)
234 if ( ( ret = header_create(&(info->hdr), info->header_buf, info->header_len, info->msg) ) == 0 )
235 ret = message_set_header(info->msg, hdr, NULL);
236 }
237 if ( ret == 0 ) {
238 message_get_body(info->msg, &body);
239 body_get_stream( body, &ostream);
240 if ( info->nbytes )
241 memmove( info->buf, info->buf + (BUF_SIZE - info->nbytes), info->nbytes);
242 while ( info->nbytes || ( ( ret = stream_read(istream, info->buf, BUF_SIZE, info->ioffset, &info->nbytes) ) == 0 && info->nbytes ) ) {
243 info->ioffset += info->nbytes;
244 while( info->nbytes ) {
245 if ( ( ret = stream_write(ostream, info->buf, info->nbytes, info->ooffset, &nbytes ) ) != 0 )
246 break;
247 info->nbytes -= nbytes;
248 info->ooffset += nbytes;
249 }
250 }
251 }
252 if ( ret != EAGAIN && info )
253 _attachment_free(info, ret);
254 return ret;
255 }
256
1 /* GNU mailutils - a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Library Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <io0.h>
25
26 struct _file_stream
27 {
28 FILE *file;
29 int offset;
30 };
31
32 static void _file_destroy(stream_t stream)
33 {
34 struct _file_stream *fs = stream->owner;
35
36 if ( fs->file )
37 fclose(fs->file);
38 free(fs);
39 }
40
41 static int _file_read(stream_t stream, char *optr, size_t osize, off_t offset, size_t *nbytes)
42 {
43 struct _file_stream *fs = stream->owner;
44
45 if ( fs->offset != offset ) {
46 fseek( fs->file, offset, SEEK_SET );
47 fs->offset = offset;
48 }
49 *nbytes = fread( optr, osize, 1, fs->file);
50 if ( *nbytes == 0 ) {
51 if ( ferror( fs->file ) )
52 return errno;
53 } else
54 fs->offset += *nbytes;
55 return 0;
56 }
57
58
59 static int _file_write(stream_t stream, const char *iptr, size_t isize, off_t offset, size_t *nbytes)
60 {
61 struct _file_stream *fs = stream->owner;
62
63 if ( fs->offset != offset ) {
64 fseek( fs->file, offset, SEEK_SET );
65 fs->offset = offset;
66 }
67 *nbytes = fwrite( iptr, isize, 1, fs->file);
68 if ( *nbytes == 0 ) {
69 if ( ferror( fs->file ) )
70 return errno;
71 } else
72 fs->offset += *nbytes;
73 return 0;
74 }
75
76 int file_stream_create(stream_t *stream, const char *filename, int flags)
77 {
78 struct _file_stream *fs;
79 char *mode;
80 int ret;
81
82 if ( stream == NULL || filename == NULL )
83 return EINVAL;
84
85 if ( ( fs = calloc(sizeof(struct _file_stream), 1) ) == NULL )
86 return ENOMEM;
87
88 if ( ( flags & ( MU_STREAM_READ|MU_STREAM_WRITE ) ) == ( MU_STREAM_READ|MU_STREAM_WRITE ) )
89 mode = "r+b";
90 else if ( flags & MU_STREAM_READ )
91 mode = "rb";
92 else if ( flags & MU_STREAM_WRITE )
93 mode = "wb";
94 else
95 return EINVAL;
96
97 if ( ( fs->file = fopen(filename, mode) ) == NULL ) {
98 ret = errno;
99 free( fs );
100 return ret;
101 }
102 if ( ( ret = stream_create(stream, flags, fs) ) != 0 ) {
103 fclose( fs->file );
104 free( fs );
105 return ret;
106 }
107
108 stream_set_read(*stream, _file_read, fs );
109 stream_set_write(*stream, _file_write, fs );
110 stream_set_destroy(*stream, _file_destroy, fs );
111 return 0;
112 }
113
1 /* GNU mailutils - a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Library Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <io0.h>
25
26 struct _ts_desc {
27 const char *encoding;
28 int (*decode)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
29 int (*encode)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
30 };
31
32 struct _trans_stream
33 {
34 stream_t stream; /* encoder/decoder read/writes data to here */
35 int cur_offset;
36 int offset;
37 char *leftover;
38 int llen;
39 int (*transcoder)(const char *iptr, size_t isize, char *optr, size_t *nbytes);
40 };
41
42 int _base64_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
43 int _base64_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
44 int _qp_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
45 int _qp_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes);
46
47 #define NUM_TRANSCODERS 5
48 struct _ts_desc tslist[NUM_TRANSCODERS] = { { "base64", _base64_decode, _base64_encode},
49 { "quoted-printable", _qp_decode, _qp_encode},
50 { "7bit", NULL, NULL},
51 { "8bit", NULL, NULL},
52 { "binary", NULL, NULL}
53 };
54
55 static void _trans_destroy(stream_t stream)
56 {
57 struct _trans_stream *ts = stream->owner;
58
59 stream_destroy(&(ts->stream), NULL);
60 if ( ts->leftover )
61 free(ts->leftover);
62 free(ts);
63 }
64
65 static int _trans_read(stream_t stream, char *optr, size_t osize, off_t offset, size_t *nbytes)
66 {
67 struct _trans_stream *ts = stream->owner;
68 size_t isize = osize;
69 char *iptr;
70 int consumed, ret;
71
72 if ( nbytes == NULL || optr == NULL || osize == 0 )
73 return EINVAL;
74
75 *nbytes = 0;
76
77 if ( offset && ts->offset != offset )
78 return ESPIPE;
79 if ( offset == 0 )
80 ts->cur_offset = 0;
81 if ( ( iptr = alloca(isize) ) == NULL )
82 return ENOMEM;
83 if ( ts->leftover ) {
84 memcpy( iptr, ts->leftover, ts->llen);
85 free( ts->leftover );
86 ts->leftover = NULL;
87 ts->offset = 0; // encase of error;
88 }
89 if ( ( ret = stream_read(ts->stream, iptr + ts->llen, isize - ts->llen, ts->cur_offset, &osize) ) != 0 )
90 return ret;
91 ts->cur_offset += osize;
92 consumed = ts->transcoder(iptr, isize, optr, nbytes);
93 if ( ts->llen = (isize - consumed ) ) {
94 if ( ( ts->leftover = malloc(ts->llen) ) == NULL )
95 return ENOMEM;
96 memcpy(ts->leftover, iptr + consumed, ts->llen);
97 }
98 ts->offset = offset;
99 return 0;
100 }
101
102
103 static int _trans_write(stream_t stream, const char *iptr, size_t isize, off_t offset, size_t *nbytes)
104 {
105 struct _trans_stream *ts = stream->owner;
106 size_t osize = isize;
107 char *optr;
108 int ret;
109
110 if ( nbytes == NULL || iptr == NULL || isize == 0 )
111 return EINVAL;
112
113 *nbytes = 0;
114
115 if ( offset && ts->offset != offset )
116 return ESPIPE;
117 if ( offset == 0 )
118 ts->cur_offset = 0;
119 if ( ( optr = alloca(osize) ) == NULL )
120 return ENOMEM;
121
122 *nbytes = ts->transcoder(iptr, isize, optr, &osize);
123 if ( ( ret = stream_write(ts->stream, optr, osize, ts->cur_offset, &osize) ) != 0 )
124 return ret;
125
126 ts->cur_offset += osize;
127
128 return 0;
129 }
130
131 int encoder_stream_create(stream_t *stream, stream_t iostream, const char *encoding)
132 {
133 struct _trans_stream *ts;
134 int i, ret;
135
136 if ( stream == NULL || iostream == NULL || encoding == NULL )
137 return EINVAL;
138
139 if ( ( ts = calloc(sizeof(struct _trans_stream), 1) ) == NULL )
140 return ENOMEM;
141 for( i = 0; i < NUM_TRANSCODERS; i++ ) {
142 if ( strcasecmp( encoding, tslist[i].encoding ) == 0 )
143 break;
144 }
145 if ( i == NUM_TRANSCODERS )
146 return ENOTSUP;
147
148 if ( ( ret = stream_create(stream, MU_STREAM_RDWR, ts) ) != 0 )
149 return ret;
150 ts->transcoder = tslist[i].encode;
151 stream_set_read(*stream, _trans_read, ts );
152 stream_set_write(*stream, _trans_write, ts );
153 stream_set_destroy(*stream, _trans_destroy, ts );
154 ts->stream = iostream;
155 return 0;
156 }
157
158 int decoder_stream_create(stream_t *stream, stream_t iostream, const char *encoding)
159 {
160 struct _trans_stream *ts;
161 int i, ret;
162
163 if ( stream == NULL || iostream == NULL || encoding == NULL )
164 return EINVAL;
165
166 if ( ( ts = calloc(sizeof(struct _trans_stream), 1) ) == NULL )
167 return ENOMEM;
168 for( i = 0; i < NUM_TRANSCODERS; i++ ) {
169 if ( strcasecmp( encoding, tslist[i].encoding ) == 0 )
170 break;
171 }
172 if ( i == NUM_TRANSCODERS )
173 return ENOTSUP;
174
175 if ( ( ret = stream_create(stream, MU_STREAM_RDWR, ts) ) != 0 )
176 return ret;
177 ts->transcoder = tslist[i].decode;
178 stream_set_read(*stream, _trans_read, ts );
179 stream_set_write(*stream, _trans_write, ts );
180 stream_set_destroy(*stream, _trans_destroy, ts );
181 ts->stream = iostream;
182 return 0;
183 }
184
185 /*------------------------------------------------------
186 * base64 encode/decode
187 *----------------------------------------------------*/
188 static int _b64_input(char c)
189 {
190 const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
191 int i;
192
193 for (i = 0; i < 64; i++) {
194 if (table[i] == c)
195 return i;
196 }
197 return -1;
198 }
199
200 static int _b64_output(int index)
201 {
202 const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
203
204 if (index < 64)
205 return table[index];
206 return -1;
207 }
208
209 int _base64_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
210 {
211 int i, tmp = 0;
212 int consumed = 0;
213 char data[4];
214
215 while ( consumed < isize ) {
216 while ( ( i < 4 ) && ( consumed < isize ) ) {
217 tmp = _b64_input(*iptr++);
218 consumed++;
219 if ( tmp != -1 )
220 data[i++] = tmp;
221 }
222 if ( i == 4 ) { // I have a entire block of data 32 bits
223 // get the output data
224 *optr++ = ( data[0] << 2 ) | ( ( data[1] & 0x30 ) >> 4 );
225 *optr++ = ( ( data[1] & 0xf ) << 4 ) | ( ( data[2] & 0x3c ) >> 2 );
226 *optr++ = ( ( data[2] & 0x3 ) << 6 ) | data[3];
227 (*nbytes) += 3;
228 }
229 else // I did not get all the data
230 return consumed;
231 i = 0;
232 }
233 return consumed;
234 }
235
236 int _base64_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
237 {
238 int consumed = 0;
239
240 while (consumed < (isize - 3) && (*nbytes + 4) < isize) {
241 *optr++ = _b64_output(*iptr >> 2);
242 *optr++ = _b64_output(((*iptr++ & 0x3) << 4) | ((*iptr & 0xf0) >> 4));
243 *optr++ = _b64_output(((*iptr++ & 0xf) << 2) | ((*iptr & 0xc0) >> 6));
244 *optr++ = _b64_output(*iptr++ & 0x3f);
245 consumed += 3;
246 (*nbytes) += 4;
247 }
248 return consumed;
249 }
250
251 /*------------------------------------------------------
252 * quoted-printable decoder/encoder
253 *------------------------------------------------------*/
254 static const char _hexdigits[16] = "0123456789ABCDEF";
255
256 static int _ishex(int c)
257 {
258 int i;
259
260 if ((c == 0x0a) || (c == 0x0d))
261 return 1;
262
263 for (i = 0; i < 16; i++)
264 if (c == _hexdigits[i])
265 return 1;
266
267 return 0;
268 }
269
270 int _qp_decode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
271 {
272 char c;
273 int last_char = 0, consumed = 0;
274
275 while (consumed < isize) {
276 c = *iptr++;
277 if ( ((c >= 33) && (c <= 60)) || ((c >= 62) && (c <= 126)) || ((c == '=') && !_ishex(*iptr)) ) {
278 *optr++ = c;
279 (*nbytes)++;
280 consumed++;
281 }
282 else if (c == '=') {
283 // there must be 2 more characters before I consume this
284 if ((isize - consumed) < 3) {
285 return consumed;
286 }
287 else {
288 // you get =XX where XX are hex characters
289 char chr[2];
290 int new_c;
291
292 chr[0] = *iptr++;
293 if (chr[0] != 0x0a) { // LF after = means soft line break, ignore it
294 chr[1] = *iptr++;
295 new_c = strtoul(chr, NULL, 16);
296 if (new_c == '\r')
297 new_c = '\n';
298 *optr++ = new_c;
299 (*nbytes)++;
300 consumed += 3;
301 }
302 else
303 consumed += 2;
304 }
305 }
306 else if (c == 0x0d) { // CR character
307 // there must be at least 1 more character before I consume this
308 if ((isize - consumed) < 2)
309 return (consumed);
310 else {
311 iptr++; // skip the LF character
312 *optr++ = '\n';
313 (*nbytes)++;
314 consumed += 2;
315 }
316 }
317 else if ((c == 9) || (c == 32)) {
318 if ((last_char == 9) || (last_char == 32))
319 consumed++;
320 else {
321 *optr++ = c;
322 (*nbytes)++;
323 consumed++;
324 }
325 }
326 last_char = c;
327 }
328 return consumed;
329 }
330
331
332 #define QP_LINE_MAX 76
333 int _qp_encode(const char *iptr, size_t isize, char *optr, size_t *nbytes)
334 {
335 int count = 0;
336 int consumed = 0, c;
337
338 while (consumed < isize && (*nbytes + 4) < isize) {
339 if (count == QP_LINE_MAX) {
340 *optr++ = '=';
341 *optr++ = '\n';
342 (*nbytes) += 2;
343 count = 0;
344 }
345
346 c = *iptr++;
347 consumed++;
348 if ( ((c >= 32) && (c <= 60)) || ((c >= 62) && (c <= 126)) || (c == 9)) {
349 *optr++ = c;
350 (*nbytes)++;
351 count++;
352 }
353 else {
354 if (count >= (QP_LINE_MAX - 3)) {
355 // add spaces
356 while (count < QP_LINE_MAX) {
357 *optr++ = ' ';
358 (*nbytes)++;
359 count++;
360 }
361 consumed--;
362 iptr--;
363 }
364 else {
365 *optr++ = '=';
366 *optr++ = _hexdigits[c & 0xf];
367 *optr++ = _hexdigits[(c/16) & 0xf];
368 (*nbytes) += 3;
369 count += 3;
370 }
371 }
372 }
373 return consumed;
374 }