/[MITgcm]/MITgcm/tools/mpack-1.6/uudecode.c
ViewVC logotype

Contents of /MITgcm/tools/mpack-1.6/uudecode.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download)
Tue Aug 26 20:45:25 2003 UTC (20 years, 8 months ago) by edhill
Branch: MAIN
CVS Tags: checkpoint58l_post, checkpoint51k_post, checkpoint57t_post, checkpoint57o_post, checkpoint52l_pre, checkpoint52e_pre, hrcube4, checkpoint58e_post, mitgcm_mapl_00, checkpoint52n_post, checkpoint52j_post, checkpoint53d_post, checkpoint58u_post, checkpoint58w_post, checkpoint54a_pre, checkpoint51o_pre, checkpoint57m_post, checkpoint55c_post, checkpoint54e_post, checkpoint52e_post, checkpoint57s_post, checkpoint51n_pre, checkpoint54a_post, checkpoint53c_post, checkpoint57k_post, checkpoint55d_pre, checkpoint57d_post, checkpoint51l_post, checkpoint57g_post, checkpoint51q_post, checkpoint57b_post, checkpoint57c_pre, checkpoint51j_post, checkpoint58r_post, checkpoint55j_post, checkpoint56b_post, checkpoint57i_post, checkpoint57y_post, hrcube_1, checkpoint57e_post, branch-netcdf, checkpoint52d_pre, checkpoint52l_post, checkpoint55h_post, checkpoint58n_post, checkpoint53b_post, checkpoint58x_post, checkpoint52k_post, checkpoint52b_pre, checkpoint57g_pre, checkpoint54b_post, checkpoint53b_pre, checkpoint55b_post, checkpoint58t_post, checkpoint58h_post, checkpoint54d_post, checkpoint56c_post, checkpoint52m_post, checkpoint57y_pre, checkpoint55, checkpoint53a_post, checkpoint57f_pre, checkpoint57a_post, checkpoint54, checkpoint58q_post, checkpoint54f_post, checkpoint57v_post, checkpoint55g_post, checkpoint51o_post, checkpoint51p_post, checkpoint58j_post, checkpoint52a_pre, checkpoint59e, checkpoint59d, checkpoint59g, checkpoint59f, checkpoint59a, checkpoint55f_post, checkpoint59c, checkpoint59b, checkpoint59m, checkpoint59l, checkpoint59n, checkpoint59i, checkpoint59h, checkpoint59k, checkpoint59j, checkpoint57r_post, checkpoint59, checkpoint58, checkpoint57a_pre, checkpoint55i_post, checkpoint57, checkpoint56, checkpoint51i_post, checkpoint53, checkpoint52, checkpoint51f_post, checkpoint52d_post, eckpoint57e_pre, checkpoint51r_post, checkpoint52a_post, checkpoint57h_done, checkpoint58f_post, checkpoint52b_post, checkpoint53g_post, checkpoint52f_post, branchpoint-genmake2, checkpoint57x_post, checkpoint57n_post, checkpoint52c_post, checkpoint58d_post, checkpoint58c_post, checkpoint57w_post, checkpoint57p_post, checkpint57u_post, checkpoint57f_post, checkpoint58a_post, checkpoint51h_pre, checkpoint51l_pre, checkpoint58i_post, checkpoint57q_post, checkpoint51g_post, checkpoint58g_post, ecco_c52_e35, hrcube5, checkpoint58o_post, checkpoint57z_post, checkpoint57c_post, checkpoint58y_post, checkpoint55e_post, checkpoint58k_post, checkpoint52i_post, checkpoint52j_pre, checkpoint58v_post, checkpoint53f_post, checkpoint55a_post, checkpoint51t_post, checkpoint53d_pre, checkpoint54c_post, checkpoint58s_post, checkpoint58p_post, checkpoint51n_post, checkpoint57j_post, checkpoint58b_post, checkpoint57h_pre, checkpoint51i_pre, checkpoint58m_post, checkpoint57l_post, checkpoint52i_pre, checkpoint51u_post, checkpoint52h_pre, checkpoint52f_pre, checkpoint57h_post, hrcube_2, hrcube_3, checkpoint56a_post, checkpoint51m_post, checkpoint51s_post, checkpoint55d_post
Branch point for: branch-nonh, branch-genmake2, tg2-branch, checkpoint51n_branch, netcdf-sm0
File MIME type: text/plain
Initial check-in of the CMU "mpack" utility.  This allows us to (portably)
send MITgcm output as MIME-encoded email messages and will be used by the
"testreport" script.  The CMU license is basically an "AS-IS" statement.

