Back to home page

MITgcm

 
 

    


File indexing completed on 2018-03-02 18:45:01 UTC

view on githubraw file Latest commit e768bd12 on 2008-02-26 17:05:00 UTC
ec6cf3b09d Ed H*0001 /* (C) Copyright 1993,1994 by Carnegie Mellon University
                0002  * All Rights Reserved.
                0003  *
                0004  * Permission to use, copy, modify, distribute, and sell this software
                0005  * and its documentation for any purpose is hereby granted without
                0006  * fee, provided that the above copyright notice appear in all copies
                0007  * and that both that copyright notice and this permission notice
                0008  * appear in supporting documentation, and that the name of Carnegie
                0009  * Mellon University not be used in advertising or publicity
                0010  * pertaining to distribution of the software without specific,
                0011  * written prior permission.  Carnegie Mellon University makes no
                0012  * representations about the suitability of this software for any
                0013  * purpose.  It is provided "as is" without express or implied
                0014  * warranty.
                0015  *
                0016  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
                0017  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
                0018  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
                0019  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                0020  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
                0021  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                0022  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                0023  * SOFTWARE.
                0024  */
                0025 #include <stdio.h>
e768bd1221 Jean*0026 #include <stdlib.h>
ec6cf3b09d Ed H*0027 #include <ctype.h>
                0028 #include <string.h>
e768bd1221 Jean*0029 #include <unistd.h>
ec6cf3b09d Ed H*0030 #include "xmalloc.h"
                0031 #include "common.h"
                0032 #include "part.h"
                0033 
                0034 extern char *os_idtodir(char *id);
                0035 extern FILE *os_newtypedfile(char *fname, char *contentType, int flags, params contentParams);
                0036 extern FILE *os_createnewfile(char *fname);
e768bd1221 Jean*0037 extern int os_binhex(struct part *inpart, int part, int nparts);
                0038 extern void os_closetypedfile(FILE *outfile);
                0039 extern void os_donewithdir(char *dir);
                0040 extern void os_perror(char *str);
                0041 extern void chat(char *s);
                0042 
                0043 extern void part_ungets(char *s, struct part *part);
                0044 extern void part_close(struct part *part);
                0045 extern int handleMessage(struct part *inpart, char *defaultContentType,
                0046              int inAppleDouble, int extractText);
ec6cf3b09d Ed H*0047 
                0048 static FILE *startDescFile(char *fname);
e768bd1221 Jean*0049 static void uudecodeline(char *line, FILE *outfile);
ec6cf3b09d Ed H*0050 
e768bd1221 Jean*0051 int parseSubject(char *subject, char **fnamep, int *partp, int *npartsp);
                0052 int saveUuFile(struct part *inpart, char *fname, int part, int nparts,
                0053            char *firstline);
                0054 int descEnd(char *line);
                0055 int uudecodefiles(char *dir, int nparts);
