uunconc.c

Go to the documentation of this file.
00001 /*
00002  * This file is part of uudeview, the simple and friendly multi-part multi-
00003  * file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
00004  * be contacted at [email protected]
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  * GNU General Public License for more details.
00015  */
00016 
00017 /*
00018  * These are the functions that are responsible for decoding. The
00019  * original idea is from a freeware utility called "uunconc", and
00020  * few lines of this code may still bear a remote resemblance to
00021  * its code. If you are the author or know him, contact me.
00022  * This program could only decode one multi-part, uuencoded file
00023  * where the parts were in order. Base64, XX and BinHex decoding,
00024  * support for multi-files and part-ordering covered by myself.
00025  **/
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #ifdef _MSC_VER
00029 #include "config.h.win32"
00030 #else
00031 #include "config.h"
00032 #endif
00033 #endif
00034 
00035 #include 
00036 
00037 #ifdef SYSTEM_WINDLL
00038 #include 
00039 #endif
00040 #ifdef SYSTEM_OS2
00041 #include 
00042 #endif
00043 
00044 #include 
00045 #include 
00046 
00047 #ifdef STDC_HEADERS
00048 #include 
00049 #include 
00050 #endif
00051 #ifdef HAVE_UNISTD_H
00052 #include 
00053 #endif
00054 #ifdef HAVE_ERRNO_H
00055 #include 
00056 #endif
00057 
00058 #include <crc32.h>
00059 #include <uudeview.h>
00060 #include <uuint.h>
00061 #include <fptools.h>
00062 #include <uustring.h>
00063 
00064 /****
00065  Some stuff coming from GLib.. Added here because of strange behaviour when using a mixed compiler environment
00066  ****/
00067 #ifdef _MSC_VER
00068 #include 
00069 #include 
00070 #include 
00071 
00072 /*
00073  * create_temp_file based on the mkstemp implementation from the GNU C library.
00074  * Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
00075  */
00076 static gint
00077 create_temp_file (gchar *tmpl, 
00078                   int    permissions)
00079 {
00080   char *XXXXXX;
00081   int count, fd;
00082   static const char letters[] =
00083     "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00084   static const int NLETTERS = sizeof (letters) - 1;
00085   glong value;
00086   GTimeVal tv;
00087   static int counter = 0;
00088 
00089   /* find the last occurrence of "XXXXXX" */
00090   XXXXXX = g_strrstr (tmpl, "XXXXXX");
00091 
00092   if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
00093     {
00094       errno = EINVAL;
00095       return -1;
00096     }
00097 
00098   /* Get some more or less random data. */
00099   g_get_current_time (&tv);
00100   value = (tv.tv_usec ^ tv.tv_sec) + counter++;
00101 
00102   for (count = 0; count < 100; value += 7777, ++count)
00103     {
00104       glong v = value;
00105 
00106       /* Fill in the random bits. */
00107       XXXXXX[0] = letters[v % NLETTERS];
00108       v /= NLETTERS;
00109       XXXXXX[1] = letters[v % NLETTERS];
00110       v /= NLETTERS;
00111       XXXXXX[2] = letters[v % NLETTERS];
00112       v /= NLETTERS;
00113       XXXXXX[3] = letters[v % NLETTERS];
00114       v /= NLETTERS;
00115       XXXXXX[4] = letters[v % NLETTERS];
00116       v /= NLETTERS;
00117       XXXXXX[5] = letters[v % NLETTERS];
00118 
00119       /* tmpl is in UTF-8 on Windows, thus use g_open() */
00120       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, permissions);
00121 
00122       if (fd >= 0)
00123         return fd;
00124       else if (errno != EEXIST)
00125         /* Any other error will apply also to other names we might
00126  * try, and there are 2^32 or so of them, so give up now.
00127  */
00128         return -1;
00129     }
00130 
00131   /* We got out of the loop because we ran out of combinations to try. */
00132   errno = EEXIST;
00133   return -1;
00134 }
00135 #endif
00136 /****
00137  End of GLib code
00138  ****/
00139 
00140 char * uunconc_id = "$Id: uunconc.c 260 2007-05-04 23:32:54Z csk $";
00141 
00142 /* for braindead systems */
00143 #ifndef SEEK_SET
00144 #ifdef L_BEGIN
00145 #define SEEK_SET L_BEGIN
00146 #else
00147 #define SEEK_SET 0
00148 #endif
00149 #endif
00150 
00151 /*
00152  * decoder states
00153  */
00154 
00155 #define BEGIN (1)
00156 #define DATA (2)
00157 #define END (3)
00158 #define DONE (4)
00159 
00160 /*
00161  * mallocable areas
00162  */
00163 
00164 char *uunconc_UUxlat;
00165 char *uunconc_UUxlen;
00166 char *uunconc_B64xlat;
00167 char *uunconc_XXxlat;
00168 char *uunconc_BHxlat;
00169 char *uunconc_save;
00170 
00171 /*
00172  * decoding translation tables and line length table
00173  */
00174 
00175 static int      * UUxlen;       /* initialized in UUInitConc() */
00176 static int      * UUxlat;       /* from the malloc'ed areas above */
00177 static int      * B64xlat;
00178 static int      * XXxlat;
00179 static int      * BHxlat;
00180 
00181 /*
00182  * buffer for decoding
00183  */
00184 
00185 static char *save[3];
00186 
00187 /*
00188  * mallocable areas
00189  */
00190 
00191 char *uuncdl_fulline;
00192 char *uuncdp_oline;
00193 
00194 /*
00195  * Return information for QuickDecode
00196  */
00197 
00198 static int uulboundary;
00199 
00200 /*
00201  * To prevent warnings when using a char as index into an array
00202  */
00203 
00204 #define ACAST(s) ((int)(unsigned char)(s))
00205 
00206 /*
00207  * Initialize decoding tables
00208  */
00209 
00210 void
00211 UUInitConc (void)
00212 {
00213   int i, j;
00214 
00215   /*
00216  * Update pointers
00217  */
00218   UUxlen  = (int *) uunconc_UUxlen;
00219   UUxlat  = (int *) uunconc_UUxlat;
00220   B64xlat = (int *) uunconc_B64xlat;
00221   XXxlat  = (int *) uunconc_XXxlat;
00222   BHxlat  = (int *) uunconc_BHxlat;
00223 
00224   save[0] = uunconc_save;
00225   save[1] = uunconc_save + 256;
00226   save[2] = uunconc_save + 512;
00227 
00228   /* prepare decoding translation table */
00229   for(i = 0; i < 256; i++)
00230     UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
00231 
00232   /*
00233  * At some time I received a file which used lowercase characters for
00234  * uuencoding. This shouldn't be, but let's accept it. Must take special
00235  * care that this doesn't break xxdecoding. This is giving me quite a
00236  * headache. If this one file hadn't been a Pocahontas picture, I might
00237  * have ignored it for good.
00238  */
00239 
00240   for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
00241     UUxlat[i] /* = UUxlat[i+64] */ = j;
00242   for (i = '`', j = 0; i < '`' + 32; i++, j++)
00243     UUxlat[i] = j;
00244 
00245   /* add special cases */
00246   UUxlat['`'] = UUxlat[' '];
00247   UUxlat['~'] = UUxlat['^'];
00248 
00249   /* prepare line length table */
00250   UUxlen[0] = 1;
00251   for(i = 1, j = 5; i <= 61; i += 3, j += 4)
00252     UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
00253 
00254   /* prepare other tables */
00255   for (i=0; i<64; i++) {
00256     B64xlat[ACAST(B64EncodeTable[i])] = i;
00257     XXxlat [ACAST(XXEncodeTable [i])] = i;
00258     BHxlat [ACAST(BHEncodeTable [i])] = i;
00259   }
00260 }
00261 
00262 /*
00263  * Workaround for Netscape
00264  */
00265 
00266 /*
00267  * Determines whether Netscape may have broken up a data line (by
00268  * inserting a newline). This only seems to happen after 
00269  * href statement
00270  */
00271 
00272 static int
00273 UUBrokenByNetscape (char *string)
00274 {
00275   char *ptr;
00276   int len;
00277 
00278   if (string==NULL || (len=strlen(string))<3)
00279     return 0;
00280 
00281   if ((ptr = _FP_stristr (string, "00282     if (_FP_stristr (string, "") > ptr)
00283       return 2;
00284   }
00285 
00286   ptr = string + len;
00287 
00288   while (len && (*(ptr-1)=='\015' || *(ptr-1)=='\012')) {
00289     ptr--; len--;
00290   }
00291   if (len<3)         return 0;
00292   if (*--ptr == ' ') ptr--;
00293   ptr--;
00294 
00295   if (_FP_strnicmp (ptr, ", 2) == 0)
00296     return 1;
00297 
00298   return 0;
00299 }
00300 
00301 /*
00302  * Try to repair a Netscape-corrupted line of data.
00303  * This must only be called on corrupted lines, since non-Netscape
00304  * data may even _get_ corrupted by this procedure.
00305  * 
00306  * Some checks are included multiply to speed up the procedure. For
00307  * example: (*p1!='<' || strnicmp(p1,"",4)). If the first expression
00308  * becomes true, the costly function isn't called :-)
00309  *
00310  * Since '<', '>', '&' might even be replaced by their html equivalents
00311  * in href strings, I'm now using two passes, the first one for & + co,
00312  * the second one for hrefs.
00313  */
00314 
00315 static int
00316 UUNetscapeCollapse (char *string)
00317 {
00318   char *p1=string, *p2=string;
00319   int res = 0;
00320 
00321   if (string==NULL)
00322     return 0;
00323 
00324   /*
00325  * First pass
00326  */
00327   while (*p1) {
00328     if (*p1 == '&') {
00329       if      (_FP_strnicmp (p1, "&", 5) == 0) { p1+=5; *p2++='&'; res=1; }
00330       else if (_FP_strnicmp (p1, "<",  4) == 0) { p1+=4; *p2++='<'; res=1; }
00331       else if (_FP_strnicmp (p1, ">",  4) == 0) { p1+=4; *p2++='>'; res=1; }
00332       else *p2++ = *p1++;
00333     }
00334     else *p2++ = *p1++;
00335   }
00336   *p2 = '\0';
00337   /*
00338  * Second pass
00339  */
00340   p1 = p2 = string;
00341 
00342   while (*p1) {
00343     if (*p1 == '<') {
00344       if ((_FP_strnicmp (p1, ", 7) == 0 ||
00345            _FP_strnicmp (p1, "00346           (_FP_strstr (p1, "") != 0 || _FP_strstr (p1, "") != 0)) {
00347         while (*p1 && *p1!='>')        p1++;
00348         if (*p1=='\0' || *(p1+1)!='<') return 0;
00349         p1++;
00350         while (*p1 && (*p1!='<' || _FP_strnicmp(p1,"",4)!=0)) {
00351           *p2++ = *p1++;
00352         }
00353         if (_FP_strnicmp(p1,"",4) != 0)
00354           return 0;
00355         p1+=4;
00356         res=1;
00357       }
00358       else
00359         *p2++ = *p1++;
00360     }
00361     else
00362       *p2++ = *p1++;
00363   }
00364   *p2 = '\0';
00365 
00366   return res;
00367 }
00368 
00369 /*
00370  * The second parameter is 0 if we are still searching for encoded data,
00371  * otherwise it indicates the encoding we're using right now. If we're
00372  * still in the searching stage, we must be a little more strict in
00373  * deciding for or against encoding; there's too much plain text looking
00374  * like encoded data :-(
00375  */
00376 
00377 int
00378 UUValidData (char *ptr, int encoding, int *bhflag)
00379 {
00380   int i=0, j, len=0, suspicious=0, flag=0;
00381   char *s = ptr;
00382 
00383   if ((s == NULL) || (*s == '\0')) {
00384     return 0;              /* bad string */
00385   }
00386 
00387   while (*s && *s!='\012' && *s!='\015') {
00388     s++;
00389     len++;
00390     i++;
00391   }
00392 
00393   if (i == 0)
00394     return 0;
00395 
00396   switch (encoding) {
00397   case UU_ENCODED:
00398     goto _t_UU;
00399   case XX_ENCODED:
00400     goto _t_XX;
00401   case B64ENCODED:
00402     goto _t_B64;
00403   case BH_ENCODED:
00404     goto _t_Binhex;
00405   case YENC_ENCODED:
00406     return YENC_ENCODED;
00407   }
00408 
00409  _t_Binhex:                 /* Binhex Test */
00410   len = i; s = ptr;
00411 
00412   /*
00413  * bhflag notes the state we're in. Within the data, it's 1. If we're
00414  * still looking for the initial :, it's 0
00415  */
00416   if (*bhflag == 0 && *s != ':') {
00417     if (encoding==BH_ENCODED) return 0;
00418     goto _t_B64;
00419   }
00420   else if (*bhflag == 0 /* *s == ':' */) {
00421     s++; len--;
00422   }
00423 
00424   while (len && BHxlat[ACAST(*s)] != -1) {
00425     len--; s++;
00426   }
00427 
00428   /* allow space characters at the end of the line if we are sure */
00429   /* that this is Binhex encoded data or the line was long enough */
00430 
00431   flag = (*s == ':') ? 0 : 1;
00432 
00433   if (*s == ':' && len>0) {
00434     s++; len--;
00435   }
00436   if (((i>=60 && len<=10) || encoding) && *s==' ') {
00437     while (len && *s==' ') {
00438       s++; len--;
00439     }
00440   }
00441 
00442   /*
00443  * BinHex data shall have exactly 64 characters (except the last
00444  * line). We ignore everything with less than 40 characters to
00445  * be flexible
00446  */
00447 
00448   if (len != 0 || (flag && i < 40)) {
00449     if (encoding==BH_ENCODED) return 0;
00450     goto _t_B64;
00451   }
00452 
00453   *bhflag = flag;
00454 
00455   return BH_ENCODED;
00456 
00457  _t_B64:                    /* Base64 Test */
00458   len = i; s = ptr;
00459 
00460   /*
00461  * Face it: there _are_ Base64 lines that are not a multiple of four
00462  * in length :-(
00463  *
00464  * if (len%4)
00465  * goto _t_UU;
00466  */
00467 
00468   while (len--) {
00469     if (*s < 0 || (B64xlat[ACAST(*s)] == -1 && *s != '=')) {
00470       /* allow space characters at the end of the line if we are sure */
00471       /* that this is Base64 encoded data or the line was long enough */
00472       if (((i>=60 && len<=10) || encoding) && *s++==' ') {
00473         while (*s==' ' && len) s++;
00474         if (len==0) return B64ENCODED;
00475       }
00476       if (encoding==B64ENCODED) return 0;
00477       goto _t_UU;
00478     }
00479     else if (*s == '=') {   /* special case at end */
00480       /* if we know this is B64encoded, allow spaces at end of line */
00481       s++;
00482       if (*s=='=' && len>=1) {
00483         len--; s++;
00484       }
00485       if (encoding && len && *s==' ') {
00486         while (len && *s==' ') {
00487           s++; len--;
00488         }
00489       }
00490       if (len != 0) {
00491         if (encoding==B64ENCODED) return 0;
00492         goto _t_UU;
00493       }
00494       return B64ENCODED;
00495     }
00496     s++;
00497   }
00498   return B64ENCODED;
00499 
00500  _t_UU:
00501   len = i; s = ptr;
00502 
00503   if (UUxlat[ACAST(*s)] == -1) {    /* uutest */
00504     if (encoding==UU_ENCODED) return 0;
00505     goto _t_XX;
00506   }
00507 
00508   j = UUxlen[UUxlat[ACAST(*s)]];
00509 
00510   if (len-1 == j)           /* remove trailing character */
00511     len--;
00512   if (len != j) {
00513     switch (UUxlat[ACAST(*s)]%3) {
00514     case 1:
00515       if (j-2 == len) j-=2;
00516       break;
00517     case 2:
00518       if (j-1 == len) j-=1;
00519       break;
00520     }
00521   }
00522 
00523   /*
00524  * some encoders are broken with respect to encoding the last line of
00525  * a file and produce extraoneous characters beyond the expected EOL
00526  * So were not too picky here about the last line, as long as it's longer
00527  * than necessary and shorter than the maximum
00528  * this tolerance broke the xxdecoding, because xxencoded data was
00529  * detected as being uuencoded :( so don't accept 'h' as first character
00530  * also, if the first character is lowercase, don't accept the line to
00531  * have space characters. the only encoder I've heard of which uses
00532  * lowercase characters at least accepts the special case of encoding
00533  * 0 as `. The strchr() shouldn't be too expensive here as it's only
00534  * evaluated if the first character is lowercase, which really shouldn't
00535  * be in uuencoded text.
00536  */
00537   if (len != j &&
00538       ((ptr[0] == '-' && ptr[1] == '-' && strstr(ptr,"part")!=NULL) ||
00539        !(*ptr != 'M' && *ptr != 'h' &&
00540          len > j && len <= UUxlen[UUxlat['M']]))) {
00541     if (encoding==UU_ENCODED) return 0;
00542     goto _t_XX;             /* bad length */
00543   }
00544 
00545   if (len != j || islower ((unsigned char)*ptr)) {
00546     /*
00547  * if we are not in a 'uuencoded' state, don't allow the line to have
00548  * space characters at all. if we know we _are_ decoding uuencoded
00549  * data, the rest of the line, beyond the length of encoded data, may
00550  * have spaces.
00551  */
00552     if (encoding != UU_ENCODED)
00553       if (strchr (ptr, ' ') != NULL)
00554         goto _t_XX;
00555 
00556 /* suspicious = 1; we're careful here REMOVED 0.4.15 __FP__ */
00557     len        = j;
00558   }
00559 
00560   while (len--) {
00561     if (*s < 0 || UUxlat[ACAST(*s++)] < 0) {
00562       if (encoding==UU_ENCODED) return 0;
00563       goto _t_XX;           /* bad code character */
00564     }
00565     if (*s == ' ' && suspicious) {
00566       if (encoding==UU_ENCODED) return 0;
00567       goto _t_XX;           /* this line looks _too_ suspicious */
00568     }
00569   }
00570   return UU_ENCODED;        /* data is valid */
00571 
00572  _t_XX:                     /* XX Test */
00573   len = i; s = ptr;
00574 
00575   if (XXxlat[ACAST(*s)] == -1)
00576     return 0;
00577 
00578   j = UUxlen[XXxlat[ACAST(*s)]];   /* Same line length table as UUencoding */
00579 
00580   if (len-1 == j)           /* remove trailing character */
00581     len--;
00582   if (len != j)
00583     switch (UUxlat[ACAST(*s)]%3) {
00584     case 1:
00585       if (j-2 == len) j-=2;
00586       break;
00587     case 2:
00588       if (j-1 == len) j-=1;
00589       break;
00590     }
00591   /*
00592  * some encoders are broken with respect to encoding the last line of
00593  * a file and produce extraoneous characters beyond the expected EOL
00594  * So were not too picky here about the last line, as long as it's longer
00595  * than necessary and shorter than the maximum
00596  */
00597   if (len != j && !(*ptr != 'h' && len > j && len <= UUxlen[UUxlat['h']]))
00598     return 0;               /* bad length */
00599 
00600   while(len--) {
00601     if(*s < 0 || XXxlat[ACAST(*s++)] < 0) {
00602       return 0;             /* bad code character */
00603     }
00604   }
00605   return XX_ENCODED;        /* data is valid */
00606 }
00607 
00608 /*
00609  * This function may be called upon a line that does not look like
00610  * valid encoding on first sight, but might be erroneously encoded
00611  * data from Netscape, Lynx or MS Exchange. We might need to read
00612  * a new line from the stream, which is why we need the FILE.
00613  * Returns the type of encoded data if successful or 0 otherwise.
00614  */
00615 
00616 int
00617 UURepairData (FILE *datei, char *line, int encoding, int *bhflag)
00618 {
00619   int nflag, vflag=0, safety=42;
00620   char *ptr;
00621 
00622   nflag = UUBrokenByNetscape (line);
00623 
00624   while (vflag == 0 && nflag && safety--) {
00625     if (nflag == 1) {           /* need next line to repair */
00626       if (strlen (line) > 250)
00627         break;
00628       ptr = line + strlen (line);
00629       while (ptr>line && (*(ptr-1)=='\015' || *(ptr-1)=='\012'))
00630         ptr--;
00631       if (_FP_fgets (ptr, 255-(ptr-line), datei) == NULL)
00632         break;
00633     }
00634     else {                      /* don't need next line to repair */
00635     }
00636     if (UUNetscapeCollapse (line)) {
00637       if ((vflag = UUValidData (line, encoding, bhflag)) == 0)
00638         nflag = UUBrokenByNetscape (line);
00639     }
00640     else
00641       nflag = 0;
00642   }
00643   /*
00644  * Sometimes, a line is garbled even without it being split into
00645  * the next line. Then we try this in our despair
00646  */
00647   if (vflag == 0) {
00648     if (UUNetscapeCollapse (line))
00649       vflag = UUValidData (line, encoding, bhflag);
00650   }
00651 
00652   /*
00653  * If this line looks uuencoded, but the line is one character short
00654  * of a valid line, it was probably broken by MS Exchange. According
00655  * to my test cases, there is at most one space character missing;
00656  * there are never two spaces together.
00657  * If adding a space character helps making this line uuencoded, do
00658  * it!
00659  */
00660 
00661   if (vflag == 0) {
00662     ptr    = line + strlen(line);
00663     while (ptr>line && (*(ptr-1)=='\012' || *(ptr-1)=='\015')) {
00664       ptr--;
00665     }
00666     *ptr++ = ' ';
00667     *ptr-- = '\0';
00668     if ((vflag = UUValidData (line, encoding, bhflag)) != UU_ENCODED) {
00669       *ptr  = '\0';
00670       vflag = 0;
00671     }
00672   }
00673   return vflag;
00674 }
00675 
00676 /*
00677  * Decode a single encoded line using method
00678  */
00679 
00680 size_t
00681 UUDecodeLine (char *s, char *d, int method)
00682 {
00683   int i, j, c, cc, count=0, z1, z2, z3, z4;
00684   static int leftover=0;
00685   int *table;
00686 
00687   /*
00688  * for re-initialization
00689  */
00690 
00691   if (s == NULL || d == NULL) {
00692     leftover = 0;
00693     return 0;
00694   }
00695 
00696   /*
00697  * To shut up gcc -Wall
00698  */
00699   z1 = z2 = z3 = z4 = 0;
00700 
00701   if (method == UU_ENCODED || method == XX_ENCODED) {
00702     if (method == UU_ENCODED)
00703       table = UUxlat;
00704     else
00705       table = XXxlat;
00706 
00707     i = table [ACAST(*s++)];
00708     j = UUxlen[i] - 1;
00709 
00710     while(j > 0) {
00711       c  = table[ACAST(*s++)] << 2;
00712       cc = table[ACAST(*s++)];
00713       c |= (cc >> 4);
00714 
00715       if(i-- > 0)
00716         d[count++] = c;
00717       
00718       cc <<= 4;
00719       c    = table[ACAST(*s++)];
00720       cc  |= (c >> 2);
00721       
00722       if(i-- > 0)
00723         d[count++] = cc;
00724       
00725       c <<= 6;
00726       c |= table[ACAST(*s++)];
00727       
00728       if(i-- > 0)
00729         d[count++] = c;
00730       
00731       j -= 4;
00732     }
00733   }
00734   else if (method == B64ENCODED) {
00735     if (leftover) {
00736       strcpy (uuncdl_fulline+leftover, s);
00737       leftover = 0;
00738       s        = uuncdl_fulline;
00739     }
00740 
00741     while ((z1 = B64xlat[ACAST(*s)]) != -1) {
00742       if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
00743       if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
00744       if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
00745 
00746       d[count++] = (z1 << 2) | (z2 >> 4);
00747       d[count++] = (z2 << 4) | (z3 >> 2);
00748       d[count++] = (z3 << 6) | (z4);
00749 
00750       s += 4;
00751     }
00752     if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
00753       d[count++] = (z1 << 2) | (z2 >> 4);
00754       s+=2;
00755     }
00756     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
00757       d[count++] = (z1 << 2) | (z2 >> 4);
00758       d[count++] = (z2 << 4) | (z3 >> 2);
00759       s+=3;
00760     }
00761     while (B64xlat[ACAST(*s)] != -1)
00762       uuncdl_fulline[leftover++] = *s++;
00763   }
00764   else if (method == BH_ENCODED) {
00765     if (leftover) {
00766       strcpy (uuncdl_fulline+leftover, s);
00767       leftover = 0;
00768       s        = uuncdl_fulline;
00769     }
00770     else if (*s == ':')
00771       s++;
00772 
00773     while ((z1 = BHxlat[ACAST(*s)]) != -1) {
00774       if ((z2 = BHxlat[ACAST(*(s+1))]) == -1) break;
00775       if ((z3 = BHxlat[ACAST(*(s+2))]) == -1) break;
00776       if ((z4 = BHxlat[ACAST(*(s+3))]) == -1) break;
00777 
00778       d[count++] = (z1 << 2) | (z2 >> 4);
00779       d[count++] = (z2 << 4) | (z3 >> 2);
00780       d[count++] = (z3 << 6) | (z4);
00781 
00782       s += 4;
00783     }
00784     if (z1 != -1 && z2 != -1 && *(s+2) == ':') {
00785       d[count++] = (z1 << 2) | (z2 >> 4);
00786       s+=2;
00787     }
00788     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == ':') {
00789       d[count++] = (z1 << 2) | (z2 >> 4);
00790       d[count++] = (z2 << 4) | (z3 >> 2);
00791       s+=3;
00792     }
00793     while (BHxlat[ACAST(*s)] != -1)
00794       uuncdl_fulline[leftover++] = *s++;
00795   }
00796   else if (method == YENC_ENCODED) {
00797     while (*s) {
00798       if (*s == '=') {
00799         if (*++s != '\0') {
00800           d[count++] = (char) ((int) *s - 64 - 42);
00801           s++;
00802         }
00803       }
00804       else if (*s == '\n' || *s == '\r') {
00805         s++; /* ignore */
00806       }
00807       else {
00808         d[count++] = (char) ((int) *s++ - 42);
00809       }
00810     }
00811   }
00812 
00813   return count;
00814 }
00815 
00816 /*
00817  * ``Decode'' Quoted-Printable text
00818  */
00819 
00820 static int
00821 UUDecodeQP (FILE *datain, FILE *dataout, int *state,
00822             long maxpos, int method, int flags,
00823             char *boundary)
00824 {
00825   char *line=uugen_inbuffer, *p1, *p2;
00826   int val;
00827 
00828   uulboundary = -1;
00829 
00830   while (!feof (datain) && 
00831          (ftell(datain)FL_TOEND ||
00832           (!(flags&FL_PROPER) && uu_fast_scanning))) {
00833     if (_FP_fgets (line, 255, datain) == NULL)
00834       break;
00835     if (ferror (datain)) {
00836       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
00837                  uustring (S_SOURCE_READ_ERR),
00838                  strerror (uu_errno = errno));
00839       return UURET_IOERR;
00840     }
00841     line[255] = '\0';
00842 
00843     if (boundary && line[0]=='-' && line[1]=='-' &&
00844         strncmp (line+2, boundary, strlen (boundary)) == 0) {
00845       if (line[strlen(boundary)+2]=='-')
00846         uulboundary = 1;
00847       else
00848         uulboundary = 0;
00849       return UURET_OK;
00850     }
00851 
00852     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
00853       UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
00854                  uustring (S_DECODE_CANCEL));
00855       return UURET_CANCEL;
00856     }
00857 
00858     p1 = p2 = line;
00859 
00860     while (*p2) {
00861       while (*p2 && *p2 != '=')
00862         p2++;
00863       if (*p2 == '\0')
00864         break;
00865       *p2 = '\0';
00866       fprintf (dataout, "%s", p1);
00867       p1  = ++p2;
00868 
00869       if (isxdigit ((unsigned char)*p2) && isxdigit ((unsigned char)*(p2+1))) {
00870         val  = ((isdigit((unsigned char)*p2))    ?  (*p2-'0')   : (tolower(*p2)-'a'+10)) << 4;
00871         val |= ((isdigit((unsigned char)*(p2+1)))?(*(p2+1)-'0') : (tolower(*(p2+1))-'a'+10));
00872 
00873         fputc (val, dataout);
00874         p2 += 2;
00875         p1  = p2;
00876       }
00877       else if (*p2 == '\012' || *(p2+1) == '\015') {
00878         /* soft line break */
00879         *p2 = '\0';
00880         break;
00881       }
00882       else {
00883         /* huh? */
00884         fputc ('=', dataout);
00885       }
00886     }
00887     /*
00888  * p2 points to a nullbyte right after the CR/LF/CRLF
00889  */
00890     val = 0;
00891     while (p2>p1 && isspace ((unsigned char)*(p2-1))) {
00892       if (*(p2-1) == '\012' || *(p2-1) == '\015')
00893         val = 1;
00894       p2--;
00895     }
00896     *p2 = '\0';
00897 
00898     /*
00899  * If the part ends directly after this line, the data does not end
00900  * with a linebreak. Or, as the docs put it, "the CRLF preceding the
00901  * encapsulation line is conceptually attached to the boundary.
00902  * So if the part ends here, don't print a line break"
00903  */
00904     if (val && (!feof (datain) && 
00905                 (ftell(datain)FL_TOEND ||
00906                  (!(flags&FL_PROPER) && uu_fast_scanning))))
00907       fprintf (dataout, "%s\n", p1);
00908     else
00909       fprintf (dataout, "%s", p1);
00910   }
00911   return UURET_OK;
00912 }
00913 
00914 /*
00915  * ``Decode'' plain text. Our job is to properly handle the EOL sequence
00916  */
00917 
00918 static int
00919 UUDecodePT (FILE *datain, FILE *dataout, int *state,
00920             long maxpos, int method, int flags,
00921             char *boundary)
00922 {
00923   char *line=uugen_inbuffer, *ptr;
00924 
00925   uulboundary = -1;
00926 
00927   while (!feof (datain) && 
00928          (ftell(datain)FL_TOEND ||
00929           (!(flags&FL_PROPER) && uu_fast_scanning))) {
00930     if (_FP_fgets (line, 255, datain) == NULL)
00931       break;
00932     if (ferror (datain)) {
00933       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
00934                  uustring (S_SOURCE_READ_ERR),
00935                  strerror (uu_errno = errno));
00936       return UURET_IOERR;
00937     }
00938     line[255] = '\0';
00939 
00940     if (boundary && line[0]=='-' && line[1]=='-' &&
00941         strncmp (line+2, boundary, strlen (boundary)) == 0) {
00942       if (line[strlen(boundary)+2]=='-')
00943         uulboundary = 1;
00944       else
00945         uulboundary = 0;
00946       return UURET_OK;
00947     }
00948 
00949     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
00950       UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
00951                  uustring (S_DECODE_CANCEL));
00952       return UURET_CANCEL;
00953     }
00954 
00955     ptr = line + strlen (line);
00956 
00957     while (ptr>line && (*(ptr-1) == '\012' || *(ptr-1) == '\015'))
00958       ptr--;
00959 
00960 
00961     /*
00962  * If the part ends directly after this line, the data does not end
00963  * with a linebreak. Or, as the docs put it, "the CRLF preceding the
00964  * encapsulation line is conceptually attached to the boundary.
00965  * So if the part ends here, don't print a line break"
00966  */
00967     if ((*ptr == '\012' || *ptr == '\015') &&
00968         (ftell(datain)FL_TOEND || flags&FL_PARTIAL ||
00969          !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
00970       *ptr = '\0';
00971       fprintf (dataout, "%s\n", line);
00972     }
00973     else {
00974       *ptr = '\0';
00975       fprintf (dataout, "%s", line);
00976     }
00977   }
00978   return UURET_OK;
00979 }
00980 
00981 /*
00982  * Decode a single field using method. For the moment, this supports
00983  * Base64 and Quoted Printable only, to support RFC 1522 header decoding.
00984  * Quit when seeing the RFC 1522 ?= end marker.
00985  */
00986 
00987 int
00988 UUDecodeField (char *s, char *d, int method)
00989 {
00990   int z1, z2, z3, z4;
00991   int count=0;
00992 
00993   if (method == B64ENCODED) {
00994     while ((z1 = B64xlat[ACAST(*s)]) != -1) {
00995       if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
00996       if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
00997       if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
00998 
00999       d[count++] = (z1 << 2) | (z2 >> 4);
01000       d[count++] = (z2 << 4) | (z3 >> 2);
01001       d[count++] = (z3 << 6) | (z4);
01002 
01003       s+=4;
01004     }
01005     if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
01006       d[count++] = (z1 << 2) | (z2 >> 4);
01007       s+=2;
01008     }
01009     else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
01010       d[count++] = (z1 << 2) | (z2 >> 4);
01011       d[count++] = (z2 << 4) | (z3 >> 2);
01012       s+=3;
01013     }
01014   }
01015   else if (method == QP_ENCODED) {
01016     while (*s && (*s != '?' || *(s+1) != '=')) {
01017       while (*s && *s != '=' && (*s != '?' || *(s+1) != '=')) {
01018         d[count++] = *s++;
01019       }
01020       if (*s == '=') {
01021         if (isxdigit ((unsigned char)*(s+1)) && isxdigit ((unsigned char)*(s+2))) {
01022           d[count]  = (isdigit ((unsigned char)*(s+1)) ? (*(s+1)-'0') : (tolower (*(s+1))-'a'+10)) << 4;
01023           d[count] |= (isdigit ((unsigned char)*(s+2)) ? (*(s+2)-'0') : (tolower (*(s+2))-'a'+10));
01024           count++;
01025           s+=3;
01026         }
01027         else if (*(s+1) == '\012' || *(s+1) == '\015') {
01028           s+=2;
01029         }
01030         else {
01031           d[count++] = *s++;
01032         }
01033       }
01034     }
01035   }
01036   else {
01037     return -1;
01038   }
01039 
01040   d[count] = '\0';
01041   return count;
01042 }
01043 
01044 int
01045 UUDecodePart (FILE *datain, FILE *dataout, int *state,
01046               long maxpos, int method, int flags,
01047               char *boundary)
01048 {
01049   char *line = uugen_fnbuffer;
01050   int line_len = UUGEN_FNBUFFER_LEN;
01051   char *oline=uuncdp_oline;
01052   int warning=0, vlc=0, lc[2], hadct=0;
01053   int tc=0, tf=0, vflag, haddata=0, haddh=0;
01054   long yefilesize=0, yepartends=0;
01055   crc32_t yepartcrc=crc32(0L, Z_NULL, 0);
01056   static crc32_t yefilecrc=0;
01057   static int bhflag=0;
01058   size_t count=0;
01059   size_t yepartsize=0;
01060   char *ptr;
01061 
01062   if (datain == NULL || dataout == NULL) {
01063     yefilecrc = crc32(0L, Z_NULL, 0);
01064     bhflag = 0;
01065     return UURET_OK;
01066   }
01067 
01068   /*
01069  * Use specialized functions for QP_ENCODED and PT_ENCODED plaintext
01070  */
01071 
01072   if (method == QP_ENCODED)
01073     return UUDecodeQP (datain, dataout, state, maxpos,
01074                        method, flags, boundary);
01075   else if (method == PT_ENCODED)
01076     return UUDecodePT (datain, dataout, state, maxpos,
01077                        method, flags, boundary);
01078 
01079   lc[0] = lc[1] = 0;
01080   vflag = 0;
01081 
01082   uulboundary = -1;
01083 
01084   if (method == YENC_ENCODED) {
01085     *state = BEGIN;
01086   }
01087 
01088   while (!feof (datain) && *state != DONE && 
01089          (ftell(datain)FL_TOEND || maxpos==-1 ||
01090           (!(flags&FL_PROPER) && uu_fast_scanning))) {
01091     if (_FP_fgets (line, line_len, datain) == NULL)
01092       break;
01093 
01094     if (ferror (datain)) {
01095       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01096                  uustring (S_SOURCE_READ_ERR),
01097                  strerror (uu_errno = errno));
01098       return UURET_IOERR;
01099     }
01100 
01101     if (line[0]=='\015' || line[0]=='\012') { /* Empty line? */
01102       if (*state == DATA &&
01103           (method == UU_ENCODED || method == XX_ENCODED))
01104         *state = END;
01105 
01106       /*
01107  * if we had a whole block of valid lines before, we reset our
01108  * 'valid data' flag, tf. Without this 'if', we'd break decoding
01109  * files with interleaved blank lines. The value of 5 is chosen
01110  * quite arbitrarly.
01111  */
01112 
01113       if (vlc > 5)
01114         tf = tc = 0;
01115       vlc = 0;
01116       continue;
01117     }
01118     
01119     /*
01120  * Busy Polls
01121  */
01122 
01123     if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
01124       UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
01125                  uustring (S_DECODE_CANCEL));
01126       return UURET_CANCEL;
01127     }
01128 
01129     /*
01130  * try to make sense of data
01131  */
01132 
01133     line[255] = '\0'; /* For Safety of string functions */
01134     count     =  0;
01135 
01136     if (boundary && line[0]=='-' && line[1]=='-' &&
01137         strncmp (line+2, boundary, strlen (boundary)) == 0) {
01138       if (line[strlen(boundary)+2]=='-')
01139         uulboundary = 1;
01140       else
01141         uulboundary = 0;
01142       return UURET_OK;
01143     }
01144 
01145     /*
01146  * Use this pseudo-handling only if !FL_PROPER
01147  */
01148 
01149     if ((flags&FL_PROPER) == 0) {
01150       if (strncmp (line, "BEGIN", 5) == 0 &&
01151           _FP_strstr  (line, "CUT HERE")  && !tf) { /* I hate these lines */
01152         tc = tf = vlc = 0;
01153         continue;
01154       }
01155       /* MIME body boundary */
01156       if (line[0] == '-' && line[1] == '-' && method == B64ENCODED) {
01157         if ((haddata || tc) && (haddh || hadct)) {
01158           *state = DONE;
01159           vlc   = 0;
01160           lc[0] = lc[1] = 0;
01161           continue;
01162         }
01163         hadct = 0;
01164         haddh = 1;
01165         continue;
01166       }
01167       if (_FP_strnicmp (line, "Content-Type", 12) == 0)
01168         hadct = 1;
01169     }
01170 
01171     if (*state == BEGIN) {
01172       if ((method == UU_ENCODED || method == XX_ENCODED) &&
01173           (strncmp      (line, "begin ",       6) == 0 ||
01174            _FP_strnicmp (line, "
begin ", 11) == 0)) { /* for LYNX */
01175         *state = DATA;
01176         continue;
01177       }
01178       else if (method == BH_ENCODED && line[0] == ':') {
01179         if (UUValidData (line, BH_ENCODED, &bhflag) == BH_ENCODED) {
01180           bhflag = 0;
01181           *state = DATA;
01182         }
01183         else
01184           continue;
01185       }
01186       else if (method == YENC_ENCODED &&
01187                strncmp (line, "=ybegin ", 8) == 0 &&
01188                _FP_strstr (line, " name=") != NULL) {
01189         *state = DATA;
01190 
01191         if ((ptr = _FP_strstr (line, " size=")) != NULL) {
01192           ptr += 6;
01193           yefilesize = atoi (ptr);
01194         }
01195         else {
01196           yefilesize = -1;
01197         }
01198 
01199         if (_FP_strstr (line, " part=") != NULL) {
01200           if (_FP_fgets (line, line_len, datain) == NULL) {
01201             break;
01202           }
01203 
01204           if ((ptr = _FP_strstr (line, " end=")) == NULL) {
01205             break;
01206           }
01207        
01208           yepartends = atoi (ptr + 5);
01209         }
01210         tf = 1;
01211         continue;
01212       }
01213       else {
01214         continue;
01215       }
01216       
01217       tc = tf = vlc = 0;
01218       lc[0] = lc[1] = 0;
01219     }
01220     else if ((*state == END) &&
01221              (method == UU_ENCODED || method == XX_ENCODED)) {
01222       if (strncmp (line, "end", 3) == 0) {
01223         *state = DONE;
01224         break;
01225       }
01226     }
01227 
01228     if (*state == DATA && method == YENC_ENCODED &&
01229         strncmp (line, "=yend ", 6) == 0) {
01230       if ((ptr = _FP_strstr (line, " pcrc32=")) != NULL) {
01231         crc32_t pcrc32 = strtoul (ptr + 8, NULL, 16);
01232         if (pcrc32 != yepartcrc) {
01233           UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01234                      uustring (S_PCRC_MISMATCH), progress.curfile, progress.partno);
01235         }
01236       }
01237       if ((ptr = _FP_strstr (line, " crc32=")) != NULL)
01238       {
01239         crc32_t fcrc32 = strtoul (ptr + 7, NULL, 16);
01240         if (fcrc32 != yefilecrc) {
01241           UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01242                      uustring (S_CRC_MISMATCH), progress.curfile);
01243         }
01244       }
01245       if ((ptr = _FP_strstr (line, " size=")) != NULL)
01246       {
01247         size_t size = atol(ptr + 6);
01248         if (size != yepartsize && yefilesize != -1) {
01249           if (size != yefilesize)
01250             UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01251                        uustring (S_PSIZE_MISMATCH), progress.curfile,
01252                        progress.partno, yepartsize, size);
01253           else
01254             UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01255                        uustring (S_SIZE_MISMATCH), progress.curfile,
01256                        yepartsize, size);
01257         }
01258       }
01259       if (yepartends == 0 || yepartends >= yefilesize) {
01260         *state = DONE;
01261       }
01262       break;
01263     }
01264 
01265     if (*state == DATA || *state == END) {
01266       if (method==B64ENCODED && line[0]=='-' && line[1]=='-' && tc) {
01267         break;
01268       }
01269 
01270       if ((vflag = UUValidData (line, (tf)?method:0, &bhflag)) == 0)
01271         vflag = UURepairData (datain, line, (tf)?method:0, &bhflag);
01272 
01273       /*
01274  * correct XX/UUencoded lines that were declared Base64
01275  */
01276 
01277       if ((method == XX_ENCODED || method == UU_ENCODED) &&
01278           vflag == B64ENCODED) {
01279         if (UUValidData (line, method, &bhflag) == method)
01280           vflag = method;
01281       }
01282 
01283       if (vflag == method) {
01284         if (tf) {
01285           count  = UUDecodeLine (line, oline, method);
01286           if (method == YENC_ENCODED) {
01287             if (yepartends)
01288               yepartcrc = crc32(yepartcrc, (unsigned char*)oline, count);
01289             yefilecrc = crc32(yefilecrc, (unsigned char*)oline, count);
01290             yepartsize += count;
01291           }
01292           vlc++; lc[1]++;
01293         }
01294         else if (tc == 3) {
01295           count  = UUDecodeLine (save[0], oline,         method);
01296           count += UUDecodeLine (save[1], oline + count, method);
01297           count += UUDecodeLine (save[2], oline + count, method);
01298           count += UUDecodeLine (line,    oline + count, method);
01299           tf     = 1;
01300           tc     = 0;
01301 
01302           /*
01303  * complain if we had one or two invalid lines amidst of
01304  * correctly encoded data. This usually means that the
01305  * file is in error
01306  */
01307 
01308           if (lc[1] > 10 && (lc[0] >= 1 && lc[0] <= 2) && !warning) {
01309             UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01310                        uustring (S_DATA_SUSPICIOUS));
01311             warning=1;
01312           }
01313           lc[0] = 0;
01314           lc[1] = 3;
01315         }
01316         else {
01317           _FP_strncpy (save[tc++], line, 256);
01318         }
01319 
01320         if (method == UU_ENCODED)
01321           *state = (line[0] == 'M') ? DATA : END;
01322         else if (method == XX_ENCODED)
01323           *state = (line[0] == 'h') ? DATA : END;
01324         else if (method == B64ENCODED)
01325           *state = (strchr (line, '=') == NULL) ? DATA : DONE;
01326         else if (method == BH_ENCODED)
01327           *state = (!line[0] || strchr(line+1,':')==NULL)?DATA:DONE;
01328       }
01329       else {
01330         vlc = tf = tc = 0;
01331         haddh = 0;
01332         lc[0]++;
01333       }
01334     }
01335     else if (*state != DONE) {
01336       return UURET_NOEND;
01337     }
01338 
01339     if (count) {
01340       if (method == BH_ENCODED) {
01341         if (UUbhwrite (oline, 1, count, dataout) != count) {
01342           UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01343                      uustring (S_WR_ERR_TEMP),
01344                      strerror (uu_errno = errno));
01345           return UURET_IOERR;
01346         }
01347       }
01348       else if (fwrite (oline, 1, count, dataout) != count) {
01349         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01350                    uustring (S_WR_ERR_TEMP),
01351                    strerror (uu_errno = errno));
01352         return UURET_IOERR;
01353       }
01354       haddata++;
01355       count = 0;
01356     }
01357   }
01358 
01359   if (*state  == DONE ||
01360       (*state == DATA && method == B64ENCODED &&
01361        vflag == B64ENCODED && (flags&FL_PROPER || haddh))) {
01362     for (tf=0; tf01363       count += UUDecodeLine (save[tf], oline + count, method);
01364     if (count) {
01365       if (method == BH_ENCODED) {
01366         if (UUbhwrite (oline, 1, count, dataout) != count) {
01367           UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01368                      uustring (S_WR_ERR_TEMP),
01369                      strerror (uu_errno = errno));
01370           return UURET_IOERR;
01371         }
01372       }
01373       else if (fwrite (oline, 1, count, dataout) != count) {
01374         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01375                    uustring (S_WR_ERR_TEMP),
01376                    strerror (uu_errno = errno));
01377         return UURET_IOERR;
01378       }
01379     }
01380   }
01381   return UURET_OK;
01382 }
01383 
01384 /*
01385  * this function decodes the file into a temporary file
01386  */
01387 
01388 int
01389 UUDecode (uulist *data)
01390 {
01391   int state=BEGIN, part=-1, res=0, hb;
01392   long rsize, dsize, numbytes;
01393   FILE *datain, *dataout;
01394   unsigned char r[8];
01395   char *mode, *ntmp, *template;
01396   uufile *iter;
01397   size_t bytes;
01398   int fd;
01399 
01400   if (data == NULL || data->thisfile == NULL)
01401     return UURET_ILLVAL;
01402 
01403   if (data->state & UUFILE_TMPFILE)
01404     return UURET_OK;
01405 
01406   if (data->state & UUFILE_NODATA)
01407     return UURET_NODATA;
01408 
01409   if (data->state & UUFILE_NOBEGIN) {
01410     if (!uu_desperate)
01411       return UURET_NODATA;
01412     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01413                uustring(S_DATA_SUSPICIOUS));
01414   }
01415 
01416   if (data->uudet == PT_ENCODED)
01417     mode = "wt";        /* open text files in text mode */
01418   else
01419     mode = "wb";        /* otherwise in binary */
01420 
01421 
01422   template = g_build_filename (g_get_tmp_dir(), "uuXXXXXX", NULL);
01423   data->binfile = strdup (template);
01424 #ifdef _MSC_VER
01425   fd = create_temp_file (data->binfile, 0600);
01426 #else
01427   fd = g_mkstemp (data->binfile);
01428 #endif
01429   g_free (template);
01430 
01431   if (fd == -1) {
01432     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01433                uustring (S_NO_TEMP_NAME));
01434     _FP_free (data->binfile);
01435     data->binfile = NULL;
01436     uu_errno = errno;
01437     return UURET_NOMEM;
01438   }
01439 
01440   if ((dataout = fdopen (fd, mode)) == NULL) {
01441     /*
01442  * we couldn't create a temporary file. Usually this means that TMP
01443  * and TEMP aren't set
01444  */
01445     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01446                uustring (S_WR_ERR_TARGET),
01447                data->binfile, strerror (uu_errno = errno));
01448     _FP_free (data->binfile);
01449     data->binfile = NULL;
01450     uu_errno = errno;
01451     return UURET_IOERR;
01452   }
01453   /*
01454  * we don't have begin lines in Base64 or plain text files.
01455  */
01456   if (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
01457       data->uudet == PT_ENCODED)
01458     state = DATA;
01459 
01460   /*
01461  * If we know that the file does not have a begin, we simulate
01462  * it in desperate mode
01463  */
01464 
01465   if ((data->state & UUFILE_NOBEGIN) && uu_desperate) {
01466     UUMessage (uunconc_id, __LINE__, UUMSG_WARNING, uustring(S_DATA_SUSPICIOUS));
01467     state = DATA;
01468   }
01469 
01470   (void) UUDecodeLine (NULL, NULL, 0);                   /* init */
01471   (void) UUbhwrite    (NULL, 0, 0, NULL);                /* dito */
01472   (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* yep */
01473 
01474   /*
01475  * initialize progress information
01476  */
01477   progress.action = 0;
01478   if (data->filename != NULL) {
01479     _FP_strncpy (progress.curfile,
01480                  (strlen(data->filename)>255)?
01481                  (data->filename+strlen(data->filename)-255):data->filename,
01482                  256);
01483   }
01484   else {
01485     _FP_strncpy (progress.curfile,
01486                  (strlen(data->binfile)>255)?
01487                  (data->binfile+strlen(data->binfile)-255):data->binfile,
01488                  256);
01489   }
01490   progress.partno   =  0;
01491   progress.numparts =  0;
01492   progress.fsize    = -1;
01493   progress.percent  =  0;
01494   progress.action   =  UUACT_DECODING;
01495 
01496   iter = data->thisfile;
01497   while (iter) {
01498     progress.numparts = (iter->partno)?iter->partno:1;
01499     iter = iter->NEXT;
01500   }
01501   
01502   /*
01503  * let's rock!
01504  */
01505 
01506   iter = data->thisfile;
01507   while (iter) {
01508 
01509     if (part==-1 && iter->partno!=1)
01510        UUMessage (uunconc_id, __LINE__, UUMSG_WARNING, "Missing everything before part #%d", iter->partno);
01511     if (part!=-1 && iter->partno!=part+1) {
01512       if (!uu_desperate)
01513         break;
01514       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01515                  uustring(S_PART_MISSING), part);
01516     }
01517     part = iter->partno;
01518 
01519     if (iter->data->sfname == NULL) {
01520       iter = iter->NEXT;
01521       continue;
01522     }
01523 
01524     /*
01525  * call our FileCallback to retrieve the file
01526  */
01527 
01528     if (uu_FileCallback) {
01529       if ((res = (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
01530                                      uugen_fnbuffer, 1)) != UURET_OK)
01531         break;
01532       if ((datain = fopen (uugen_fnbuffer, "rb")) == NULL) {
01533         (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
01534                             uugen_fnbuffer, 0);
01535         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01536                    uustring (S_NOT_OPEN_FILE),
01537                    uugen_fnbuffer, strerror (uu_errno = errno));
01538         res = UURET_IOERR;
01539         break;
01540       }
01541     }
01542     else {
01543       if ((datain = fopen (iter->data->sfname, "rb")) == NULL) {
01544         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01545                    uustring (S_NOT_OPEN_FILE),
01546                    iter->data->sfname, strerror (uu_errno = errno));
01547         res = UURET_IOERR;
01548         break;
01549       }
01550       UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
01551                  uustring (S_OPEN_FILE),
01552                  iter->data->sfname);
01553       _FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
01554     }
01555 
01556     progress.partno  = part;
01557     progress.fsize   = (iter->data->length)?iter->data->length:-1;
01558     progress.percent = 0;
01559     progress.foffset = iter->data->startpos;
01560 
01561     fseek              (datain, iter->data->startpos, SEEK_SET);
01562     res = UUDecodePart (datain, dataout, &state,
01563                         iter->data->startpos+iter->data->length,
01564                         data->uudet, iter->data->flags, NULL);
01565     fclose             (datain);
01566 
01567     if (uu_FileCallback)
01568       (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname, uugen_fnbuffer, 0);
01569 
01570     if (state == DONE || res != UURET_OK)
01571       break;
01572 
01573     iter = iter->NEXT;
01574   }
01575 
01576   if (state == DATA && 
01577       (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
01578        data->uudet == PT_ENCODED))
01579     state = DONE; /* assume we're done */
01580 
01581   if (fclose (dataout)) {
01582     UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01583                uustring (S_WR_ERR_TEMP),
01584                strerror (uu_errno = errno));
01585     res = UURET_IOERR;
01586   }
01587 
01588   if (res != UURET_OK || (state != DONE && !uu_desperate)) {
01589     unlink (data->binfile);
01590     _FP_free (data->binfile);
01591     data->binfile = NULL;
01592     data->state  &= ~UUFILE_TMPFILE;
01593     data->state  |=  UUFILE_ERROR;
01594 
01595     if (res == UURET_OK && state != DONE)
01596       res = UURET_NOEND;
01597   }
01598   else if (res != UURET_OK) {
01599     data->state &= ~UUFILE_DECODED;
01600     data->state |=  UUFILE_ERROR | UUFILE_TMPFILE;
01601   }
01602   else {
01603     data->state &= ~UUFILE_ERROR;
01604     data->state |=  UUFILE_TMPFILE;
01605   }
01606 
01607   /*
01608  * If this was a BinHex file, we must extract its data or resource fork
01609  */
01610 
01611   if (data->uudet == BH_ENCODED && data->binfile)
01612   {
01613     char * template = g_build_filename (g_get_tmp_dir(), "uuXXXXXX", NULL);
01614     ntmp = strdup (template);
01615     fd = g_mkstemp (ntmp);
01616     g_free (template);
01617 
01618     if (fd == -1) {
01619       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01620                  uustring (S_NO_TEMP_NAME));
01621       progress.action = 0;
01622       free (ntmp);
01623       return UURET_NOMEM;
01624     }
01625     if ((datain = fdopen (fd, "rb")) == NULL) {
01626       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01627                  uustring (S_NOT_OPEN_FILE),
01628                  data->binfile, strerror (uu_errno = errno));
01629       progress.action = 0;
01630       free (ntmp);
01631       return UURET_IOERR;
01632     }
01633     if ((dataout = fopen (ntmp, "wb")) == NULL) {
01634       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01635                  uustring (S_NOT_OPEN_TARGET),
01636                  ntmp, strerror (uu_errno = errno));
01637       progress.action = 0;
01638       fclose (datain);
01639       free   (ntmp);
01640       return UURET_IOERR;
01641     }
01642     /*
01643  * read fork lengths. remember they're in Motorola format
01644  */
01645     r[0] = fgetc (datain);
01646     hb   = (int) r[0] + 22;
01647     fseek (datain, (int) r[0] + 12, SEEK_SET);
01648     fread (r, 1, 8, datain);
01649 
01650     dsize = (((long) 1 << 24) * (long) r[0]) +
01651             (((long) 1 << 16) * (long) r[1]) +
01652             (((long) 1 <<  8) * (long) r[2]) +
01653             (                   (long) r[3]);
01654     rsize = (((long) 1 << 24) * (long) r[4]) +
01655             (((long) 1 << 16) * (long) r[5]) +
01656             (((long) 1 <<  8) * (long) r[6]) +
01657             (                   (long) r[7]);
01658 
01659     UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
01660                uustring (S_BINHEX_SIZES),
01661                dsize, rsize);
01662 
01663     if (dsize == 0) {
01664       fseek  (datain, dsize + hb + 2, SEEK_SET);
01665       numbytes = rsize;
01666     }
01667     else if (rsize == 0) {
01668       fseek  (datain, hb, SEEK_SET);
01669       numbytes = dsize;
01670     }
01671     else {
01672       /* we should let the user have the choice here */
01673       UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
01674                  uustring (S_BINHEX_BOTH));
01675       fseek  (datain, hb, SEEK_SET);
01676       numbytes = dsize;
01677     }
01678 
01679     progress.action   = 0;
01680     progress.partno   = 0;
01681     progress.numparts = 1;
01682     progress.fsize    = (numbytes)?numbytes:-1;
01683     progress.foffset  = hb;
01684     progress.percent  = 0;
01685     progress.action   = UUACT_COPYING;
01686 
01687     /*
01688  * copy the chosen fork
01689  */
01690 
01691     while (!feof (datain) && numbytes) {
01692       if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
01693         UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
01694                    uustring (S_DECODE_CANCEL));
01695         fclose (datain);
01696         fclose (dataout);
01697         unlink (ntmp);
01698         free   (ntmp);
01699         return UURET_CANCEL;
01700       }
01701 
01702       bytes = fread (uugen_inbuffer, 1,
01703                      (size_t) ((numbytes>1024)?1024:numbytes), datain);
01704 
01705       if (ferror (datain) || (bytes == 0 && !feof (datain))) {
01706         progress.action = 0;
01707         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01708                    uustring (S_SOURCE_READ_ERR),
01709                    data->binfile, strerror (uu_errno = errno));
01710         fclose (datain);
01711         fclose (dataout);
01712         unlink (ntmp);
01713         free   (ntmp);
01714         return UURET_IOERR;
01715       }
01716       if (fwrite (uugen_inbuffer, 1, bytes, dataout) != bytes) {
01717         progress.action = 0;
01718         UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01719                    uustring (S_WR_ERR_TARGET),
01720                    ntmp, strerror (uu_errno = errno));
01721         fclose (datain);
01722         fclose (dataout);
01723         unlink (ntmp);
01724         free   (ntmp);
01725         return UURET_IOERR;
01726       }
01727       numbytes -= bytes;
01728     }
01729 
01730     if (numbytes) {
01731       UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01732                  uustring (S_SHORT_BINHEX),
01733                  (data->filename)?data->filename:
01734                  (data->subfname)?data->subfname:"???",
01735                  numbytes);
01736     }
01737 
01738     /*
01739  * replace temp file
01740  */
01741 
01742     fclose (datain);
01743     if (fclose (dataout)) {
01744       UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
01745                  uustring (S_WR_ERR_TARGET),
01746                  ntmp, strerror (uu_errno = errno));
01747       unlink (ntmp);
01748       free   (ntmp);
01749       return UURET_IOERR;
01750     }
01751 
01752     if (unlink (data->binfile)) {
01753       UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
01754                  uustring (S_TMP_NOT_REMOVED),
01755                  data->binfile, strerror (uu_errno = errno));
01756     }
01757 
01758     free (data->binfile);
01759     data->binfile = ntmp;
01760   }
01761 
01762   progress.action = 0;
01763   return res;
01764 }
01765 
01766 /*
01767  * QuickDecode for proper MIME attachments. We expect the pointer to
01768  * be on the first header line.
01769  */
01770 
01771 int
01772 UUQuickDecode (FILE *datain, FILE *dataout, char *boundary, long maxpos)
01773 {
01774   int state=BEGIN, encoding=-1;
01775   headers myenv;
01776 
01777   /*
01778  * Read header and find out about encoding.
01779  */
01780 
01781   memset (&myenv, 0, sizeof (headers));
01782   UUScanHeader (datain, &myenv);
01783 
01784   if (_FP_stristr (myenv.ctenc, "uu") != NULL)
01785     encoding = UU_ENCODED;
01786   else if (_FP_stristr (myenv.ctenc, "xx") != NULL)
01787     encoding = XX_ENCODED;
01788   else if (_FP_stricmp (myenv.ctenc, "base64") == 0)
01789     encoding = B64ENCODED;
01790   else if (_FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
01791     encoding = QP_ENCODED;
01792   else
01793     encoding = PT_ENCODED;
01794 
01795   UUkillheaders (&myenv);
01796 
01797   /*
01798  * okay, so decode this one
01799  */
01800 
01801   (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* init */
01802   return UUDecodePart (datain, dataout, &state, maxpos,
01803                        encoding, FL_PROPER|FL_TOEND,
01804                        boundary);
01805 }

Generated on Sun Oct 12 01:45:30 2008 for NNTPGrab by  1.5.4