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