/[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.1 - (hide 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 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     #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