Imported upstream version 0.59.3
[hcoop/debian/courier-authlib.git] / rfc822 / encode.c
1 /*
2 ** Copyright 2003-2004 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 */
5
6 /*
7 ** $Id: encode.c,v 1.5 2005/11/16 02:23:15 mrsam Exp $
8 */
9 #include "encode.h"
10 #include <string.h>
11 #include <stdlib.h>
12
13 static int quoted_printable(struct libmail_encode_info *,
14 const char *, size_t);
15 static int base64(struct libmail_encode_info *,
16 const char *, size_t);
17 static int eflush(struct libmail_encode_info *,
18 const char *, size_t);
19
20 void libmail_encode_start(struct libmail_encode_info *info,
21 const char *transfer_encoding,
22 int (*callback_func)(const char *, size_t, void *),
23 void *callback_arg)
24 {
25 info->output_buf_cnt=0;
26 info->input_buf_cnt=0;
27
28 switch (*transfer_encoding) {
29 case 'q':
30 case 'Q':
31 info->encoding_func=quoted_printable;
32 info->input_buffer[0]=0; /* Recycle for qp encoding */
33 break;
34 case 'b':
35 case 'B':
36 info->encoding_func=base64;
37 break;
38 default:
39 info->encoding_func=eflush;
40 break;
41 }
42 info->callback_func=callback_func;
43 info->callback_arg=callback_arg;
44 }
45
46 int libmail_encode(struct libmail_encode_info *info,
47 const char *ptr,
48 size_t cnt)
49 {
50 return ((*info->encoding_func)(info, ptr, cnt));
51 }
52
53 int libmail_encode_end(struct libmail_encode_info *info)
54 {
55 int rc=(*info->encoding_func)(info, NULL, 0);
56
57 if (rc == 0 && info->output_buf_cnt > 0)
58 {
59 rc= (*info->callback_func)(info->output_buffer,
60 info->output_buf_cnt,
61 info->callback_arg);
62 info->output_buf_cnt=0;
63 }
64
65 return rc;
66 }
67
68 static int eflush(struct libmail_encode_info *info, const char *ptr, size_t n)
69 {
70 while (n > 0)
71 {
72 size_t i;
73
74 if (info->output_buf_cnt == sizeof(info->output_buffer))
75 {
76 int rc= (*info->callback_func)(info->output_buffer,
77 info->output_buf_cnt,
78 info->callback_arg);
79
80 info->output_buf_cnt=0;
81 if (rc)
82 return rc;
83 }
84
85 i=n;
86
87 if (i > sizeof(info->output_buffer) - info->output_buf_cnt)
88 i=sizeof(info->output_buffer) - info->output_buf_cnt;
89
90 memcpy(info->output_buffer + info->output_buf_cnt, ptr, i);
91 info->output_buf_cnt += i;
92 ptr += i;
93 n -= i;
94 }
95 return 0;
96 }
97
98 static int base64_flush(struct libmail_encode_info *);
99
100 static int base64(struct libmail_encode_info *info,
101 const char *buf, size_t n)
102 {
103 if (!buf)
104 {
105 int rc=0;
106
107 if (info->input_buf_cnt > 0)
108 rc=base64_flush(info);
109
110 return rc;
111 }
112
113 while (n)
114 {
115 size_t i;
116
117 if (info->input_buf_cnt == sizeof(info->input_buffer))
118 {
119 int rc=base64_flush(info);
120
121 if (rc != 0)
122 return rc;
123 }
124
125 i=n;
126 if (i > sizeof(info->input_buffer) - info->input_buf_cnt)
127 i=sizeof(info->input_buffer) - info->input_buf_cnt;
128
129 memcpy(info->input_buffer + info->input_buf_cnt,
130 buf, i);
131 info->input_buf_cnt += i;
132 buf += i;
133 n -= i;
134 }
135 return 0;
136 }
137
138 static const char base64tab[]=
139 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
140
141 static int base64_flush(struct libmail_encode_info *info)
142 {
143 int a=0,b=0,c=0;
144 int i, j;
145 int d, e, f, g;
146 char output_buf[ sizeof(info->input_buffer) / 3 * 4+1];
147
148 for (j=i=0; i<info->input_buf_cnt; i += 3)
149 {
150 a=(unsigned char)info->input_buffer[i];
151 b= i+1 < info->input_buf_cnt ?
152 (unsigned char)info->input_buffer[i+1]:0;
153 c= i+2 < info->input_buf_cnt ?
154 (unsigned char)info->input_buffer[i+2]:0;
155
156 d=base64tab[ a >> 2 ];
157 e=base64tab[ ((a & 3 ) << 4) | (b >> 4)];
158 f=base64tab[ ((b & 15) << 2) | (c >> 6)];
159 g=base64tab[ c & 63 ];
160 if (i + 1 >= info->input_buf_cnt) f='=';
161 if (i + 2 >= info->input_buf_cnt) g='=';
162 output_buf[j++]=d;
163 output_buf[j++]=e;
164 output_buf[j++]=f;
165 output_buf[j++]=g;
166 }
167
168 info->input_buf_cnt=0;
169
170 output_buf[j++]='\n';
171 return eflush(info, output_buf, j);
172 }
173
174 static const char xdigit[]="0123456789ABCDEF";
175
176 static int quoted_printable(struct libmail_encode_info *info,
177 const char *p, size_t n)
178 {
179 char local_buf[256];
180 int local_buf_cnt=0;
181
182 #define QPUT(c) do { if (local_buf_cnt == sizeof(local_buf)) \
183 { int rc=eflush(info, local_buf, local_buf_cnt); \
184 local_buf_cnt=0; if (rc) return (rc); } \
185 local_buf[local_buf_cnt]=(c); ++local_buf_cnt; } while(0)
186
187 if (!p)
188 return (0);
189
190 while (n)
191 {
192
193
194 /*
195 ** Repurpose input_buffer[0] as a flag whether the previous
196 ** character was a space.
197 **
198 ** A space before a newline gets escaped.
199 */
200
201 if (info->input_buffer[0])
202 {
203 if (*p == '\n')
204 {
205 QPUT('=');
206 QPUT('2');
207 QPUT('0');
208 }
209 else
210 {
211 QPUT(' ');
212 }
213 ++info->input_buf_cnt;
214 }
215
216 info->input_buffer[0]=0;
217
218 if (*p == ' ')
219 {
220 info->input_buffer[0]=1;
221 p++;
222 --n;
223 continue;
224 }
225
226 if (info->input_buf_cnt > 72 && *p != '\n')
227 {
228 QPUT('=');
229 QPUT('\n');
230 info->input_buf_cnt=0;
231 }
232
233 if ( *p == '\n')
234 info->input_buf_cnt=0;
235 else if (*p < ' ' || *p == '=' || *p >= 0x7F)
236 {
237 QPUT('=');
238 QPUT(xdigit[ (*p >> 4) & 15]);
239 QPUT(xdigit[ *p & 15 ]);
240 info->input_buf_cnt += 3;
241 p++;
242 --n;
243 continue;
244 }
245 else info->input_buf_cnt++;
246
247 QPUT( *p);
248 p++;
249 --n;
250 }
251
252 if (local_buf_cnt > 0)
253 return eflush(info, local_buf, local_buf_cnt);
254
255 return 0;
256 }