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

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

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


Revision 1.4 - (hide annotations) (download)
Tue Feb 26 17:05:00 2008 UTC (16 years, 2 months 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 edhill 1.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 jmc 1.4 #include <stdlib.h>
27 edhill 1.1 #include <ctype.h>
28     #include <string.h>
29 jmc 1.4 #include <unistd.h>
30 edhill 1.1 #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 jmc 1.4 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 edhill 1.1
48     static FILE *startDescFile(char *fname);
49 jmc 1.4 static void uudecodeline(char *line, FILE *outfile);
50 edhill 1.1
51 jmc 1.4 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 edhill 1.1
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 jmc 1.4 if ((descfile = startDescFile(fname))) {
288 edhill 1.1 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 jmc 1.4 if ((partfile = fopen(buf, "r"))) {
476 edhill 1.1 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 jmc 1.4 if ((p = strrchr(fname, '.'))) {
744 edhill 1.1 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 jmc 1.4 static void uudecodeline(char *line, FILE *outfile)
848 edhill 1.1 {
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