1 /* (C) Copyright 1993,1994 by Carnegie Mellon University
2 * All Rights Reserved.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of Carnegie
9 * Mellon University not be used in advertising or publicity
10 * pertaining to distribution of the software without specific,
11 * written prior permission. Carnegie Mellon University makes no
12 * representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied
14 * warranty.
15 *
16 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
17 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
19 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
21 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * SOFTWARE.
24 */
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include "xmalloc.h"
29 #include "common.h"
30 #include "part.h"
31
32 extern char *os_idtodir(char *id);
33 extern FILE *os_newtypedfile(char *fname, char *contentType, int flags, params contentParams);
34 extern FILE *os_createnewfile(char *fname);
35
36 static FILE *startDescFile(char *fname);
37
38
39 /* Length of a normal uuencoded line, including newline */
40 #define UULENGTH 62
41
42 /*
43 * Table of valid boundary characters
44 *
45 * XXX: Old versions of Mark Crispin's c-client library
46 * generate boundaries which contain the syntactically
47 * illegal character '#'. It is marked in this table with
48 * a 2 in case we want to use this table in the future to
49 * complain about bad syntax.
50 *
51 */
52 static char bchar[256] = {
53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 1, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1,
56 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1,
57 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
58 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
59 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69 };
70
71 /*
72 * Read an input file, looking for data in split-uuencode format
73 */
74 int handleUuencode(struct part *inpart, char *subject, int extractText)
75 {
76 char *fname = 0, *tmpfname;
77 int part, nparts;
78 int tmppart, tmpnparts;
79 char buf[1024], buf2[1024];
80 char fnamebuf[80];
81 char *boundary_end, *p;
82 int wantdescfile = 0;
83 FILE *descfile = 0;
84
85 /* Scan "Subject:" header for filename/part information */
86 if (parseSubject(subject, &fname, &part, &nparts) != 0) {
87 part = -1;
88 }
89 if (part == 0) {
90 return saveUuFile(inpart, fname, part, nparts, (char *)0);
91 }
92 if (part == 1) {
93 wantdescfile = 1;
94 }
95
96 /* Scan body for interesting lines */
97 while (part_gets(buf, sizeof(buf), inpart)) {
98 /* Uuencode "begin" line */
99 if (!strncmp(buf, "begin ", 6) &&
100 isdigit(buf[6]) && isdigit(buf[7]) && isdigit(buf[8]) &&
101 buf[9] == ' ') {
102 if (part == -1) {
103 /*
104 * We have no part N of M information. Perhaps it is
105 * a single-part uuencoded file.
106 */
107 return saveUuFile(inpart, (char *)0, 1, 0, buf);
108 }
109 else {
110 if (descfile) fclose(descfile);
111 return saveUuFile(inpart, fname, part, nparts, buf);
112 }
113 }
114 else if (!strncmp(buf, "section ", 8) && isdigit(buf[8])) {
115 tmppart = 0;
116 for (p = buf+8; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
117 if (tmppart == 0) continue;
118 if (strncmp(p, " of ", 4) == 0) {
119 /*
120 * "section N of ... of file F ..."
121 */
122 for (p += 4; *p && strncmp(p, " of file ", 9) != 0; p++);
123 if (!*p) continue;
124 p += 9;
125 tmpfname = p;
126 p = strchr(p, ' ');
127 if (!p) continue;
128 *p = '\0';
129 if (descfile) fclose(descfile);
130 return saveUuFile(inpart, tmpfname, tmppart, 0, (char *)0);
131 }
132 else if (*p == '/' && isdigit(p[1])) {
133 /*
134 * "section N/M file F ..."
135 */
136 tmpnparts = 0;
137 for (p++; isdigit(*p); p++) {
138 tmpnparts = tmpnparts*10 + *p - '0';
139 }
140 while (*p && isspace(*p)) p++;
141 if (tmppart > tmpnparts || strncmp(p, "file ", 5) != 0) {
142 continue;
143 }
144 tmpfname = p+5;
145 p = strchr(tmpfname, ' ');
146 if (!p) continue;
147 *p = '\0';
148 if (descfile) fclose(descfile);
149 return saveUuFile(inpart, tmpfname, tmppart, tmpnparts,
150 (char *)0);
151 }
152 }
153 else if (!strncmp(buf, "POST V", 6)) {
154 /*
155 * "POST Vd.d.d F (Part N/M)"
156 */
157 p = strchr(buf+6, ' ');
158 if (!p) continue;
159 tmpfname = p+1;
160 p = strchr(tmpfname, ' ');
161 if (!p || strncmp(p, " (Part ", 7) != 0) continue;
162 *p = '\0';
163 p += 7;
164 tmppart = 0;
165 while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
166 if (tmppart == 0 || *p++ != '/') continue;
167 tmpnparts = 0;
168 while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
169 if (tmppart > tmpnparts || *p != ')') continue;
170 if (descfile) fclose(descfile);
171 return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
172 }
173 else if (!strncmp(buf, "File: ", 6)) {
174 /*
175 * "File: F -- part N of M -- ...
176 */
177 tmpfname = buf+6;
178 p = strchr(tmpfname, ' ');
179 if (!p || strncmp(p, " -- part ", 9) != 0) continue;
180 *p = '\0';
181 p += 9;
182 tmppart = 0;
183 while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
184 if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
185 p += 4;
186 tmpnparts = 0;
187 while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
188 if (tmppart > tmpnparts || strncmp(p, " -- ", 4) != 0) continue;
189 if (descfile) fclose(descfile);
190 return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
191 }
192 else if (!strncmp(buf, "[Section: ", 10)) {
193 /*
194 * "[Section: N/M File: F ..."
195 */
196 tmppart = 0;
197 for (p = buf+10; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
198 if (tmppart == 0) continue;
199 tmpnparts = 0;
200 for (p++; isdigit(*p); p++) {
201 tmpnparts = tmpnparts*10 + *p - '0';
202 }
203 while (*p && isspace(*p)) p++;
204 if (tmppart > tmpnparts || strncmp(p, "File: ", 6) != 0) {
205 continue;
206 }
207 tmpfname = p+6;
208 p = strchr(tmpfname, ' ');
209 if (!p) continue;
210 *p = '\0';
211 if (descfile) fclose(descfile);
212 return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
213 }
214 else if (*buf == '[') {
215 /*
216 * "[F ... - part N of M]"
217 * (usual BinHex practice)
218 */
219 tmpfname = buf+1;
220 p = strchr(tmpfname, ' ');
221 if (!p) continue;
222 *p++ = '\0';
223 while (p && strncmp(p, "- part ", 7) != 0) {
224 p = strchr(p+1, '-');
225 }
226 if (!p) continue;
227 p += 7;
228 tmppart = 0;
229 while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
230 if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
231 p += 4;
232 tmpnparts = 0;
233 while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
234 if (tmppart > tmpnparts || *p != ']') continue;
235 if (descfile) fclose(descfile);
236 return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
237 }
238 else if (fname && part > 0 && nparts > 0 && part <= nparts &&
239 (!strncmp(buf, "BEGIN", 5) ||
240 !strncmp(buf, "--- BEGIN ---", 12) ||
241 (buf[0] == 'M' && strlen(buf) == UULENGTH))) {
242 /*
243 * Found the start of a section of uuencoded data
244 * and have the part N of M information.
245 */
246 if (descfile) fclose(descfile);
247 return saveUuFile(inpart, fname, part, nparts, buf);
248 }
249 else if (!strncasecmp(buf, "x-file-name: ", 13)) {
250 for (p = buf + 13; *p && !isspace(*p); p++);
251 *p = '\0';
252 strncpy(fnamebuf, buf+13, sizeof(fnamebuf)-1);
253 fnamebuf[sizeof(fnamebuf)-1] = '\0';
254 fname = fnamebuf;
255 continue;
256 }
257 else if (!strncasecmp(buf, "x-part: ", 8)) {
258 tmppart = atoi(buf+8);
259 if (tmppart > 0) part = tmppart;
260 continue;
261 }
262 else if (!strncasecmp(buf, "x-part-total: ", 14)) {
263 tmpnparts = atoi(buf+14);
264 if (tmpnparts > 0) nparts = tmpnparts;
265 continue;
266 }
267 else if (part == 1 && fname && !descfile &&
268 !strncasecmp(buf, "x-file-desc: ", 13)) {
269 if (descfile = startDescFile(fname)) {
270 fputs(buf+13, descfile);
271 fclose(descfile);
272 descfile = 0;
273 }
274 continue;
275 }
276 else if (!strcmp(buf,
277 "(This file must be converted with BinHex 4.0)\n")) {
278 if (descfile) fclose(descfile);
279 return os_binhex(inpart, 1, 1);
280 }
281 else if (!strncasecmp(buf, "content-", 8)) {
282 /*
283 * HEURISTIC: If we see something that looks like a content-*
284 * header, push it back and call the message parser.
285 */
286 p = buf+8;
287 /* Check to see if header's field-name is syntactically valid */
288 while (*p) {
289 if (*p == ':' || *p <= ' ' || *p >= '\177') break;
290 p++;
291 }
292 if (*p == ':') {
293 part_ungets(buf, inpart);
294 if (descfile) fclose(descfile);
295 return handleMessage(inpart, "text/plain", 0, extractText);
296 }
297 }
298 if (buf[0] == '-' && buf[1] == '-') {
299 /*
300 * Heuristic: If we see something that looks like a
301 * multipart boundary, followed by something that looks
302 * like a header, push them back and parse as a multipart.
303 */
304 p = buf+2;
305 while (*p) {
306 if (!bchar[(unsigned char)*p]) break;
307 p++;
308 }
309 if (*p != '\n') {
310 /*
311 * We found an invalid boundary character.
312 * Move 'p' such that it will fail all subsequent checks.
313 */
314 p = buf + 2;
315 }
316 /* Back up to ignore trailing whitespace */
317 while (p > buf+2 && p[-1] == ' ') p--;
318
319 /*
320 * Check that boundary is within legal size limits
321 * If so, peek at next line
322 */
323 if (p - buf > 2 && p - buf <= 72 &&
324 part_gets(buf2, sizeof(buf2), inpart)) {
325 boundary_end = p;
326 p = buf2;
327 /*
328 * Check to see if a syntactically valid header follows
329 * what looks to be a boundary.
330 *
331 * XXX: Unfortunately, we can't check for "Content-";
332 * it is syntactically valid to have a body-part
333 * header that doesn't start with that and ZMail
334 * takes advantage of that. If this heuristic starts
335 * causing problems, we could keep looking ahead until
336 * we find a "Content-" header or find something that's
337 * not a header.
338 */
339 while (*p) {
340 if (*p == ':' || *p <= ' ' || *p >= '\177') break;
341 p++;
342 }
343
344 /* Push back the lookahead line */
345 part_ungets(buf2, inpart);
346
347 if (p > buf2 && *p == ':') {
348 /* Push back the boundary */
349 part_ungets(buf, inpart);
350
351 /*
352 * Generate and push back a header to get us into
353 * the multipart parser.
354 */
355 *boundary_end = '\0';
356 sprintf(buf2,
357 "Content-type: multipart/mixed; boundary=\"%s\"\n\n",
358 buf+2);
359 part_ungets(buf2, inpart);
360
361 if (descfile) fclose(descfile);
362 return handleMessage(inpart, "text/plain", 0, extractText);
363 }
364 }
365 }
366
367 /*
368 * Save useful-looking text that is before a "part 1 of N"
369 * in a description file.
370 */
371 if (wantdescfile && !descfile) {
372 for (p = buf; *p && isspace(*p); p++);
373 if (*p) {
374 if (!strncasecmp(p, "x-", 2)) {
375 /*
376 * Check for "X-foobar:"
377 * If so, there probably will be a "X-File-Desc:" line
378 * later, so ignore this line.
379 */
380 while (*p != ':' && *p > ' ' && *p < '\177') p++;
381 if (*p == ':') continue;
382 }
383 if (!descEnd(buf) && (descfile = startDescFile(fname))) {
384 fputs(buf, descfile);
385 }
386 wantdescfile = 0;
387 }
388 }
389 else if (descfile) {
390 if (descEnd(buf)) {
391 fclose(descfile);
392 descfile = 0;
393 }
394 else {
395 fputs(buf, descfile);
396 }
397 }
398 }
399
400 if (descfile) fclose(descfile);
401 return 0;
402 }
403
404 /*
405 * Handle a split-uuencode part
406 * If nparts is 0, then look for an "end" line to detect the last part.
407 * If fname is null, then we are attempting to decode a single-part message.
408 * If firstline is non-null, it is written as the first line of the saved part
409 */
410 int
411 saveUuFile(struct part *inpart, char *fname, int part, int nparts, char *firstline)
412 {
413 char buf[1024];
414 char *dir;
415 FILE *partfile;
416
417 if (fname) {
418 sprintf(buf, "Saving part %d ", part);
419 if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts);
420 strcat(buf, fname);
421 chat(buf);
422 }
423 else fname = "unknown";
424
425 /* Create directory to store parts and copy this part there. */
426 dir = os_idtodir(fname);
427 if (!dir) return 1;
428 sprintf(buf, "%s%d", dir, part);
429 partfile = os_createnewfile(buf);
430 if (!partfile) {
431 os_perror(buf);
432 return 1;
433 }
434 if (firstline) fputs(firstline, partfile);
435 while (part_gets(buf, sizeof(buf), inpart)) {
436 fputs(buf, partfile);
437 if (nparts == 0 && strcmp(buf, "end\n") == 0) {
438 /* This is the last part. Remember the fact */
439 nparts = part;
440 fclose(partfile);
441 sprintf(buf, "%sCT", dir);
442 partfile = os_createnewfile(buf);
443 if (!partfile) {
444 os_perror(buf);
445 }
446 else {
447 fprintf(partfile, "%d\n", nparts);
448 }
449 break;
450 }
451 }
452 fclose(partfile);
453
454 /* Retrieve any previously saved number of the last part */
455 if (nparts == 0) {
456 sprintf(buf, "%sCT", dir);
457 if (partfile = fopen(buf, "r")) {
458 if (fgets(buf, sizeof(buf), partfile)) {
459 nparts = atoi(buf);
460 if (nparts < 0) nparts = 0;
461 }
462 fclose(partfile);
463 }
464 }
465
466 if (nparts == 0) return 0;
467
468 /* Check to see if we have all parts. Start from the highest numbers
469 * as we are more likely not to have them.
470 */
471 for (part = nparts; part; part--) {
472 sprintf(buf, "%s%d", dir, part);
473 partfile = fopen(buf, "r");
474 if (partfile) {
475 fclose(partfile);
476 }
477 else {
478 return 0;
479 }
480 }
481
482 return uudecodefiles(dir, nparts);
483 }
484
485 /*
486 * Parse a Subject: header, looking for clues with which to decode
487 * split-uuencoded data.
488 */
489 int
490 parseSubject(char *subject, char **fnamep, int *partp, int *npartsp)
491 {
492 char *scan, *bak, *start;
493 int part = -1, nparts = 0, hasdot = 0;
494
495 /* No subject header */
496 if (!subject) return 1;
497
498 /* Skip leading whitespace and other garbage */
499 scan = subject;
500 while (*scan == ' ' || *scan == '\t' || *scan == '-') scan++;
501 if (!strncasecmp(scan, "repost", 6)) {
502 for (scan += 6; *scan == ' ' || *scan == '\t'
503 || *scan == ':' || *scan == '-'; scan++);
504 }
505
506 /* Replies aren't usually data */
507 if (!strncasecmp(scan, "re:", 3)) return 1;
508
509 /* Get filename */
510
511 /* Grab the first filename-like string. Explicitly ignore strings with
512 * prefix "v<digit>" ending in ":", since that is a popular volume/issue
513 * representation syntax
514 */
515 do {
516 while (*scan != '\n' && !isalnum(*scan) && *scan != '_') ++scan;
517 *fnamep = start = scan;
518 while (isalnum(*scan) || *scan == '-' || *scan == '+' || *scan == '&'
519 || *scan == '_' || *scan == '.') {
520 if (*scan++ == '.') hasdot = 1;
521 }
522 if (!*scan || *scan == '\n') return 1;
523 } while (start == scan
524 || (start[0] == 'v' && isdigit(start[1]) && *scan == ':'));
525 *scan++ = '\0';
526
527 /* Try looking for a filename with a "." in it later in the subject line.
528 * Exclude <digit>.<digit>, since that is usually a version number.
529 */
530 if (!hasdot) {
531 while (*(start = scan) != '\0' && *scan != '\n') {
532 while (isspace(*start)) ++start;
533 for (scan = start; isalnum(*scan) || *scan == '-' || *scan == '+'
534 || *scan == '&' || *scan == '_' || *scan == '.'; ++scan) {
535 if (*scan == '.' &&
536 (!isdigit(scan[-1]) || !isdigit(scan[1]))) {
537 hasdot = 1;
538 }
539 }
540 if (hasdot && scan > start) {
541 *fnamep = start;
542 *scan++ = '\0';
543 break;
544 }
545 while (*scan && *scan != '\n' && !isalnum(*scan)) ++scan;
546 }
547 scan = *fnamep + strlen(*fnamep) + 1;
548 }
549
550 /* Get part number */
551 while (*scan && *scan != '\n') {
552 /* skip over versioning */
553 if (*scan == 'v' && isdigit(scan[1])) {
554 ++scan;
555 while (isdigit(*scan)) ++scan;
556 }
557 /* look for "1/6" or "1 / 6" or "1 of 6" or "1-of-6" or "1o6" */
558 if (isdigit(*scan) &&
559 (scan[1] == '/'
560 || (scan[1] == ' ' && scan[2] == '/')
561 || (scan[1] == ' ' && scan[2] == 'o' && scan[3] == 'f')
562 || (scan[1] == '-' && scan[2] == 'o' && scan[3] == 'f')
563 || (scan[1] == 'o' && isdigit(scan[2])))) {
564 while (isdigit(scan[-1])) scan--;
565 part = 0;
566 while (isdigit(*scan)) {
567 part = part * 10 + *scan++ - '0';
568 }
569 while (*scan != '\0' && *scan != '\n' && !isdigit(*scan)) scan++;
570 if (isdigit(*scan)) {
571 nparts = 0;
572 while (isdigit(*scan)) {
573 nparts = nparts * 10 + *scan++ - '0';
574 }
575 }
576 break;
577 }
578
579 /* look for "6 parts" or "part 1" */
580 if (!strncasecmp("part", scan, 4)) {
581 if (scan[4] == 's') {
582 for (bak = scan; bak >= subject && !isdigit(*bak); bak--);
583 if (bak > subject) {
584 while (bak > subject && isdigit(bak[-1])) bak--;
585 nparts = 0;
586 while (isdigit(*bak)) {
587 nparts = nparts * 10 + *bak++ - '0';
588 }
589 }
590 } else {
591 while (*scan && *scan != '\n' && !isdigit(*scan)) scan++;
592 bak = scan - 1;
593 if (isdigit(*scan)) {
594 part = 0;
595 do {
596 part = part * 10 + *scan++ - '0';
597 } while (isdigit(*scan));
598 }
599 scan = bak;
600 }
601 }
602 scan++;
603 }
604
605 if (nparts == 0 || part == -1 || part > nparts) return 1;
606 *partp = part;
607 *npartsp = nparts;
608 return 0;
609 }
610
611 /*
612 * Return nonzero if 'line' should mark the end of a part-1 description
613 */
614 int
615 descEnd(char *line)
616 {
617 return !strncmp(line, "---", 3) ||
618 !strncmp(line, "#!", 2) ||
619 !strncasecmp(line, "part=", 5) ||
620 !strncasecmp(line, "begin", 5);
621 }
622
623 /*
624 * Open and return a file pointer for a description file for 'fname'.
625 * If a description file for 'fname' already exists, or if there is an
626 * error, return a null pointer.
627 */
628 static FILE *startDescFile(char *fname)
629 {
630 char buf[1024];
631 char *dir;
632 FILE *descfile;
633
634 /* Create directory to store parts and copy this part there. */
635 dir = os_idtodir(fname);
636 if (!dir) return 0;
637 sprintf(buf, "%s0", dir);
638
639 /* See if part 0 already exists, return failure if so */
640 descfile = fopen(buf, "r");
641 if (descfile) {
642 fclose(descfile);
643 return 0;
644 }
645
646 descfile = os_createnewfile(buf);
647 if (!descfile) {
648 os_perror(buf);
649 return 0;
650 }
651 return descfile;
652 }
653
654 /*
655 * Decode the uuencoded file that is in 'nparts' pieces in 'dir'.
656 */
657 int
658 uudecodefiles(char *dir, int nparts)
659 {
660 int part;
661 enum {st_start, st_inactive, st_decode, st_nextlast, st_last,
662 st_binhex} state;
663 FILE *infile;
664 FILE *outfile = NULL;
665 struct part *inpart;
666 char buf[1024];
667 char lastline[UULENGTH+1];
668 char *fname, *p;
669 char *contentType = "application/octet-stream";
670 int line_length = 0;
671
672 /* If a part 0, copy to description filename */
673 sprintf(buf, "%s0", dir);
674 infile = fopen(buf, "r");
675 if (infile) {
676 outfile = os_createnewfile(TEMPFILENAME);
677 if (outfile) {
678 while (fgets(buf, sizeof(buf), infile)) {
679 fputs(buf, outfile);
680 }
681 fclose(outfile);
682 outfile = NULL;
683 }
684 fclose(infile);
685 sprintf(buf, "%s0", dir);
686 remove(buf);
687 }
688
689 state = st_start;
690
691 /* Handle each part in order */
692 for (part = 1; part <= nparts; part++) {
693 sprintf(buf, "%s%d", dir, part);
694 infile = fopen(buf, "r");
695 if (!infile) {
696 os_perror(buf);
697 if (outfile) fclose(outfile);
698 remove(TEMPFILENAME);
699 return 1;
700 }
701
702 while (fgets(buf, sizeof(buf), infile)) {
703 switch (state) {
704 case st_start: /* Looking for start of uuencoded
705 * or binhex'ed file */
706 if (!strcmp(buf,
707 "(This file must be converted with BinHex 4.0)\n")) {
708 state = st_binhex;
709 inpart = part_init(infile);
710 os_binhex(inpart, part, nparts);
711 part_close(inpart);
712 goto endbinhex;
713 }
714 if (strncmp(buf, "begin ", 6)) break;
715 /* skip mode */
716 p = buf + 6;
717 while (*p && !isspace(*p)) p++;
718 while (*p && isspace(*p)) p++;
719 fname = p;
720 while (*p && !isspace(*p)) p++;
721 *p = '\0';
722 if (!*fname) return 1;
723
724 /* Guess the content-type of common filename extensions */
725 if (p = strrchr(fname, '.')) {
726 if (!strcasecmp(p, ".gif")) contentType = "image/gif";
727 if (!strcasecmp(p, ".jpg")) contentType = "image/jpeg";
728 if (!strcasecmp(p, ".jpeg")) contentType = "image/jpeg";
729 if (!strcasecmp(p, ".mpg")) contentType = "video/mpeg";
730 if (!strcasecmp(p, ".mpeg")) contentType = "video/mpeg";
731 }
732
733 /* Create output file and start decoding */
734 outfile = os_newtypedfile(fname, contentType, FILE_BINARY,
735 (params) 0);
736 if (!outfile) {
737 fclose(infile);
738 return 1;
739 }
740 state = st_decode;
741 break;
742
743 case st_inactive: /* Looking for uuencoded data to resume */
744 if (*buf != 'M' || strlen(buf) != line_length) {
745 if (*buf == 'B' && !strncmp(buf, "BEGIN", 5)) {
746 state = st_decode;
747 }
748 break;
749 }
750 state = st_decode;
751 /* FALL THROUGH */
752 case st_decode: /* Decoding data */
753 if (line_length == 0) line_length = strlen(buf);
754 if (*buf == 'M' && strlen(buf) == line_length) {
755 uudecodeline(buf, outfile);
756 break;
757 }
758 if (strlen(buf) > line_length) {
759 state = st_inactive;
760 break;
761 }
762 /*
763 * May be on nearing end of file.
764 * Save this line in case we are.
765 */
766 strcpy(lastline, buf);
767 if (*buf == ' ' || *buf == '`') {
768 state = st_last;
769 }
770 else {
771 state = st_nextlast;
772 }
773 break;
774
775 case st_nextlast: /* May be nearing end of file */
776 if (*buf == ' ' || *buf == '`') {
777 state = st_last;
778 }
779 else {
780 state = st_inactive;
781 }
782 break;
783
784 case st_last: /* Should be at end of file */
785 if (!strncmp(buf, "end", 3) && isspace(buf[3])) {
786 /* Handle that last line we saved */
787 uudecodeline(lastline, outfile);
788 fclose(infile);
789 os_closetypedfile(outfile);
790 for (;part <= nparts; part++) {
791 sprintf(buf, "%s%d", dir, part);
792 remove(buf);
793 }
794 sprintf(buf, "%sCT", dir);
795 remove(buf);
796 os_donewithdir(dir);
797 return 0;
798 }
799 state = st_inactive;
800 break;
801
802 case st_binhex:
803 if (strncmp(buf, "---", 3)) break;
804 inpart = part_init(infile);
805 os_binhex(inpart, part, nparts);
806 part_close(inpart);
807 goto endbinhex;
808 }
809 }
810 if (state != st_binhex) state = st_inactive;
811 fclose(infile);
812 endbinhex:
813 sprintf(buf, "%s%d", dir, part);
814 remove(buf);
815 }
816 if (outfile) os_closetypedfile(outfile);
817 if (state == st_binhex) os_binhex(0, 0, 0);
818 sprintf(buf, "%sCT", dir);
819 remove(buf);
820 os_donewithdir(dir);
821 return 0;
822 }
823
824 #define DEC(c) (((c) - ' ') & 077)
825
826 /*
827 * Decode a uuencoded line to 'outfile'
828 */
829 int uudecodeline(char *line, FILE *outfile)
830 {
831 int c, len;
832
833 len = DEC(*line++);
834 while (len) {
835 c = DEC(*line) << 2 | DEC(line[1]) >> 4;
836 putc(c, outfile);
837 if (--len) {
838 c = DEC(line[1]) << 4 | DEC(line[2]) >> 2;
839 putc(c, outfile);
840 if (--len) {
841 c = DEC(line[2]) << 6 | DEC(line[3]);
842 putc(c, outfile);
843 len--;
844 }
845 }
846 line += 4;
847 }
848 return;
849 }
850
851

  ViewVC Help
Powered by ViewVC 1.1.22