ec6cf3b09d Ed H*0056 
                0057 /* Length of a normal uuencoded line, including newline */
                0058 #define UULENGTH 62
                0059 
                0060 /*
                0061  * Table of valid boundary characters
                0062  *
                0063  * XXX: Old versions of Mark Crispin's c-client library
                0064  * generate boundaries which contain the syntactically
                0065  * illegal character '#'.  It is marked in this table with
                0066  * a 2 in case we want to use this table in the future to
                0067  * complain about bad syntax.
                0068  *
                0069  */
                0070 static char bchar[256] = {
                0071      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0072      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0073      1, 0, 0, 2,  0, 0, 0, 1,  1, 1, 0, 1,  1, 1, 1, 1,
                0074      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 0,  0, 1, 0, 1,
                0075      0, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
                0076      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 0,  0, 0, 0, 1,
                0077      0, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
                0078      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 0,  0, 0, 0, 0,
                0079      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0080      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0081      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0082      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0083      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0084      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0085      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0086      0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
                0087 };
                0088 
                0089 /*
                0090  * Read an input file, looking for data in split-uuencode format
                0091  */
                0092 int handleUuencode(struct part *inpart, char *subject, int extractText)
                0093 {
                0094     char *fname = 0, *tmpfname;
                0095     int part, nparts;
                0096     int tmppart, tmpnparts;
                0097     char buf[1024], buf2[1024];
                0098     char fnamebuf[80];
                0099     char *boundary_end, *p;
                0100     int wantdescfile = 0;
                0101     FILE *descfile = 0;
                0102 
                0103     /* Scan "Subject:" header for filename/part information */
                0104     if (parseSubject(subject, &fname, &part, &nparts) != 0) {
                0105     part = -1;
                0106     }
                0107     if (part == 0) {
                0108     return saveUuFile(inpart, fname, part, nparts, (char *)0);
                0109     }
                0110     if (part == 1) {
                0111     wantdescfile = 1;
                0112     }
                0113 
                0114     /* Scan body for interesting lines */
                0115     while (part_gets(buf, sizeof(buf), inpart)) {
                0116     /* Uuencode "begin" line */
                0117     if (!strncmp(buf, "begin ", 6) &&
                0118         isdigit(buf[6]) && isdigit(buf[7]) && isdigit(buf[8]) &&
                0119         buf[9] == ' ') {
                0120         if (part == -1) {
                0121         /*
                0122          * We have no part N of M information.  Perhaps it is
                0123          * a single-part uuencoded file.
                0124          */
                0125         return saveUuFile(inpart, (char *)0, 1, 0, buf);
                0126         }
                0127         else {
                0128         if (descfile) fclose(descfile);
                0129         return saveUuFile(inpart, fname, part, nparts, buf);
                0130         }
                0131     }
                0132     else if (!strncmp(buf, "section ", 8) && isdigit(buf[8])) {
                0133         tmppart = 0;
                0134         for (p = buf+8; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
                0135         if (tmppart == 0) continue;
                0136         if (strncmp(p, " of ", 4) == 0) {
                0137         /*
                0138          * "section N of ... of file F ..."
                0139          */
                0140         for (p += 4; *p && strncmp(p, " of file ", 9) != 0; p++);
                0141         if (!*p) continue;
                0142         p += 9;
                0143         tmpfname = p;
                0144         p = strchr(p, ' ');
                0145         if (!p) continue;
                0146         *p = '\0';
                0147         if (descfile) fclose(descfile);
                0148         return saveUuFile(inpart, tmpfname, tmppart, 0, (char *)0);
                0149         }
                0150         else if (*p == '/' && isdigit(p[1])) {
                0151         /*
                0152          * "section N/M   file F ..."
                0153          */
                0154         tmpnparts = 0;
                0155         for (p++; isdigit(*p); p++) {
                0156             tmpnparts = tmpnparts*10 + *p - '0';
                0157         }
                0158         while (*p && isspace(*p)) p++;
                0159         if (tmppart > tmpnparts || strncmp(p, "file ", 5) != 0) {
                0160             continue;
                0161         }
                0162         tmpfname = p+5;
                0163         p = strchr(tmpfname, ' ');
                0164         if (!p) continue;
                0165         *p = '\0';
                0166         if (descfile) fclose(descfile);
                0167         return saveUuFile(inpart, tmpfname, tmppart, tmpnparts,
                0168                   (char *)0);
                0169         }
                0170     }
                0171     else if (!strncmp(buf, "POST V", 6)) {
                0172         /*
                0173          * "POST Vd.d.d F (Part N/M)"
                0174          */
                0175         p = strchr(buf+6, ' ');
                0176         if (!p) continue;
                0177         tmpfname = p+1;
                0178         p = strchr(tmpfname, ' ');
                0179         if (!p || strncmp(p, " (Part ", 7) != 0) continue;
                0180         *p = '\0';
                0181         p += 7;
                0182         tmppart = 0;
                0183         while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
                0184         if (tmppart == 0 || *p++ != '/') continue;
                0185         tmpnparts = 0;
                0186         while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
                0187         if (tmppart > tmpnparts || *p != ')') continue;
                0188         if (descfile) fclose(descfile);
                0189         return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
                0190     }
                0191     else if (!strncmp(buf, "File: ", 6)) {
                0192         /*
                0193          * "File: F -- part N of M -- ...
                0194          */
                0195         tmpfname = buf+6;
                0196         p = strchr(tmpfname, ' ');
                0197         if (!p || strncmp(p, " -- part ", 9) != 0) continue;
                0198         *p = '\0';
                0199         p += 9;
                0200         tmppart = 0;
                0201         while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
                0202         if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
                0203         p += 4;
                0204         tmpnparts = 0;
                0205         while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
                0206         if (tmppart > tmpnparts || strncmp(p, " -- ", 4) != 0) continue;
                0207         if (descfile) fclose(descfile);
                0208         return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
                0209     }
                0210     else if (!strncmp(buf, "[Section: ", 10)) {
                0211         /*
                0212          * "[Section: N/M  File: F ..."
                0213          */
                0214         tmppart = 0;
                0215         for (p = buf+10; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
                0216         if (tmppart == 0) continue;
                0217         tmpnparts = 0;
                0218         for (p++; isdigit(*p); p++) {
                0219         tmpnparts = tmpnparts*10 + *p - '0';
                0220         }
                0221         while (*p && isspace(*p)) p++;
                0222         if (tmppart > tmpnparts || strncmp(p, "File: ", 6) != 0) {
                0223         continue;
                0224         }
                0225         tmpfname = p+6;
                0226         p = strchr(tmpfname, ' ');
                0227         if (!p) continue;
                0228         *p = '\0';
                0229         if (descfile) fclose(descfile);
                0230         return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
                0231     }
                0232     else if (*buf == '[') {
                0233         /*
                0234          * "[F ... - part N of M]"
                0235          * (usual BinHex practice)
                0236          */
                0237         tmpfname = buf+1;
                0238         p = strchr(tmpfname, ' ');
                0239         if (!p) continue;
                0240         *p++ = '\0';
                0241         while (p && strncmp(p, "- part ", 7) != 0) {
                0242         p = strchr(p+1, '-');
                0243         }
                0244         if (!p) continue;
                0245         p += 7;
                0246         tmppart = 0;
                0247         while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
                0248         if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
                0249         p += 4;
                0250         tmpnparts = 0;
                0251         while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
                0252         if (tmppart > tmpnparts || *p != ']') continue;
                0253         if (descfile) fclose(descfile);
                0254         return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
                0255     }
                0256     else if (fname && part > 0 && nparts > 0 && part <= nparts &&
                0257          (!strncmp(buf, "BEGIN", 5) ||
                0258           !strncmp(buf, "--- BEGIN ---", 12) ||
                0259           (buf[0] == 'M' && strlen(buf) == UULENGTH))) {
                0260         /*
                0261          * Found the start of a section of uuencoded data
                0262          * and have the part N of M information.
                0263          */
                0264         if (descfile) fclose(descfile);
                0265         return saveUuFile(inpart, fname, part, nparts, buf);
                0266     }
                0267     else if (!strncasecmp(buf, "x-file-name: ", 13)) {
                0268         for (p = buf + 13; *p && !isspace(*p); p++);
                0269         *p = '\0';
                0270         strncpy(fnamebuf, buf+13, sizeof(fnamebuf)-1);
                0271         fnamebuf[sizeof(fnamebuf)-1] = '\0';
                0272         fname = fnamebuf;
                0273         continue;
                0274     }
                0275     else if (!strncasecmp(buf, "x-part: ", 8)) {
                0276         tmppart = atoi(buf+8);
                0277         if (tmppart > 0) part = tmppart;
                0278         continue;
                0279     }
                0280     else if (!strncasecmp(buf, "x-part-total: ", 14)) {
                0281         tmpnparts = atoi(buf+14);
                0282         if (tmpnparts > 0) nparts = tmpnparts;
                0283         continue;
                0284     }
                0285     else if (part == 1 && fname && !descfile &&
                0286          !strncasecmp(buf, "x-file-desc: ", 13)) {
e768bd1221 Jean*0287         if ((descfile = startDescFile(fname))) {
ec6cf3b09d Ed H*0288         fputs(buf+13, descfile);
                0289         fclose(descfile);
                0290         descfile = 0;
                0291         }
                0292         continue;
                0293     }
                0294     else if (!strcmp(buf,
                0295              "(This file must be converted with BinHex 4.0)\n")) {
                0296         if (descfile) fclose(descfile);
                0297         return os_binhex(inpart, 1, 1);
                0298     }
                0299     else if (!strncasecmp(buf, "content-", 8)) {
                0300         /*
                0301          * HEURISTIC: If we see something that looks like a content-*
                0302          * header, push it back and call the message parser.
                0303          */
                0304         p = buf+8;
                0305         /* Check to see if header's field-name is syntactically valid */
                0306         while (*p) {
                0307         if (*p == ':' || *p <= ' ' || *p >= '\177') break;
                0308         p++;
                0309         }
                0310         if (*p == ':') {
                0311         part_ungets(buf, inpart);
                0312         if (descfile) fclose(descfile);
                0313         return handleMessage(inpart, "text/plain", 0, extractText);
                0314         }
                0315     }
                0316     if (buf[0] == '-' && buf[1] == '-') {
                0317         /*
                0318          * Heuristic: If we see something that looks like a
                0319          * multipart boundary, followed by something that looks
                0320          * like a header, push them back and parse as a multipart.
                0321          */
                0322         p = buf+2;
                0323         while (*p) {
                0324         if (!bchar[(unsigned char)*p]) break;
                0325         p++;
                0326         }
                0327         if (*p != '\n') {
                0328         /*
                0329          * We found an invalid boundary character.
                0330          * Move 'p' such that it will fail all subsequent checks.
                0331          */
                0332         p = buf + 2;
                0333         }
                0334         /* Back up to ignore trailing whitespace */
                0335         while (p > buf+2 && p[-1] == ' ') p--;
                0336 
                0337         /*
                0338          * Check that boundary is within legal size limits
                0339          * If so, peek at next line
                0340          */
                0341         if (p - buf > 2 && p - buf <= 72 &&
                0342         part_gets(buf2, sizeof(buf2), inpart)) {
                0343         boundary_end = p;
                0344         p = buf2;
                0345         /*
                0346          * Check to see if a syntactically valid header follows
                0347          * what looks to be a boundary.
                0348          *
                0349          * XXX: Unfortunately, we can't check for "Content-";
                0350          * it is syntactically valid to have a body-part
                0351          * header that doesn't start with that and ZMail
                0352          * takes advantage of that.  If this heuristic starts
                0353          * causing problems, we could keep looking ahead until
                0354          * we find a "Content-" header or find something that's
                0355          * not a header.
                0356          */
                0357         while (*p) {
                0358             if (*p == ':' || *p <= ' ' || *p >= '\177') break;
                0359             p++;
                0360         }
                0361         
                0362         /* Push back the lookahead line */
                0363         part_ungets(buf2, inpart);
                0364 
                0365         if (p > buf2 && *p == ':') {
                0366             /* Push back the boundary */
                0367             part_ungets(buf, inpart);
                0368 
                0369             /*
                0370              * Generate and push back a header to get us into
                0371              * the multipart parser.
                0372              */
                0373             *boundary_end = '\0';
                0374             sprintf(buf2,
                0375              "Content-type: multipart/mixed; boundary=\"%s\"\n\n",
                0376                 buf+2);
                0377             part_ungets(buf2, inpart);
                0378             
                0379             if (descfile) fclose(descfile);
                0380             return handleMessage(inpart, "text/plain", 0, extractText);
                0381         }
                0382         }
                0383     }
                0384 
                0385     /*
                0386      * Save useful-looking text that is before a "part 1 of N"
                0387      * in a description file.
                0388      */
                0389     if (wantdescfile && !descfile) {
                0390         for (p = buf; *p && isspace(*p); p++);
                0391         if (*p) {
                0392         if (!strncasecmp(p, "x-", 2)) {
                0393             /*
                0394              * Check for "X-foobar:"
                0395              * If so, there probably will be a "X-File-Desc:" line
                0396              * later, so ignore this line.
                0397              */
                0398             while (*p != ':' && *p > ' ' && *p < '\177') p++;
                0399             if (*p == ':') continue;
                0400         }
                0401         if (!descEnd(buf) && (descfile = startDescFile(fname))) {
                0402             fputs(buf, descfile);
                0403         }
                0404         wantdescfile = 0;
                0405         }
                0406     }
                0407     else if (descfile) {
                0408         if (descEnd(buf)) {
                0409         fclose(descfile);
                0410         descfile = 0;
                0411         }
                0412         else {
                0413         fputs(buf, descfile);
                0414         }
                0415     }
                0416     }   
                0417 
                0418     if (descfile) fclose(descfile);
                0419     return 0;
                0420 }
                0421 
                0422 /*
                0423  * Handle a split-uuencode part
                0424  * If nparts is 0, then look for an "end" line to detect the last part.
                0425  * If fname is null, then we are attempting to decode a single-part message.
                0426  * If firstline is non-null, it is written as the first line of the saved part
                0427  */
                0428 int
                0429 saveUuFile(struct part *inpart, char *fname, int part, int nparts, char *firstline)
                0430 {
                0431     char buf[1024];
                0432     char *dir;
                0433     FILE *partfile;
                0434 
                0435     if (fname) {
                0436     sprintf(buf, "Saving part %d ", part);
                0437     if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts);
                0438     strcat(buf, fname);
                0439     chat(buf);
                0440     }
                0441     else fname = "unknown";
                0442 
                0443     /* Create directory to store parts and copy this part there. */
                0444     dir = os_idtodir(fname);
                0445     if (!dir) return 1;
                0446     sprintf(buf, "%s%d", dir, part);
                0447     partfile = os_createnewfile(buf);
                0448         if (!partfile) {
                0449     os_perror(buf);
                0450     return 1;
                0451     }
                0452     if (firstline) fputs(firstline, partfile);
                0453     while (part_gets(buf, sizeof(buf), inpart)) {
                0454     fputs(buf, partfile);
                0455     if (nparts == 0 && strcmp(buf, "end\n") == 0) {
                0456         /* This is the last part. Remember the fact */
                0457         nparts = part;
                0458         fclose(partfile);
                0459         sprintf(buf, "%sCT", dir);
                0460         partfile = os_createnewfile(buf);
                0461         if (!partfile) {
                0462         os_perror(buf);
                0463         }
                0464         else {
                0465         fprintf(partfile, "%d\n", nparts);
                0466         }
                0467         break;
                0468     }
                0469     }
                0470     fclose(partfile);
                0471 
                0472     /* Retrieve any previously saved number of the last part */
                0473     if (nparts == 0) {
                0474     sprintf(buf, "%sCT", dir);
e768bd1221 Jean*0475     if ((partfile = fopen(buf, "r"))) {
ec6cf3b09d Ed H*0476         if (fgets(buf, sizeof(buf), partfile)) {
                0477         nparts = atoi(buf);
                0478         if (nparts < 0) nparts = 0;
                0479         }
                0480         fclose(partfile);
                0481     }
                0482     }
                0483 
                0484     if (nparts == 0) return 0;
                0485 
                0486     /* Check to see if we have all parts.  Start from the highest numbers
                0487      * as we are more likely not to have them.
                0488      */
                0489     for (part = nparts; part; part--) {
                0490     sprintf(buf, "%s%d", dir, part);
                0491     partfile = fopen(buf, "r");
                0492     if (partfile) {
                0493         fclose(partfile);
                0494     }
                0495     else {
                0496         return 0;
                0497     }
                0498     }
                0499 
                0500     return uudecodefiles(dir, nparts);
                0501 }
                0502 
                0503 /*
                0504  * Parse a Subject: header, looking for clues with which to decode
                0505  * split-uuencoded data.
                0506  */
                0507 int
                0508 parseSubject(char *subject, char **fnamep, int *partp, int *npartsp)
                0509 {
                0510     char *scan, *bak, *start;
                0511     int part = -1, nparts = 0, hasdot = 0;
                0512 
                0513     /* No subject header */
                0514     if (!subject) return 1;
                0515 
                0516     /* Skip leading whitespace and other garbage */
                0517     scan = subject;
                0518     while (*scan == ' ' || *scan == '\t' || *scan == '-') scan++;
                0519     if (!strncasecmp(scan, "repost", 6)) {
                0520     for (scan += 6; *scan == ' ' || *scan == '\t'
                0521          || *scan == ':' || *scan == '-'; scan++);
                0522     }
                0523 
                0524     /* Replies aren't usually data */
                0525     if (!strncasecmp(scan, "re:", 3)) return 1;
                0526 
                0527     /* Get filename */
                0528 
                0529     /* Grab the first filename-like string.  Explicitly ignore strings with
                0530      * prefix "v<digit>" ending in ":", since that is a popular volume/issue
                0531      * representation syntax
                0532      */
                0533     do {
                0534     while (*scan != '\n' && !isalnum(*scan) && *scan != '_') ++scan;
                0535     *fnamep = start = scan;
                0536     while (isalnum(*scan) || *scan == '-' || *scan == '+' || *scan == '&'
                0537            || *scan == '_' || *scan == '.') {
                0538         if (*scan++ == '.') hasdot = 1;
                0539     }
                0540     if (!*scan || *scan == '\n') return 1;
                0541     } while (start == scan
                0542          || (start[0] == 'v' && isdigit(start[1]) && *scan == ':'));
                0543     *scan++ = '\0';
                0544     
                0545     /* Try looking for a filename with a "." in it later in the subject line.
                0546      * Exclude <digit>.<digit>, since that is usually a version number.
                0547      */
                0548     if (!hasdot) {
                0549         while (*(start = scan) != '\0' && *scan != '\n') {
                0550         while (isspace(*start)) ++start;
                0551         for (scan = start; isalnum(*scan) || *scan == '-' || *scan == '+'
                0552          || *scan == '&' || *scan == '_' || *scan == '.'; ++scan) {
                0553         if (*scan == '.' && 
                0554             (!isdigit(scan[-1]) || !isdigit(scan[1]))) {
                0555             hasdot = 1;
                0556         }
                0557         }
                0558         if (hasdot && scan > start) {
                0559         *fnamep = start;
                0560         *scan++ = '\0';
                0561         break;
                0562         }
                0563         while (*scan && *scan != '\n' && !isalnum(*scan)) ++scan;
                0564         }
                0565         scan = *fnamep + strlen(*fnamep) + 1;
                0566     }
                0567 
                0568     /* Get part number */
                0569     while (*scan && *scan != '\n') {
                0570     /* skip over versioning */
                0571     if (*scan == 'v' && isdigit(scan[1])) {
                0572         ++scan;
                0573         while (isdigit(*scan)) ++scan;
                0574     }
                0575     /* look for "1/6" or "1 / 6" or "1 of 6" or "1-of-6" or "1o6" */
                0576     if (isdigit(*scan) &&
                0577         (scan[1] == '/'
                0578          || (scan[1] == ' ' && scan[2] == '/')
                0579          || (scan[1] == ' ' && scan[2] == 'o' && scan[3] == 'f')
                0580          || (scan[1] == '-' && scan[2] == 'o' && scan[3] == 'f')
                0581          || (scan[1] == 'o' && isdigit(scan[2])))) {
                0582         while (isdigit(scan[-1])) scan--;
                0583         part = 0;
                0584         while (isdigit(*scan)) {
                0585         part = part * 10 + *scan++ - '0';
                0586         }
                0587         while (*scan != '\0' && *scan != '\n' && !isdigit(*scan)) scan++;
                0588         if (isdigit(*scan)) {
                0589         nparts = 0;
                0590         while (isdigit(*scan)) {
                0591             nparts = nparts * 10 + *scan++ - '0';
                0592         }
                0593         }
                0594         break;
                0595     }
                0596 
                0597     /* look for "6 parts" or "part 1" */
                0598     if (!strncasecmp("part", scan, 4)) {
                0599         if (scan[4] == 's') {
                0600         for (bak = scan; bak >= subject && !isdigit(*bak); bak--);
                0601         if (bak > subject) {
                0602             while (bak > subject && isdigit(bak[-1])) bak--;
                0603             nparts = 0;
                0604             while (isdigit(*bak)) {
                0605             nparts = nparts * 10 + *bak++ - '0';
                0606             }
                0607         }
                0608         } else {
                0609         while (*scan && *scan != '\n' && !isdigit(*scan)) scan++;
                0610         bak = scan - 1;
                0611         if (isdigit(*scan)) {
                0612             part = 0;
                0613             do {
                0614             part = part * 10 + *scan++ - '0';
                0615             } while (isdigit(*scan));
                0616         }
                0617         scan = bak;
                0618         }
                0619     }
                0620     scan++;
                0621     }
                0622 
                0623     if (nparts == 0 || part == -1 || part > nparts) return 1;
                0624     *partp = part;
                0625     *npartsp = nparts;
                0626     return 0;
                0627 }
                0628 
                0629 /*
                0630  * Return nonzero if 'line' should mark the end of a part-1 description
                0631  */
                0632 int
                0633 descEnd(char *line)
                0634 {
                0635     return !strncmp(line, "---", 3) ||
                0636     !strncmp(line, "#!", 2) ||
                0637     !strncasecmp(line, "part=", 5) ||
                0638     !strncasecmp(line, "begin", 5);
                0639 }
                0640 
                0641 /*
                0642  * Open and return a file pointer for a description file for 'fname'.
                0643  * If a description file for 'fname' already exists, or if there is an
                0644  * error, return a null pointer.
                0645  */
                0646 static FILE *startDescFile(char *fname)
                0647 {
                0648     char buf[1024];
                0649     char *dir;
                0650     FILE *descfile;
                0651 
                0652     /* Create directory to store parts and copy this part there. */
                0653     dir = os_idtodir(fname);
                0654     if (!dir) return 0;
                0655     sprintf(buf, "%s0", dir);
                0656 
                0657     /* See if part 0 already exists, return failure if so */
                0658     descfile = fopen(buf, "r");
                0659     if (descfile) {
                0660     fclose(descfile);
                0661     return 0;
                0662     }
                0663 
                0664     descfile = os_createnewfile(buf);
                0665     if (!descfile) {
                0666     os_perror(buf);
                0667     return 0;
                0668     }
                0669     return descfile;
                0670 }
                0671 
                0672 /*
                0673  * Decode the uuencoded file that is in 'nparts' pieces in 'dir'.
                0674  */
                0675 int
                0676 uudecodefiles(char *dir, int nparts)
                0677 {
                0678     int part;
                0679     enum {st_start, st_inactive, st_decode, st_nextlast, st_last,
                0680         st_binhex} state;
                0681     FILE *infile;
                0682     FILE *outfile = NULL;
                0683     struct part *inpart;
                0684     char buf[1024];
                0685     char lastline[UULENGTH+1];
                0686     char *fname, *p;
                0687     char *contentType = "application/octet-stream";
                0688     int line_length = 0;
                0689 
                0690     /* If a part 0, copy to description filename */
                0691     sprintf(buf, "%s0", dir);
                0692     infile = fopen(buf, "r");
                0693     if (infile) {
                0694     outfile = os_createnewfile(TEMPFILENAME);
                0695     if (outfile) {
                0696         while (fgets(buf, sizeof(buf), infile)) {
                0697         fputs(buf, outfile);
                0698         }
                0699         fclose(outfile);
                0700         outfile = NULL;
                0701     }
                0702     fclose(infile);
                0703     sprintf(buf, "%s0", dir);
                0704     remove(buf);
                0705     }
                0706 
                0707     state = st_start;
                0708 
                0709     /* Handle each part in order */
                0710     for (part = 1; part <= nparts; part++) {
                0711     sprintf(buf, "%s%d", dir, part);
                0712     infile = fopen(buf, "r");
                0713     if (!infile) {
                0714         os_perror(buf);
                0715         if (outfile) fclose(outfile);
                0716         remove(TEMPFILENAME);
                0717         return 1;
                0718     }
                0719 
                0720     while (fgets(buf, sizeof(buf), infile)) {
                0721         switch (state) {
                0722         case st_start:  /* Looking for start of uuencoded
                0723                  *  or binhex'ed file */
                0724         if (!strcmp(buf,
                0725             "(This file must be converted with BinHex 4.0)\n")) {
                0726             state = st_binhex;
                0727             inpart = part_init(infile);
                0728             os_binhex(inpart, part, nparts);
                0729             part_close(inpart);
                0730             goto endbinhex;
                0731         }
                0732         if (strncmp(buf, "begin ", 6)) break;
                0733         /* skip mode */
                0734         p = buf + 6;
                0735         while (*p && !isspace(*p)) p++;
                0736         while (*p && isspace(*p)) p++;
                0737         fname = p;
                0738         while (*p && !isspace(*p)) p++;
                0739         *p = '\0';
                0740         if (!*fname) return 1;
                0741 
                0742         /* Guess the content-type of common filename extensions */
e768bd1221 Jean*0743         if ((p = strrchr(fname, '.'))) {
ec6cf3b09d Ed H*0744             if (!strcasecmp(p, ".gif")) contentType = "image/gif";
                0745             if (!strcasecmp(p, ".jpg")) contentType = "image/jpeg";
                0746             if (!strcasecmp(p, ".jpeg")) contentType = "image/jpeg";
                0747             if (!strcasecmp(p, ".mpg")) contentType = "video/mpeg";
                0748             if (!strcasecmp(p, ".mpeg")) contentType = "video/mpeg";
                0749         }
                0750 
                0751         /* Create output file and start decoding */
                0752         outfile = os_newtypedfile(fname, contentType, FILE_BINARY,
                0753                       (params) 0);
                0754         if (!outfile) {
                0755             fclose(infile);
                0756             return 1;
                0757         }
                0758         state = st_decode;
                0759         break;
                0760 
                0761         case st_inactive:   /* Looking for uuencoded data to resume */
                0762         if (*buf != 'M' || strlen(buf) != line_length) {
                0763             if (*buf == 'B' && !strncmp(buf, "BEGIN", 5)) {
                0764             state = st_decode;
                0765             }
                0766             break;
                0767         }
                0768         state = st_decode;
                0769         /* FALL THROUGH */
                0770         case st_decode: /* Decoding data */
                0771         if (line_length == 0) line_length = strlen(buf);
                0772         if (*buf == 'M' && strlen(buf) == line_length) {
                0773             uudecodeline(buf, outfile);
                0774             break;
                0775         }
                0776         if (strlen(buf) > line_length) {
                0777             state = st_inactive;
                0778             break;
                0779         }
                0780         /*
                0781          * May be on nearing end of file.
                0782          * Save this line in case we are.
                0783          */
                0784         strcpy(lastline, buf);
                0785         if (*buf == ' ' || *buf == '`') {
                0786             state = st_last;
                0787         }
                0788         else {
                0789             state = st_nextlast;
                0790         }
                0791         break;
                0792 
                0793         case st_nextlast:   /* May be nearing end of file */
                0794         if (*buf == ' ' || *buf == '`') {
                0795             state = st_last;
                0796         }
                0797         else {
                0798             state = st_inactive;
                0799         }
                0800         break;
                0801 
                0802         case st_last:   /* Should be at end of file */
                0803         if (!strncmp(buf, "end", 3) && isspace(buf[3])) {
                0804             /* Handle that last line we saved */
                0805             uudecodeline(lastline, outfile);
                0806             fclose(infile);
                0807             os_closetypedfile(outfile);
                0808             for (;part <= nparts; part++) {
                0809             sprintf(buf, "%s%d", dir, part);
                0810             remove(buf);
                0811             }
                0812             sprintf(buf, "%sCT", dir);
                0813             remove(buf);
                0814             os_donewithdir(dir);
                0815             return 0;
                0816         }
                0817         state = st_inactive;
                0818         break;
                0819 
                0820         case st_binhex:
                0821         if (strncmp(buf, "---", 3)) break;
                0822         inpart = part_init(infile);
                0823         os_binhex(inpart, part, nparts);
                0824         part_close(inpart);
                0825         goto endbinhex;
                0826         }
                0827     }
                0828     if (state != st_binhex) state = st_inactive;
                0829     fclose(infile);
                0830     endbinhex:
                0831     sprintf(buf, "%s%d", dir, part);
                0832     remove(buf);
                0833     }
                0834     if (outfile) os_closetypedfile(outfile);
                0835     if (state == st_binhex) os_binhex(0, 0, 0);
                0836     sprintf(buf, "%sCT", dir);
                0837     remove(buf);
                0838     os_donewithdir(dir);
                0839     return 0;
                0840 }
                0841 
                0842 #define DEC(c)  (((c) - ' ') & 077)
                0843 
                0844 /*
                0845  * Decode a uuencoded line to 'outfile'
                0846  */
e768bd1221 Jean*0847 static void uudecodeline(char *line, FILE *outfile)
ec6cf3b09d Ed H*0848 {
                0849     int c, len;
                0850 
                0851     len = DEC(*line++);
                0852     while (len) {
                0853     c = DEC(*line) << 2 | DEC(line[1]) >> 4;
                0854     putc(c, outfile);
                0855     if (--len) {
                0856         c = DEC(line[1]) << 4 | DEC(line[2]) >> 2;
                0857         putc(c, outfile);
                0858         if (--len) {
                0859         c = DEC(line[2]) << 6 | DEC(line[3]);
                0860         putc(c, outfile);
                0861         len--;
                0862         }
                0863     }
                0864     line += 4;
                0865     }
                0866 }
                0867 
                0868