/[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.4 - (show annotations) (download)
Tue Feb 26 17:05:00 2008 UTC (16 years, 1 month ago) by jmc
Branch: MAIN
CVS Tags: checkpoint64y, checkpoint64x, checkpoint64z, checkpoint64q, checkpoint64p, checkpoint64s, checkpoint64r, checkpoint64u, checkpoint64t, checkpoint64w, checkpoint64v, checkpoint64i, checkpoint64h, checkpoint64k, checkpoint64j, checkpoint64m, checkpoint64l, checkpoint64o, checkpoint64n, checkpoint64a, checkpoint64c, checkpoint64b, checkpoint64e, checkpoint64d, checkpoint64g, checkpoint64f, checkpoint63p, checkpoint63q, checkpoint63r, checkpoint63s, checkpoint63l, checkpoint63m, checkpoint63n, checkpoint63o, checkpoint63h, checkpoint63i, checkpoint63j, checkpoint63k, checkpoint63d, checkpoint63e, checkpoint63f, checkpoint63g, checkpoint63a, checkpoint63b, checkpoint63c, checkpoint64, checkpoint65, checkpoint60, checkpoint61, checkpoint62, checkpoint63, checkpoint66g, checkpoint66f, checkpoint66e, checkpoint66d, checkpoint66c, checkpoint66b, checkpoint66a, checkpoint66o, checkpoint66n, checkpoint66m, checkpoint66l, checkpoint66k, checkpoint66j, checkpoint66i, checkpoint66h, checkpoint65z, checkpoint65x, checkpoint65y, checkpoint65r, checkpoint65s, checkpoint65p, checkpoint65q, checkpoint65v, checkpoint65w, checkpoint65t, checkpoint65u, checkpoint65j, checkpoint65k, checkpoint65h, checkpoint65i, checkpoint65n, checkpoint65o, checkpoint65l, checkpoint65m, checkpoint65b, checkpoint65c, checkpoint65a, checkpoint65f, checkpoint65g, checkpoint65d, checkpoint65e, checkpoint59q, checkpoint59p, checkpoint59r, checkpoint59o, checkpoint62c, checkpoint62b, checkpoint62a, checkpoint62g, checkpoint62f, checkpoint62e, checkpoint62d, checkpoint62k, checkpoint62j, checkpoint62i, checkpoint62h, checkpoint62o, checkpoint62n, checkpoint62m, checkpoint62l, checkpoint62s, checkpoint62r, checkpoint62q, checkpoint62p, checkpoint62w, checkpoint62v, checkpoint62u, checkpoint62t, checkpoint62z, checkpoint62y, checkpoint62x, checkpoint61f, checkpoint61g, checkpoint61d, checkpoint61e, checkpoint61b, checkpoint61c, checkpoint61a, checkpoint61n, checkpoint61o, checkpoint61l, checkpoint61m, checkpoint61j, checkpoint61k, checkpoint61h, checkpoint61i, checkpoint61v, checkpoint61w, checkpoint61t, checkpoint61u, checkpoint61r, checkpoint61s, checkpoint61p, checkpoint61q, checkpoint61z, checkpoint61x, checkpoint61y, HEAD
Changes since 1.3: +22 -5 lines
File MIME type: text/plain
put again new version (1.6-4, see MITgcm_contrib/mpack_src) of mpack sources.
among others:
 - fix MD5 code on some 64 bit platforms.
 - fix lot of compilation warnings.

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

  ViewVC Help
Powered by ViewVC 1.1.22