Use the same mh_hostname() function from test/common.h in mhsign(1)
[mmh] / uip / mhoutsbr.c
1 /*
2 ** mhoutsbr.c -- routines to output MIME messages
3 **            -- given a Content structure
4 **
5 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
6 ** COPYRIGHT file in the root directory of the nmh distribution for
7 ** complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <h/tws.h>
14 #include <h/mime.h>
15 #include <h/mhparse.h>
16
17
18 static char nib2b64[0x40+1] =
19         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
20
21 /*
22 ** prototypes
23 */
24 int output_message(CT, char *);
25 int output_message_fp(CT, FILE *, char *);
26
27 /*
28 ** static prototypes
29 */
30 static int output_content(CT, FILE *);
31 static void output_headers(CT, FILE *);
32 static int write8Bit(CT, FILE *);
33 static int writeQuoted(CT, FILE *);
34 static int writeBase64(CT, FILE *);
35 static int writeBase64aux(FILE *, FILE *);
36
37
38 /*
39 ** Main routine to output a MIME message contained
40 ** in a Content structure, to a file.  Any necessary
41 ** transfer encoding is added.
42 */
43
44 int
45 output_message_fp(CT ct, FILE *fp, char *file)
46 {
47         if (output_content(ct, fp) == NOTOK)
48                 return NOTOK;
49
50         if (fflush(fp)) {
51                 advise((file?file:"<FILE*>"), "error writing to");
52                 return NOTOK;
53         }
54         return OK;
55 }
56
57 int
58 output_message(CT ct, char *file)
59 {
60         FILE *fp;
61         int status;
62
63         if ((fp = fopen(file, "w")) == NULL) {
64                 advise(file, "unable to open for writing");
65                 return NOTOK;
66         }
67         status = output_message_fp(ct, fp, file);
68         fclose(fp);
69         return status;
70 }
71
72
73 /*
74 ** Output a Content structure to a file.
75 */
76
77 static int
78 output_content(CT ct, FILE *out)
79 {
80         int result = 0;
81         CI ci = &ct->c_ctinfo;
82
83         /*
84         ** Output all header fields for this content
85         */
86         output_headers(ct, out);
87
88         /*
89         ** Now output the content bodies.
90         */
91         switch (ct->c_type) {
92         case CT_MULTIPART:
93         {
94                 struct multipart *m;
95                 struct part *part;
96
97                 m = (struct multipart *) ct->c_ctparams;
98                 for (part = m->mp_parts; part; part = part->mp_next) {
99                         CT p = part->mp_part;
100
101                         fprintf(out, "\n--%s\n", ci->ci_values[0]);
102                         if (output_content(p, out) == NOTOK)
103                                 return NOTOK;
104                 }
105                 fprintf(out, "\n--%s--\n", ci->ci_values[0]);
106         }
107         break;
108
109         case CT_MESSAGE:
110                 putc('\n', out);
111                 result = write8Bit(ct, out);
112                 break;
113
114         /*
115         ** Handle discrete types (text/application/audio/image/video)
116         */
117         default:
118                 switch (ct->c_encoding) {
119                 case CE_7BIT:
120                         putc('\n', out);
121                         result = write8Bit(ct, out);
122                         break;
123
124                 case CE_8BIT:
125                         putc('\n', out);
126                         result = write8Bit(ct, out);
127                         break;
128
129                 case CE_QUOTED:
130                         putc('\n', out);
131                         result = writeQuoted(ct, out);
132                         break;
133
134                 case CE_BASE64:
135                         putc('\n', out);
136                         result = writeBase64(ct, out);
137                         break;
138
139                 case CE_BINARY:
140                         advise(NULL, "can't handle binary transfer encoding in content");
141                         result = NOTOK;
142                         break;
143
144                 default:
145                         advise(NULL, "unknown transfer encoding in content");
146                         result = NOTOK;
147                         break;
148                 }
149                 break;
150         }
151
152         return result;
153 }
154
155
156 /*
157 ** Output all the header fields for a content
158 */
159
160 static void
161 output_headers(CT ct, FILE *out)
162 {
163         HF hp;
164
165         hp = ct->c_first_hf;
166         while (hp) {
167                 fprintf(out, "%s:%s", hp->name, hp->value);
168                 hp = hp->next;
169         }
170 }
171
172
173 /*
174 ** Output a content without any transfer encoding
175 */
176
177 static int
178 write8Bit(CT ct, FILE *out)
179 {
180         int fd;
181         char c, *file, buffer[BUFSIZ];
182         CE ce = ct->c_cefile;
183
184         file = NULL;
185         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
186                 return NOTOK;
187
188         c = '\n';
189         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
190                 c = buffer[strlen(buffer) - 1];
191                 fputs(buffer, out);
192         }
193         if (c != '\n')
194                 putc('\n', out);
195
196         (*ct->c_ceclosefnx) (ct);
197         return OK;
198 }
199
200
201 /*
202 ** Output a content using quoted-printable
203 */
204
205 static int
206 writeQuoted(CT ct, FILE *out)
207 {
208         int fd;
209         char *cp, *file;
210         char c, buffer[BUFSIZ];
211         CE ce = ct->c_cefile;
212
213         file = NULL;
214         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
215                 return NOTOK;
216
217         while (fgets(buffer, sizeof(buffer) - 1, ce->ce_fp)) {
218                 int n;
219
220                 cp = buffer + strlen(buffer) - 1;
221                 if ((c = *cp) == '\n')
222                         *cp = '\0';
223
224                 if (strncmp(cp = buffer, "From ", sizeof("From ") - 1) == 0) {
225                         fprintf(out, "=%02X", *cp++ & 0xff);
226                         n = 3;
227                 } else {
228                         n = 0;
229                 }
230                 for (; *cp; cp++) {
231                         if (n + 1 >= CPERLIN) {
232                                 fputs("=\n", out);
233                                 n = 0;
234                         }
235
236                         switch (*cp) {
237                         case ' ':
238                         case '\t':
239                                 putc(*cp, out);
240                                 n++;
241                                 break;
242
243                         default:
244                                 if (*cp < '!' || *cp > '~') {
245                                         goto three_print;
246                                 }
247                                 if (n == 0 && *cp == '.') {
248                                         /*
249                                         ** encode dot at start of line,
250                                         ** because it could be alone ...
251                                         */
252                                         goto three_print;
253                                 }
254                                 putc(*cp, out);
255                                 n++;
256                                 break;
257
258                         case '=':
259 three_print:
260                                 fprintf(out, "=%02X", *cp & 0xff);
261                                 n += 3;
262                                 break;
263                         }
264                 }
265
266                 if (c == '\n') {
267                         if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
268                                 fputs("=\n", out);
269
270                         putc('\n', out);
271                 } else {
272                         fputs("=\n", out);
273                 }
274         }
275
276         (*ct->c_ceclosefnx) (ct);
277         return OK;
278 }
279
280
281 /*
282 ** Output a content using base64
283 */
284
285 static int
286 writeBase64(CT ct, FILE *out)
287 {
288         int fd, result;
289         char *file;
290         CE ce = ct->c_cefile;
291
292         file = NULL;
293         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
294                 return NOTOK;
295
296         result = writeBase64aux(ce->ce_fp, out);
297         (*ct->c_ceclosefnx) (ct);
298         return result;
299 }
300
301
302 static int
303 writeBase64aux(FILE *in, FILE *out)
304 {
305         unsigned int cc, n;
306         char inbuf[3];
307
308         n = BPERLIN;
309         while ((cc = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), in)) > 0) {
310                 unsigned long bits;
311                 char *bp;
312                 char outbuf[4];
313
314                 if (cc < sizeof(inbuf)) {
315                         inbuf[2] = 0;
316                         if (cc < sizeof(inbuf) - 1)
317                                 inbuf[1] = 0;
318                 }
319                 bits = (inbuf[0] & 0xff) << 16;
320                 bits |= (inbuf[1] & 0xff) << 8;
321                 bits |= inbuf[2] & 0xff;
322
323                 for (bp = outbuf + sizeof(outbuf); bp > outbuf; bits >>= 6)
324                         *--bp = nib2b64[bits & 0x3f];
325                 if (cc < sizeof(inbuf)) {
326                         outbuf[3] = '=';
327                         if (cc < sizeof inbuf - 1)
328                                 outbuf[2] = '=';
329                 }
330
331                 fwrite(outbuf, sizeof(*outbuf), sizeof(outbuf), out);
332
333                 if (cc < sizeof(inbuf)) {
334                         putc('\n', out);
335                         return OK;
336                 }
337
338                 if (--n <= 0) {
339                         n = BPERLIN;
340                         putc('\n', out);
341                 }
342         }
343         if (n != BPERLIN)
344                 putc('\n', out);
345
346         return OK;
347 }