3171 lines
86 KiB
C++
3171 lines
86 KiB
C++
/*
|
|
* This file is part of uudeview, the simple and friendly multi-part multi-
|
|
* file uudecoder program (c) 1994-2001 by Frank Pilhofer. The author may
|
|
* be contacted at fp@fpx.de
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
/*
|
|
* These are very central functions of UUDeview. Here, we scan a file
|
|
* and decide whether it contains encoded data or not. ScanPart() must
|
|
* be called repeatedly on the same file until feof(file). Each time,
|
|
* it returns information about the next part found within.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef SYSTEM_WINDLL
|
|
#include <windows.h>
|
|
#endif
|
|
#ifdef SYSTEM_OS2
|
|
#include <os2.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef STDC_HEADERS
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_MALLOC_H
|
|
#include <malloc.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include <gdefs.h>
|
|
#include <gctype.h>
|
|
#include <uudeview.h>
|
|
#include <uuint.h>
|
|
#include <fptools.h>
|
|
#include <uustring.h>
|
|
|
|
char * uuscan_id = "$Id$";
|
|
|
|
/*
|
|
* Header fields we recognize as such. See RFC822. We add "From ",
|
|
* the usual marker for a beginning of a new message, and a couple
|
|
* of usual MDA, News and MIME headers.
|
|
* We make a distinction of MIME headers as we need the difference
|
|
* to scan the bodies from partial multipart messages.
|
|
*/
|
|
|
|
static char *knownmsgheaders[] = {
|
|
"From ", "Return-Path:", "Received:", "Reply-To:",
|
|
"From:", "Sender:", "Resent-Reply-To:", "Resent-From:",
|
|
"Resent-Sender:", "Date:", "Resent-Date:", "To:",
|
|
"Resent-To:", "Cc:", "Bcc:", "Resent-bcc:",
|
|
"Message-ID:", "Resent-Message-Id:", "In-Reply-To:",
|
|
"References:", "Keywords:", "Subject:", "Comments:",
|
|
|
|
"Delivery-Date:", "Posted-Date:", "Received-Date:",
|
|
"Precedence:",
|
|
|
|
"Path:", "Newsgroups:", "Organization:", "Lines:",
|
|
"NNTP-Posting-Host:",
|
|
NULL
|
|
};
|
|
|
|
static char *knownmimeheaders[] = {
|
|
"Mime-Version:", "Content-Transfer-Encoding:",
|
|
"Content-Type:", "Content-Disposition:",
|
|
"Content-Description:", "Content-Length:",
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* for MIME (plaintext) parts without filename
|
|
*/
|
|
int mimseqno;
|
|
|
|
/*
|
|
* how many lines do we read when checking for headers
|
|
*/
|
|
#define WAITHEADER 10
|
|
|
|
/*
|
|
* The stack for encapsulated Multipart messages
|
|
*/
|
|
#define MSMAXDEPTH 3
|
|
|
|
int mssdepth = 0;
|
|
scanstate multistack[MSMAXDEPTH+1];
|
|
|
|
/*
|
|
* The state and the local envelope
|
|
*/
|
|
headers localenv;
|
|
scanstate sstate;
|
|
|
|
/*
|
|
* mallocable areas
|
|
*/
|
|
|
|
char *uuscan_shlline;
|
|
char *uuscan_pvvalue;
|
|
char *uuscan_phtext;
|
|
char *uuscan_sdline;
|
|
char *uuscan_sdbhds1;
|
|
char *uuscan_sdbhds2;
|
|
char *uuscan_spline;
|
|
|
|
/*
|
|
* Macro: print cancellation message in UUScanPart
|
|
*/
|
|
|
|
#define SPCANCEL() {UUMessage(uuscan_id,__LINE__,UUMSG_NOTE,uustring(S_SCAN_CANCEL));*errcode=UURET_CANCEL;goto ScanPartEmergency;}
|
|
|
|
/*
|
|
* Is line empty? A line is empty if it is composed of whitespace.
|
|
*/
|
|
|
|
static int
|
|
IsLineEmpty (char *data)
|
|
{
|
|
if (data == NULL) return 0;
|
|
while (*data && isspace (*data)) data++;
|
|
return ((*data)?0:1);
|
|
}
|
|
|
|
/*
|
|
* Scans a potentially folded header line from the input file. If
|
|
* initial is non-NULL, it is the first line of the header, useful
|
|
* if the calling function just coincidentally noticed that this is
|
|
* a header.
|
|
* RFC0822 does not specify a maximum length for headers, but we
|
|
* truncate everything beyond an insane value of 1024 characters.
|
|
*/
|
|
|
|
static char *
|
|
ScanHeaderLine (FILE *datei, char *initial)
|
|
{
|
|
char *ptr=uuscan_shlline;
|
|
int llength, c;
|
|
long curpos;
|
|
int hadcr;
|
|
|
|
if (initial) {
|
|
_FP_strncpy (uuscan_shlline, initial, 1024);
|
|
}
|
|
else {
|
|
/* read first line */
|
|
if (feof (datei) || ferror (datei))
|
|
return NULL;
|
|
if (_FP_fgets (uuscan_shlline, 1023, datei) == NULL)
|
|
return NULL;
|
|
uuscan_shlline[1023] = '\0';
|
|
}
|
|
|
|
llength = strlen (uuscan_shlline);
|
|
hadcr = 0;
|
|
|
|
/* strip whitespace at end */
|
|
ptr = uuscan_shlline + llength;
|
|
while (llength && isspace(*(ptr-1))) {
|
|
if (*(ptr-1) == '\012' || *(ptr-1) == '\015')
|
|
hadcr = 1;
|
|
ptr--; llength--;
|
|
}
|
|
if (llength == 0) {
|
|
uuscan_shlline[0] = '\0';
|
|
return uuscan_shlline;
|
|
}
|
|
|
|
while (!feof (datei)) {
|
|
c = fgetc (datei);
|
|
if (feof (datei))
|
|
break;
|
|
|
|
/*
|
|
* If the line didn't have a CR, it was longer than 256 characters
|
|
* and is continued anyway.
|
|
*/
|
|
|
|
if (hadcr==1 && c != ' ' && c != '\t') {
|
|
/* no LWSP-char, header line does not continue */
|
|
ungetc (c, datei);
|
|
break;
|
|
}
|
|
while (!feof (datei) && (c == ' ' || c == '\t'))
|
|
c = fgetc (datei);
|
|
|
|
if (!feof (datei))
|
|
ungetc (c, datei); /* push back for fgets() */
|
|
|
|
/* insert a single LWSP */
|
|
if (hadcr==1 && llength < 1023) {
|
|
*ptr++ = ' ';
|
|
llength++;
|
|
}
|
|
*ptr = '\0'; /* make lint happier */
|
|
|
|
if (feof (datei))
|
|
break;
|
|
|
|
/* read next line */
|
|
curpos = ftell (datei);
|
|
if (_FP_fgets (uugen_inbuffer, 255, datei) == NULL)
|
|
break;
|
|
uugen_inbuffer[255] = '\0';
|
|
|
|
if (IsLineEmpty (uugen_inbuffer)) { /* oops */
|
|
fseek (datei, curpos, SEEK_SET);
|
|
break;
|
|
}
|
|
|
|
_FP_strncpy (ptr, uugen_inbuffer, 1024-llength);
|
|
|
|
/*
|
|
* see if line was terminated with CR. Otherwise, it continues ...
|
|
*/
|
|
c = strlen (ptr);
|
|
if (c>0 && (ptr[c-1] == '\012' || ptr[c-1] == '\015'))
|
|
hadcr = 1;
|
|
else
|
|
hadcr = 0;
|
|
|
|
/*
|
|
* strip whitespace
|
|
*/
|
|
|
|
ptr += c;
|
|
llength += c;
|
|
while (llength && isspace(*(ptr-1))) {
|
|
ptr--; llength--;
|
|
}
|
|
}
|
|
*ptr = '\0';
|
|
|
|
if (llength == 0)
|
|
return NULL;
|
|
|
|
return uuscan_shlline;
|
|
}
|
|
|
|
/*
|
|
* Extract the value from a MIME attribute=value pair. This function
|
|
* receives a pointer to the attribute.
|
|
*/
|
|
static char *
|
|
ParseValue (char *attribute)
|
|
{
|
|
char *ptr=uuscan_pvvalue;
|
|
int length=0;
|
|
|
|
if (attribute == NULL)
|
|
return NULL;
|
|
|
|
while ((isalnum(*attribute) || *attribute=='_') && *attribute != '=')
|
|
attribute++;
|
|
|
|
while (isspace(*attribute))
|
|
attribute++;
|
|
|
|
if (*attribute == '=') {
|
|
attribute++;
|
|
while (isspace (*attribute))
|
|
attribute++;
|
|
}
|
|
else
|
|
return NULL;
|
|
|
|
if (*attribute == '"') {
|
|
/* quoted-string */
|
|
attribute++;
|
|
while (*attribute && *attribute != '"' && length < 255) {
|
|
if (*attribute == '\\')
|
|
*ptr++ = *++attribute;
|
|
else
|
|
*ptr++ = *attribute;
|
|
attribute++;
|
|
length++;
|
|
}
|
|
*ptr = '\0';
|
|
}
|
|
else {
|
|
/* tspecials from RFC1521 */
|
|
|
|
while (*attribute && !isspace (*attribute) &&
|
|
*attribute != '(' && *attribute != ')' &&
|
|
*attribute != '<' && *attribute != '>' &&
|
|
*attribute != '@' && *attribute != ',' &&
|
|
*attribute != ';' && *attribute != ':' &&
|
|
*attribute != '\\' &&*attribute != '"' &&
|
|
*attribute != '/' && *attribute != '[' &&
|
|
*attribute != ']' && *attribute != '?' &&
|
|
*attribute != '=' && length < 255)
|
|
*ptr++ = *attribute++;
|
|
|
|
*ptr = '\0';
|
|
}
|
|
return uuscan_pvvalue;
|
|
}
|
|
|
|
/*
|
|
* Extract the information we need from header fields
|
|
*/
|
|
|
|
static headers *
|
|
ParseHeader (headers *theheaders, char *line)
|
|
{
|
|
char **variable=NULL;
|
|
char *value = NULL, *ptr, *thenew;
|
|
int delimit = 0, length;
|
|
|
|
if (line == NULL)
|
|
return theheaders;
|
|
|
|
if (_FP_strnicmp (line, "From:", 5) == 0) {
|
|
if (theheaders->from) return theheaders;
|
|
variable = &theheaders->from;
|
|
value = line+5;
|
|
delimit = 0;
|
|
}
|
|
else if (_FP_strnicmp (line, "Subject:", 8) == 0) {
|
|
if (theheaders->subject) return theheaders;
|
|
variable = &theheaders->subject;
|
|
value = line+8;
|
|
delimit = 0;
|
|
}
|
|
else if (_FP_strnicmp (line, "To:", 3) == 0) {
|
|
if (theheaders->rcpt) return theheaders;
|
|
variable = &theheaders->rcpt;
|
|
value = line+3;
|
|
delimit = 0;
|
|
}
|
|
else if (_FP_strnicmp (line, "Date:", 5) == 0) {
|
|
if (theheaders->date) return theheaders;
|
|
variable = &theheaders->date;
|
|
value = line+5;
|
|
delimit = 0;
|
|
}
|
|
else if (_FP_strnicmp (line, "Mime-Version:", 13) == 0) {
|
|
if (theheaders->mimevers) return theheaders;
|
|
variable = &theheaders->mimevers;
|
|
value = line+13;
|
|
delimit = 0;
|
|
}
|
|
else if (_FP_strnicmp (line, "Content-Type:", 13) == 0) {
|
|
if (theheaders->ctype) return theheaders;
|
|
variable = &theheaders->ctype;
|
|
value = line+13;
|
|
delimit = ';';
|
|
|
|
/* we can probably extract more information */
|
|
if ((ptr = _FP_stristr (line, "boundary")) != NULL) {
|
|
if ((thenew = ParseValue (ptr))) {
|
|
if (theheaders->boundary) free (theheaders->boundary);
|
|
theheaders->boundary = _FP_strdup (thenew);
|
|
}
|
|
}
|
|
if ((ptr = _FP_stristr (line, "name")) != NULL) {
|
|
if ((thenew = ParseValue (ptr))) {
|
|
if (theheaders->fname) free (theheaders->fname);
|
|
theheaders->fname = _FP_strdup (thenew);
|
|
}
|
|
}
|
|
if ((ptr = _FP_stristr (line, "id")) != NULL) {
|
|
if ((thenew = ParseValue (ptr))) {
|
|
if (theheaders->mimeid) free (theheaders->mimeid);
|
|
theheaders->mimeid = _FP_strdup (thenew);
|
|
}
|
|
}
|
|
if ((ptr = _FP_stristr (line, "number")) != NULL) {
|
|
if ((thenew = ParseValue (ptr))) {
|
|
theheaders->partno = atoi (thenew);
|
|
}
|
|
}
|
|
if ((ptr = _FP_stristr (line, "total")) != NULL) {
|
|
if ((thenew = ParseValue (ptr))) {
|
|
theheaders->numparts = atoi (thenew);
|
|
}
|
|
}
|
|
}
|
|
else if (_FP_strnicmp (line, "Content-Transfer-Encoding:", 26) == 0) {
|
|
if (theheaders->ctenc) return theheaders;
|
|
variable = &theheaders->ctenc;
|
|
value = line+26;
|
|
delimit = ';';
|
|
}
|
|
else if (_FP_strnicmp (line, "Content-Disposition:", 20) == 0) {
|
|
/*
|
|
* Some encoders mention the original filename as parameter to
|
|
* Content-Type, others as parameter to Content-Disposition. We
|
|
* do prefer the first solution, but accept this situation, too.
|
|
* TODO: Read RFC1806
|
|
*/
|
|
if ((ptr = _FP_stristr (line, "name")) != NULL) {
|
|
if (theheaders->fname == NULL && (thenew=ParseValue(ptr)) != NULL) {
|
|
theheaders->fname = _FP_strdup (thenew);
|
|
}
|
|
}
|
|
variable = NULL;
|
|
}
|
|
else {
|
|
/*
|
|
* nothing interesting
|
|
*/
|
|
return theheaders;
|
|
}
|
|
|
|
/*
|
|
* okay, so extract the actual data
|
|
*/
|
|
if (variable) {
|
|
length = 0;
|
|
ptr = uuscan_phtext;
|
|
|
|
while (isspace (*value))
|
|
value++;
|
|
while (*value && (delimit==0 || *value!=delimit) &&
|
|
*value != '\012' && *value != '\015' && length < 255) {
|
|
*ptr++ = *value++;
|
|
length++;
|
|
}
|
|
while (length && isspace(*(ptr-1))) {
|
|
ptr--; length--;
|
|
}
|
|
*ptr = '\0';
|
|
|
|
if ((*variable = _FP_strdup (uuscan_phtext)) == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
return theheaders;
|
|
}
|
|
|
|
/*
|
|
* is this a header line we know about?
|
|
*/
|
|
|
|
static int
|
|
IsKnownHeader (char *line)
|
|
{
|
|
char **iter = knownmsgheaders;
|
|
|
|
while (iter && *iter) {
|
|
if (_FP_strnicmp (line, *iter, strlen (*iter)) == 0)
|
|
return 1;
|
|
iter++;
|
|
}
|
|
|
|
iter = knownmimeheaders;
|
|
|
|
while (iter && *iter) {
|
|
if (_FP_strnicmp (line, *iter, strlen (*iter)) == 0)
|
|
return 2;
|
|
iter++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Scan a header
|
|
*/
|
|
|
|
int
|
|
UUScanHeader (FILE *datei, headers *envelope)
|
|
{
|
|
char *ptr;
|
|
|
|
while (!feof (datei)) {
|
|
if ((ptr = ScanHeaderLine (datei, NULL)) == NULL)
|
|
break;
|
|
if (*ptr == '\0' || *ptr == '\012' || *ptr == '\015')
|
|
break;
|
|
ParseHeader (envelope, ptr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Scan something for encoded data and fill the fileread* structure.
|
|
* If boundary is non-NULL, we stop after having read it. If Check-
|
|
* Headers != 0, we also stop after we've found uu_headercount recog-
|
|
* nized header lines.
|
|
* If we have a boundary, then we also don't accept Base64; MIME mails
|
|
* really should handle this properly.
|
|
* We return -1 if something went wrong, 0 if everything is normal,
|
|
* 1 if we found a new header and 2 if we found a boundary.
|
|
* In MIME message bodies (not multiparts), we also disable our reduced
|
|
* MIME handling.
|
|
*/
|
|
|
|
static int
|
|
ScanData (FILE *datei, char *fname, int *errcode,
|
|
char *boundary, int ismime, int checkheaders,
|
|
fileread *result)
|
|
{
|
|
char *line=uuscan_sdline, *bhds1=uuscan_sdbhds1, *bhds2=uuscan_sdbhds2;
|
|
static char *ptr, *p2, *p3=NULL, *bhdsp, bhl;
|
|
int isb64[10], isuue[10], isxxe[10], isbhx[10], iscnt;
|
|
int cbb64, cbuue, cbxxe, cbbhx;
|
|
int bhflag=0, vflag, haddh=0, hadct=0;
|
|
int bhrpc=0, bhnf=0, c, hcount, lcount, blen=0;
|
|
int encoding=0, dflag=0, ctline=42;
|
|
int dontcare=0, hadnl=0;
|
|
long preheaders=0, oldposition;
|
|
long yefilesize=0, yepartends=0;
|
|
size_t dcc, bhopc;
|
|
char prevlinefirstchar;
|
|
|
|
*errcode = UURET_OK;
|
|
(void) UUDecodeLine (NULL, NULL, 0); /* init */
|
|
bhdsp = bhds2;
|
|
|
|
if (datei == NULL || feof (datei))
|
|
return -1;
|
|
|
|
result->startpos = ftell (datei);
|
|
hcount = lcount = 0;
|
|
|
|
for (iscnt=0; iscnt<10; iscnt++) {
|
|
isb64[iscnt] = isuue[iscnt] = isxxe[iscnt] = isbhx[iscnt] = 0;
|
|
}
|
|
|
|
iscnt = 0;
|
|
|
|
if (boundary)
|
|
blen = strlen (boundary);
|
|
|
|
line[0] = '\0';
|
|
while (!feof (datei)) {
|
|
prevlinefirstchar = line[0];
|
|
oldposition = ftell (datei);
|
|
if (_FP_fgets (line, 299, datei) == NULL)
|
|
break;
|
|
if (ferror (datei))
|
|
break;
|
|
|
|
line[299] = '\0'; /* For Safety of string functions */
|
|
|
|
/*
|
|
* Make Busy Polls
|
|
*/
|
|
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_NOTE,
|
|
uustring (S_SCAN_CANCEL));
|
|
*errcode = UURET_CANCEL;
|
|
break;
|
|
}
|
|
|
|
if (IsLineEmpty (line)) { /* line empty? */
|
|
hcount = 0;
|
|
hadnl = 1;
|
|
continue; /* then ignore */
|
|
}
|
|
|
|
if (checkheaders) {
|
|
if (IsKnownHeader (line)) {
|
|
(void) ScanHeaderLine (datei, line);
|
|
|
|
if (hcount == 0) {
|
|
preheaders = oldposition;
|
|
lcount = 0;
|
|
}
|
|
hcount++;
|
|
lcount++;
|
|
|
|
/*
|
|
* check for the various restart counters
|
|
*/
|
|
|
|
if ((hcount >= hlcount.restart) ||
|
|
(hcount >= hlcount.afterdata && ismime == 0) ||
|
|
(hcount >= hlcount.afterdata && result->uudet) ||
|
|
(hcount >= hlcount.afternl && result->uudet && hadnl)) {
|
|
/*
|
|
* Hey, a new header starts here
|
|
*/
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
break;
|
|
}
|
|
/* continue; */
|
|
}
|
|
else if (lcount > WAITHEADER) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
dontcare=0;
|
|
}
|
|
else if (hcount) {
|
|
lcount++;
|
|
dontcare=1;
|
|
}
|
|
else {
|
|
dontcare=0;
|
|
}
|
|
}
|
|
else {
|
|
dontcare=0;
|
|
}
|
|
|
|
if (boundary != NULL &&
|
|
line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, boundary, blen) == 0) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
if (boundary != NULL && line[0] == 'C' && line[1] == 'o' &&
|
|
_FP_strnicmp (line, "Content-Type:", 13) == 0) {
|
|
ptr = ScanHeaderLine (datei, line);
|
|
p2 = (ptr)?_FP_stristr(ptr,"boundary"):NULL;
|
|
p3 = (p2)?ParseValue(p2):NULL;
|
|
|
|
if (p3 && strcmp (p3, boundary) == 0) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
else {
|
|
p3 = NULL;
|
|
}
|
|
}
|
|
|
|
if (strncmp (line, "begin ", 6) == 0 ||
|
|
_FP_strnicmp (line, "<pre>begin ", 11) == 0) {
|
|
if ((result->begin || result->end ||
|
|
result->uudet == B64ENCODED ||
|
|
result->uudet == BH_ENCODED) && !uu_more_mime) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
|
|
if (*line == '<')
|
|
ptr = line + 10;
|
|
else
|
|
ptr = line + 5;
|
|
|
|
while (*ptr == ' ') ptr++;
|
|
while (isdigit (*ptr))
|
|
result->mode = result->mode * 8 + *ptr++ - '0';
|
|
while (*ptr == ' ') ptr++;
|
|
|
|
/*
|
|
* We may have picked up a filename from a uuenview-style header
|
|
*/
|
|
_FP_free (result->filename);
|
|
result->filename = _FP_strdup (ptr);
|
|
result->begin = 1;
|
|
|
|
while (isspace (result->filename[strlen(result->filename)-1]))
|
|
result->filename[strlen(result->filename)-1] = '\0';
|
|
|
|
continue;
|
|
}
|
|
|
|
if ((strncmp (line, "end", 3) == 0) &&
|
|
result->uudet != BH_ENCODED &&
|
|
result->uudet != YENC_ENCODED) {
|
|
if (result->uudet == B64ENCODED && result->begin)
|
|
result->uudet = XX_ENCODED;
|
|
|
|
if (result->uudet != B64ENCODED) {
|
|
if (result->uudet == XX_ENCODED) {
|
|
if (prevlinefirstchar == '+')
|
|
result->end = 1;
|
|
}
|
|
if (result->uudet == UU_ENCODED) {
|
|
if (prevlinefirstchar == '`')
|
|
result->end = 1;
|
|
}
|
|
else {
|
|
result->end = 1;
|
|
}
|
|
if (dflag && encoding)
|
|
result->uudet = encoding;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
hadnl = 0;
|
|
|
|
/*
|
|
* Detect a UUDeview-Style header
|
|
*/
|
|
|
|
if ((_FP_strnicmp (line, "_=_ Part ", 9) == 0 ||
|
|
_FP_strnicmp (line, "section ", 8) == 0) &&
|
|
result->uudet != YENC_ENCODED) {
|
|
if (result->uudet) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
result->partno = atoi (line + 8);
|
|
if ((ptr = _FP_stristr (line, " of ")) != NULL) {
|
|
int maxpno = atoi (ptr + 4);
|
|
if(maxpno != 0) result->maxpno = maxpno;
|
|
}
|
|
if ((ptr = _FP_stristr (line, "of file ")) != NULL) {
|
|
ptr += 8;
|
|
while (isspace (*ptr)) ptr++;
|
|
p2 = ptr;
|
|
while (isalnum(*p2) || (strchr(uufnchars, *p2)!=NULL))
|
|
p2++;
|
|
c = *p2; *p2 = '\0';
|
|
if (p2 != ptr && result->filename == NULL)
|
|
result->filename = _FP_strdup (ptr);
|
|
else if (p2 - ptr > 5 && strchr (ptr, '.') != NULL) {
|
|
/*
|
|
* This file name looks good, too. Let's use it
|
|
*/
|
|
_FP_free (result->filename);
|
|
result->filename = _FP_strdup (ptr);
|
|
}
|
|
*p2 = c;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Some reduced MIME handling. Only use if boundary == NULL. Also
|
|
* accept the "X-Orcl-Content-Type" used by some braindead program.
|
|
*/
|
|
if (boundary == NULL && !ismime && !uu_more_mime &&
|
|
result->uudet != YENC_ENCODED) {
|
|
if (_FP_strnicmp (line, "Content-Type", 12) == 0 ||
|
|
_FP_strnicmp (line, "X-Orcl-Content-Type", 19) == 0) {
|
|
/*
|
|
* We use Content-Type to mark a new attachment and split the file.
|
|
* However, we do not split if we haven't found anything encoded yet.
|
|
*/
|
|
if (result->uudet) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
if ((ptr = strchr (line, ':')) != NULL) {
|
|
ptr++;
|
|
while (isspace (*ptr)) ptr++; p2 = ptr;
|
|
while (!isspace (*p2) && *p2 != ';') p2++;
|
|
c = *p2; *p2 = '\0';
|
|
if (p2 != ptr) {
|
|
_FP_free (result->mimetype);
|
|
result->mimetype = _FP_strdup (ptr);
|
|
}
|
|
*p2 = c;
|
|
}
|
|
ctline=0;
|
|
hadct=1;
|
|
}
|
|
if ((ptr = _FP_stristr (line, "number=")) && ctline<4) {
|
|
ptr += 7; if (*ptr == '"') ptr++;
|
|
result->partno = atoi (ptr);
|
|
}
|
|
if ((ptr = _FP_stristr (line, "total=")) && ctline<4) {
|
|
ptr += 6; if (*ptr == '"') ptr++;
|
|
result->maxpno = atoi (ptr);
|
|
}
|
|
if ((ptr = _FP_stristr (line, "name=")) && ctline<4) {
|
|
ptr += 5;
|
|
while (isspace (*ptr)) ptr++;
|
|
if (*ptr == '"' && *(ptr+1) && (p2 = strchr (ptr+2, '"')) != NULL) {
|
|
c = *p2; *p2 = '\0';
|
|
_FP_free (result->filename);
|
|
result->filename = _FP_strdup (ptr+1);
|
|
*p2 = c;
|
|
}
|
|
else if (*ptr=='\''&&*(ptr+1)&&(p2 = strchr(ptr+2, '\'')) != NULL) {
|
|
c = *p2; *p2 = '\0';
|
|
_FP_free (result->filename);
|
|
result->filename = _FP_strdup (ptr+1);
|
|
*p2 = c;
|
|
}
|
|
else {
|
|
p2 = ptr;
|
|
while (isalnum(*p2) ||
|
|
*p2 == '.' || *p2=='_' || *p2 == '-' ||
|
|
*p2 == '!' || *p2=='@' || *p2 == '$')
|
|
p2++;
|
|
c = *p2; *p2 = '\0';
|
|
if (p2 != ptr && result->filename == NULL)
|
|
result->filename = _FP_strdup (ptr);
|
|
else if (p2 - ptr > 5 && strchr (ptr, '.') != NULL) {
|
|
/*
|
|
* This file name looks good, too. Let's use it
|
|
*/
|
|
_FP_free (result->filename);
|
|
result->filename = _FP_strdup (ptr);
|
|
}
|
|
*p2 = c;
|
|
}
|
|
}
|
|
if ((ptr = _FP_stristr (line, "id=")) && ctline<4) {
|
|
p2 = ptr += 3;
|
|
if (*p2 == '"') {
|
|
p2 = strchr (++ptr, '"');
|
|
}
|
|
else {
|
|
while (*p2 && isprint(*p2) && !isspace(*p2) && *p2 != ';')
|
|
p2++;
|
|
}
|
|
if (p2 && *p2 && p2!=ptr) {
|
|
c = *p2; *p2 = '\0';
|
|
if (result->mimeid)
|
|
_FP_free (result->mimeid);
|
|
result->mimeid = _FP_strdup (ptr);
|
|
*p2 = c;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handling for very short Base64 files.
|
|
*/
|
|
if (uu_tinyb64 && !ismime && !uu_more_mime) {
|
|
if (line[0] == '-' && line[1] == '-') {
|
|
if (dflag && (encoding==B64ENCODED || result->uudet==B64ENCODED)) {
|
|
if (encoding==B64ENCODED && result->uudet==0 && (haddh||hadct)) {
|
|
result->uudet = encoding;
|
|
encoding = dflag = 0;
|
|
}
|
|
haddh = 1;
|
|
continue;
|
|
}
|
|
hadct = 0;
|
|
}
|
|
}
|
|
} /* end of reduced MIME handling */
|
|
|
|
/*
|
|
* If we're in "freestyle" mode, have not encountered anything
|
|
* interesting yet, and stumble upon something that looks like
|
|
* a boundary, followed by a Content-* line, try to use it.
|
|
*/
|
|
|
|
if (boundary == NULL && !ismime && !uu_more_mime && dflag <= 1 &&
|
|
line[0] == '-' && line[1] == '-' && strlen(line+2)>10 &&
|
|
(((ptr = _FP_strrstr (line+2, "--")) == NULL) ||
|
|
(*(ptr+2) != '\012' && *(ptr+2) != '\015')) &&
|
|
_FP_strstr (line+2, "_=_") != NULL) {
|
|
prevlinefirstchar = line[0];
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
break;
|
|
}
|
|
if (_FP_strnicmp (line, "Content-", 8) == 0) {
|
|
/*
|
|
* Okay, let's do it. This breaks out of ScanData. ScanPart will
|
|
* recognize the boundary on the next call and use it.
|
|
*/
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Detection for yEnc encoding
|
|
*/
|
|
|
|
if (strncmp (line, "=ybegin ", 8) == 0 &&
|
|
_FP_strstr (line, " name=") != NULL) {
|
|
if ((result->begin || result->end) && !uu_more_mime) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* name continues to the end of the line
|
|
*/
|
|
|
|
_FP_free (result->filename);
|
|
ptr = _FP_strstr (line, " name=") + 6;
|
|
result->filename = _FP_strdup (ptr);
|
|
|
|
while (isspace (result->filename[strlen(result->filename)-1]))
|
|
result->filename[strlen(result->filename)-1] = '\0';
|
|
|
|
/*
|
|
* Determine size
|
|
*/
|
|
|
|
if ((ptr = _FP_strstr (line, " size=")) != NULL) {
|
|
ptr += 6;
|
|
yefilesize = atoi (ptr);
|
|
}
|
|
else {
|
|
yefilesize = -1;
|
|
}
|
|
|
|
/*
|
|
* check for multipart file and read =ypart line
|
|
*/
|
|
|
|
if ((ptr = _FP_strstr (line, " part=")) != NULL) {
|
|
result->partno = atoi (ptr + 6);
|
|
|
|
if (result->partno == 1) {
|
|
result->begin = 1;
|
|
}
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
break;
|
|
}
|
|
|
|
line[255] = '\0';
|
|
|
|
if (strncmp (line, "=ypart ", 7) != 0) {
|
|
break;
|
|
}
|
|
|
|
if ((ptr = _FP_strstr (line, " end=")) == NULL) {
|
|
break;
|
|
}
|
|
|
|
yepartends = atoi (ptr + 5);
|
|
}
|
|
else {
|
|
result->partno = 1;
|
|
result->begin = 1;
|
|
}
|
|
|
|
/*
|
|
* Don't want auto-detection
|
|
*/
|
|
|
|
result->uudet = YENC_ENCODED;
|
|
continue;
|
|
}
|
|
|
|
if (strncmp (line, "=yend ", 6) == 0 &&
|
|
result->uudet == YENC_ENCODED) {
|
|
if (yepartends == 0 || yepartends >= yefilesize) {
|
|
result->end = 1;
|
|
}
|
|
if (!uu_more_mime)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* if we haven't yet found anything encoded, try to find something
|
|
*/
|
|
|
|
if (!(result->uudet)) {
|
|
/*
|
|
* Netscape-Repair code is the same as in uunconc.c
|
|
*/
|
|
|
|
if ((vflag = UUValidData (line, 0, &bhflag)) == 0 && !ismime)
|
|
vflag = UURepairData (datei, line, 0, &bhflag);
|
|
|
|
/*
|
|
* Check data against all possible encodings
|
|
*/
|
|
|
|
isb64[iscnt%10] = (UUValidData (line, B64ENCODED, &bhflag)==B64ENCODED);
|
|
isuue[iscnt%10] = (UUValidData (line, UU_ENCODED, &bhflag)==UU_ENCODED);
|
|
isxxe[iscnt%10] = (UUValidData (line, XX_ENCODED, &bhflag)==XX_ENCODED);
|
|
isbhx[iscnt%10] = (UUValidData (line, BH_ENCODED, &bhflag)==BH_ENCODED);
|
|
|
|
/*
|
|
* If we've got a first valid encoded line, we get suspicious if
|
|
* it's shorter than, say, 40 characters.
|
|
*/
|
|
|
|
if (vflag == B64ENCODED &&
|
|
(dflag == 0 || encoding != B64ENCODED) &&
|
|
strlen (line) < 40 && !result->begin && !uu_tinyb64) {
|
|
isb64[iscnt%10] = 0;
|
|
vflag = 0;
|
|
}
|
|
|
|
if ((vflag == UU_ENCODED || vflag == XX_ENCODED) &&
|
|
(dflag == 0 || encoding != vflag) &&
|
|
strlen (line) < 40 && !result->begin) {
|
|
isuue[iscnt%10] = isxxe[iscnt%10] = 0;
|
|
vflag = 0;
|
|
}
|
|
|
|
iscnt++;
|
|
|
|
/*
|
|
* Ah, so we got an encoded line? How interesting!
|
|
*/
|
|
|
|
if (vflag) {
|
|
/*
|
|
* For BinHex data, we can use the initial colon ':' as begin
|
|
* and the terminating colon as ':'.
|
|
* If (vflag && !bhflag), this is the last line,
|
|
*/
|
|
if (vflag == BH_ENCODED) {
|
|
if (line[0] == ':' && result->end) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
if (line[0] == ':')
|
|
result->begin = 1;
|
|
if (bhflag == 0) {
|
|
result->uudet = BH_ENCODED;
|
|
result->end = 1;
|
|
}
|
|
}
|
|
/*
|
|
* For BinHex files, the file name is encoded in the first encoded
|
|
* data bytes. We try to extract it here
|
|
*/
|
|
if (vflag == BH_ENCODED && bhnf == 0 && result->filename == NULL) {
|
|
if (bhdsp == bhds2 ||
|
|
((bhdsp-bhds2) <= (int) bhds2[0] &&
|
|
(bhdsp-bhds2) < 256)) {
|
|
dcc = UUDecodeLine (line, bhds1, BH_ENCODED);
|
|
UUbhdecomp (bhds1, bhdsp, &bhl, &bhrpc,
|
|
dcc, 256-(bhdsp-bhds2), &bhopc);
|
|
bhdsp += bhopc;
|
|
}
|
|
if ((bhdsp-bhds2) > (int) bhds2[0] && bhds2[0]>0 &&
|
|
result->filename==NULL) {
|
|
memcpy (bhds1, bhds2+1, (int) bhds2[0]);
|
|
bhds1[(int)bhds2[0]]='\0';
|
|
result->filename = _FP_strdup (bhds1);
|
|
bhnf = 1;
|
|
}
|
|
else if (bhdsp-bhds2 >= 256 && bhds2[0]>0) {
|
|
memcpy (bhds1, bhds2+1, 255);
|
|
bhds1[255] = '\0';
|
|
result->filename = _FP_strdup (bhds1);
|
|
bhnf = 1;
|
|
}
|
|
else if (bhds2[0] <= 0)
|
|
bhnf = 1;
|
|
}
|
|
|
|
/*
|
|
* We accept an encoding if it has been true for four consecutive
|
|
* lines. Check the is<enc> arrays to avoid mistaking one encoding
|
|
* for the other. Uuencoded data is rather easily mistaken for
|
|
* Base 64. If the data matches more than one encoding, we need to
|
|
* scan further.
|
|
*/
|
|
|
|
if (iscnt > 3) {
|
|
cbb64 = (isb64[(iscnt-1)%10] && isb64[(iscnt-2)%10] &&
|
|
isb64[(iscnt-3)%10] && isb64[(iscnt-4)%10]);
|
|
cbuue = (isuue[(iscnt-1)%10] && isuue[(iscnt-2)%10] &&
|
|
isuue[(iscnt-3)%10] && isuue[(iscnt-4)%10]);
|
|
cbxxe = (isxxe[(iscnt-1)%10] && isxxe[(iscnt-2)%10] &&
|
|
isxxe[(iscnt-3)%10] && isxxe[(iscnt-4)%10]);
|
|
cbbhx = (isbhx[(iscnt-1)%10] && isbhx[(iscnt-2)%10] &&
|
|
isbhx[(iscnt-3)%10] && isbhx[(iscnt-4)%10]);
|
|
}
|
|
else {
|
|
cbb64 = cbuue = cbxxe = cbbhx = 0;
|
|
}
|
|
|
|
if (cbb64 && !cbuue && !cbxxe && !cbbhx) {
|
|
result->uudet = B64ENCODED;
|
|
}
|
|
else if (!cbb64 && cbuue && !cbxxe && !cbbhx) {
|
|
result->uudet = UU_ENCODED;
|
|
}
|
|
else if (!cbb64 && !cbuue && cbxxe && !cbbhx) {
|
|
result->uudet = XX_ENCODED;
|
|
}
|
|
else if (!cbb64 && !cbuue && !cbxxe && cbbhx) {
|
|
result->uudet = BH_ENCODED;
|
|
}
|
|
|
|
if (result->uudet) {
|
|
encoding = dflag = 0;
|
|
|
|
/*
|
|
* If we're in fast mode, we're told we don't have to look
|
|
* for anything below, so we can as well break out of every-
|
|
* thing
|
|
* We cannot fast-scan if we have a boundary to look after.
|
|
*/
|
|
|
|
if (uu_fast_scanning && boundary == NULL)
|
|
break;
|
|
|
|
/*
|
|
* Skip the encoded data. We simply wait for a boundary, for
|
|
* a header or for an empty line. But we also try to pick up
|
|
* an "end"
|
|
*/
|
|
|
|
hcount = lcount = 0;
|
|
|
|
while (!feof (datei)) {
|
|
/*
|
|
* Make Busy Polls
|
|
*/
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_NOTE,
|
|
uustring (S_SCAN_CANCEL));
|
|
*errcode = UURET_CANCEL;
|
|
break;
|
|
}
|
|
|
|
prevlinefirstchar = line[0];
|
|
oldposition = ftell (datei);
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (ferror (datei))
|
|
break;
|
|
|
|
line[255] = '\0';
|
|
|
|
/*
|
|
* Stop scanning at an empty line or a MIME-boundary.
|
|
*/
|
|
if (IsLineEmpty (line))
|
|
break;
|
|
if (boundary && line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, boundary, blen) == 0) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
else if (line[0] == 'e' && (result->uudet == UU_ENCODED ||
|
|
result->uudet == XX_ENCODED)) {
|
|
if (strncmp (line, "end", 3) == 0) {
|
|
if (result->uudet == XX_ENCODED) {
|
|
if (prevlinefirstchar == '+') {
|
|
result->end = 1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (prevlinefirstchar == '`') {
|
|
result->end = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (line[0] == 'b') {
|
|
if (strncmp (line, "begin ", 6) == 0) {
|
|
fseek (datei, oldposition, SEEK_SET);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (checkheaders) {
|
|
if (IsKnownHeader (line)) {
|
|
(void) ScanHeaderLine (datei, line);
|
|
if (hcount == 0)
|
|
preheaders = oldposition;
|
|
hcount++;
|
|
lcount++;
|
|
if ((hcount >= hlcount.restart) ||
|
|
(hcount >= hlcount.afterdata && result->uudet)) {
|
|
/*
|
|
* Hey, a new header starts here
|
|
*/
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
break;
|
|
}
|
|
}
|
|
else if (lcount > WAITHEADER) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
}
|
|
else if (hcount) {
|
|
lcount++;
|
|
}
|
|
}
|
|
if (result->uudet == BH_ENCODED) {
|
|
/* pick up ``EOF'' for BinHex files. Slow :-< */
|
|
if (line[0] && strchr (line+1, ':') != NULL) {
|
|
result->end = 1;
|
|
bhflag = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ferror (datei) || *errcode == UURET_CANCEL)
|
|
break;
|
|
|
|
if (line[0] == '-' && line[1] == '-')
|
|
haddh = 1;
|
|
|
|
/*
|
|
* Okay, got everything we need. If we had headers or a
|
|
* boundary, we break out of the outer loop immediately.
|
|
*/
|
|
|
|
if (IsKnownHeader (line) ||
|
|
(boundary && line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, boundary, blen) == 0)) {
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, we wait until finding something more interesting
|
|
* in the outer loop
|
|
*/
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Select the encoding with the best "history"
|
|
*/
|
|
|
|
cbb64 = isb64[(iscnt-1)%10];
|
|
cbuue = isuue[(iscnt-1)%10];
|
|
cbxxe = isxxe[(iscnt-1)%10];
|
|
cbbhx = isbhx[(iscnt-1)%10];
|
|
dflag = 0;
|
|
|
|
if (cbb64 || cbuue || cbxxe || cbbhx) {
|
|
for (dflag=2; dflag<iscnt && dflag<4; dflag++) {
|
|
if ((!cbb64 || !isb64[(iscnt-dflag)%10]) &&
|
|
(!cbuue || !isuue[(iscnt-dflag)%10]) &&
|
|
(!cbxxe || !isxxe[(iscnt-dflag)%10]) &&
|
|
(!cbbhx || !isbhx[(iscnt-dflag)%10])) {
|
|
dflag--;
|
|
break;
|
|
}
|
|
cbb64 &= isb64[(iscnt-dflag)%10];
|
|
cbuue &= isuue[(iscnt-dflag)%10];
|
|
cbxxe &= isxxe[(iscnt-dflag)%10];
|
|
cbbhx &= isbhx[(iscnt-dflag)%10];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* clear-cut cases
|
|
*/
|
|
|
|
if (cbb64 && !cbuue && !cbxxe && !cbbhx) {
|
|
encoding = B64ENCODED;
|
|
}
|
|
else if (!cbb64 && cbuue && !cbxxe && !cbbhx) {
|
|
encoding = UU_ENCODED;
|
|
}
|
|
else if (!cbb64 && !cbuue && cbxxe && !cbbhx) {
|
|
encoding = XX_ENCODED;
|
|
}
|
|
else if (!cbb64 && !cbuue && !cbxxe && cbbhx) {
|
|
encoding = BH_ENCODED;
|
|
}
|
|
else {
|
|
encoding = 0;
|
|
}
|
|
|
|
/*
|
|
* Check a few common non-clear-cut cases
|
|
*/
|
|
|
|
if (!encoding && cbuue && result->begin) {
|
|
encoding = UU_ENCODED;
|
|
}
|
|
else if (!encoding && cbxxe && result->begin) {
|
|
encoding = XX_ENCODED;
|
|
}
|
|
else if (!encoding && cbb64) {
|
|
encoding = B64ENCODED;
|
|
}
|
|
else if (!encoding && cbuue) {
|
|
encoding = UU_ENCODED;
|
|
}
|
|
else if (!encoding && cbxxe) {
|
|
encoding = XX_ENCODED;
|
|
}
|
|
else if (!encoding && cbbhx) {
|
|
encoding = BH_ENCODED;
|
|
}
|
|
}
|
|
else if (!dontcare) {
|
|
encoding = 0;
|
|
dflag = 0;
|
|
haddh = 0;
|
|
}
|
|
} /* if (!uudet) */
|
|
/*
|
|
* End of scanning loop
|
|
*/
|
|
} /* while (!feof (datei)) */
|
|
|
|
if (feof (datei))
|
|
oldposition = ftell (datei);
|
|
|
|
if (dflag && encoding == B64ENCODED && haddh)
|
|
result->uudet = B64ENCODED;
|
|
else if (dflag && encoding == BH_ENCODED)
|
|
result->uudet = BH_ENCODED;
|
|
|
|
/* Base64 doesn't have begin or end, so it was probably XX */
|
|
if (result->uudet == B64ENCODED && result->begin && result->end)
|
|
result->uudet = XX_ENCODED;
|
|
|
|
/* Base64 doesn't have begin or end */
|
|
if (result->uudet == B64ENCODED)
|
|
result->begin = result->end = 0;
|
|
|
|
/* Base64 and BinHex don't have a file mode */
|
|
if (result->uudet == B64ENCODED || result->uudet == BH_ENCODED ||
|
|
result->uudet == YENC_ENCODED)
|
|
result->mode = 6*64+4*8+4;
|
|
|
|
/*
|
|
* When strict MIME adherance is set, throw out suspicious attachments
|
|
*/
|
|
|
|
if (uu_more_mime) {
|
|
/*
|
|
* In a MIME message, Base64 should be appropriately tagged
|
|
*/
|
|
|
|
if (result->uudet == B64ENCODED) {
|
|
result->uudet = 0;
|
|
}
|
|
|
|
/*
|
|
* Do not accept incomplete UU or XX encoded messages
|
|
*/
|
|
|
|
if ((result->uudet != 0 && result->uudet != B64ENCODED) &&
|
|
(!result->begin || !result->end)) {
|
|
result->uudet = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* In fast mode, this length will yield a false value. We don't care.
|
|
* This must be checked for in uunconc(), where we usually stop decoding
|
|
* after reaching startpos+length
|
|
*/
|
|
|
|
if (uu_fast_scanning)
|
|
result->length = progress.fsize-result->startpos;
|
|
else
|
|
result->length = ftell(datei)-result->startpos;
|
|
|
|
if (ferror (datei)) {
|
|
*errcode = UURET_IOERR;
|
|
uu_errno = errno;
|
|
return -1;
|
|
}
|
|
if (*errcode != UURET_OK) {
|
|
return -1;
|
|
}
|
|
|
|
if (boundary && line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, boundary, blen) == 0)
|
|
return 2;
|
|
else if (boundary && p3 &&
|
|
line[0] == 'C' && line[1] == 'o' &&
|
|
_FP_strnicmp (line, "Content-Type:", 13) == 0 &&
|
|
strcmp (p3, boundary) == 0)
|
|
return 2;
|
|
else if (IsKnownHeader (line))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This is the main scanning function.
|
|
*/
|
|
|
|
fileread *
|
|
ScanPart (FILE *datei, char *fname, int *errcode)
|
|
{
|
|
int ecount, hcount, lcount;
|
|
int bhflag, begflag, vflag, blen=0, res;
|
|
long preheaders, prevpos=0, preenc, before;
|
|
char *line=uuscan_spline;
|
|
fileread *result;
|
|
char *ptr1, *ptr2;
|
|
|
|
(void) UUDecodeLine (NULL, NULL, 0); /* init */
|
|
if (datei == NULL || feof (datei)) {
|
|
*errcode = UURET_OK;
|
|
return NULL;
|
|
}
|
|
|
|
*errcode = UURET_OK;
|
|
|
|
if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
return NULL;
|
|
}
|
|
memset (result, 0, sizeof (fileread));
|
|
result->startpos = ftell (datei);
|
|
preheaders = result->startpos;
|
|
before = result->startpos;
|
|
|
|
/* if this is a new file, reset our scanning state */
|
|
if (sstate.source == NULL || strcmp (fname, sstate.source) != 0) {
|
|
sstate.isfolder = 1; /* assume yes */
|
|
sstate.ismime = 0; /* wait for MIME-Version */
|
|
sstate.mimestate = MS_HEADERS; /* assume headers follow */
|
|
/* mimseqno = 1; */
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
|
|
UUkillheaders (&sstate.envelope);
|
|
memset (&sstate.envelope, 0, sizeof (headers));
|
|
|
|
_FP_free (sstate.source);
|
|
if ((sstate.source = _FP_strdup (fname)) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/* ignore empty lines at the beginning of a file */
|
|
preheaders = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (!IsLineEmpty (line)) {
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
line[255] = '\0';
|
|
break;
|
|
}
|
|
preheaders = ftell (datei);
|
|
}
|
|
}
|
|
|
|
if (ferror(datei) || feof(datei)) {
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If we are confident that this is a mail folder and are at the
|
|
* beginning of the file, expecting to read some headers, scan
|
|
* the envelope.
|
|
*/
|
|
|
|
if (sstate.isfolder && sstate.mimestate == MS_HEADERS) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
UUkillheaders (&sstate.envelope);
|
|
|
|
/*
|
|
* clean up leftovers from invalid messages
|
|
*/
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
line[255] = '\0';
|
|
|
|
/*
|
|
* Special handling for AOL folder files, which start off with a boundary.
|
|
* We recognize them by a valid boundary line as the first line of a file.
|
|
* Note that the rest of the scanning code becomes suspicious if a boun-
|
|
* dary does never appear in a file -- this should save us from grave
|
|
* false detection errors
|
|
*/
|
|
|
|
if (!feof (datei) && line[0] == '-' && line[1] == '-' && line[2]) {
|
|
while (line[strlen(line)-1] == '\012' ||
|
|
line[strlen(line)-1] == '\015') {
|
|
line[strlen(line)-1] = '\0';
|
|
}
|
|
|
|
sstate.ismime = 1;
|
|
sstate.envelope.mimevers = _FP_strdup ("1.0");
|
|
sstate.envelope.boundary = _FP_strdup (line+2);
|
|
sstate.envelope.ctype = _FP_strdup ("multipart/mixed");
|
|
sstate.mimestate = MS_SUBPART;
|
|
|
|
*errcode = UURET_CONT;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Normal behavior: look for a RFC 822 header
|
|
*/
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
if (ParseHeader (&sstate.envelope, ptr1) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* if we've read too many lines without finding headers, then
|
|
* this probably isn't a mail folder after all
|
|
*/
|
|
lcount++;
|
|
if (lcount > WAITHEADER && hcount < hlcount.afternl)
|
|
break;
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
}
|
|
/* skip empty lines */
|
|
prevpos = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (!IsLineEmpty (line)) {
|
|
fseek (datei, prevpos, SEEK_SET);
|
|
line[255] = '\0';
|
|
break;
|
|
}
|
|
prevpos = ftell (datei);
|
|
}
|
|
/*
|
|
* If we don't have all valid MIME headers yet, but the following
|
|
* line is a MIME header, accept it anyway.
|
|
*/
|
|
|
|
if (!uu_more_mime &&
|
|
sstate.envelope.mimevers == NULL &&
|
|
sstate.envelope.ctype == NULL &&
|
|
sstate.envelope.ctenc == NULL &&
|
|
IsKnownHeader (line)) {
|
|
/*
|
|
* see above
|
|
*/
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\012';
|
|
line[1] = '\0';
|
|
}
|
|
line[255] = '\0';
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
if (ParseHeader (&sstate.envelope, ptr1) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
}
|
|
/* skip empty lines */
|
|
prevpos = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (!IsLineEmpty (line)) {
|
|
fseek (datei, prevpos, SEEK_SET);
|
|
line[255] = '\0';
|
|
break;
|
|
}
|
|
prevpos = ftell (datei);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A partial multipart message probably has only a Content-Type
|
|
* header but nothing else. In this case, at least simulate a
|
|
* MIME message
|
|
* if mimevers is not set but there are other well-known MIME
|
|
* headers, don't be too picky about it.
|
|
*/
|
|
if (sstate.envelope.ctype && sstate.envelope.mimevers==NULL &&
|
|
_FP_stristr (sstate.envelope.ctype, "multipart") != NULL &&
|
|
sstate.envelope.boundary != NULL) {
|
|
sstate.envelope.mimevers = _FP_strdup ("1.0");
|
|
hcount = hlcount.afternl;
|
|
}
|
|
else if (sstate.envelope.mimevers==NULL && sstate.envelope.ctype &&
|
|
sstate.envelope.fname && sstate.envelope.ctenc) {
|
|
sstate.envelope.mimevers = _FP_strdup ("1.0");
|
|
hcount = hlcount.afternl;
|
|
}
|
|
|
|
if (hcount < hlcount.afternl) {
|
|
/* not a folder after all */
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
else if (sstate.envelope.mimevers != NULL) {
|
|
/* this is a MIME file. check the Content-Type */
|
|
sstate.ismime = 1;
|
|
if (_FP_stristr (sstate.envelope.ctype, "multipart") != NULL) {
|
|
if (sstate.envelope.boundary == NULL) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_NO_BOUNDARY));
|
|
sstate.mimestate = MS_BODY;
|
|
_FP_free (sstate.envelope.ctype);
|
|
sstate.envelope.ctype = _FP_strdup ("text/plain");
|
|
}
|
|
else {
|
|
sstate.mimestate = MS_PREAMBLE;
|
|
}
|
|
}
|
|
else {
|
|
sstate.mimestate = MS_BODY; /* just a `simple' message */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (feof (datei) || ferror (datei)) { /* oops */
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Handle MIME stuff
|
|
*/
|
|
|
|
/*
|
|
* Read Preamble. This must be ended by a sstate.envelope.boundary line.
|
|
* If uu_usepreamble is set, we produce a result from this one
|
|
*/
|
|
|
|
if (sstate.ismime && sstate.mimestate == MS_PREAMBLE) {
|
|
result->startpos = ftell (datei); /* remember start of preamble */
|
|
prevpos = ftell (datei);
|
|
preheaders = ftell (datei);
|
|
|
|
blen = strlen (sstate.envelope.boundary);
|
|
lcount = 0;
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\0';
|
|
break;
|
|
}
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0)
|
|
break;
|
|
if (!IsLineEmpty (line))
|
|
lcount++;
|
|
|
|
prevpos = ftell (datei);
|
|
}
|
|
if (feof (datei) || ferror (datei)) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_B_NOT_FOUND));
|
|
/*
|
|
* restart and try again; don't restart if uu_fast_scanning
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
|
|
if (!uu_fast_scanning) {
|
|
*errcode = UURET_CONT;
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
}
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
|
|
ptr1 = line + 2 + blen;
|
|
if (*ptr1 == '-' && *(ptr1+1) == '-') {
|
|
/* Empty Multipart Message. Duh. */
|
|
sstate.mimestate = MS_EPILOGUE;
|
|
}
|
|
else {
|
|
sstate.mimestate = MS_SUBPART;
|
|
}
|
|
}
|
|
else { /* shouldn't happen */
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_B_NOT_FOUND));
|
|
/*
|
|
* restart and try again; don't restart if uu_fast_scanning
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
|
|
if (!uu_fast_scanning) {
|
|
*errcode = UURET_CONT;
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
}
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
/* produce result if uu_usepreamble is set */
|
|
if (uu_usepreamble && lcount) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
result->filename = _FP_strdup (line);
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
result->mimetype = _FP_strdup ("text/plain");
|
|
result->mode = 0644;
|
|
result->uudet = PT_ENCODED; /* plain text */
|
|
result->sfname = _FP_strdup (fname);
|
|
result->flags = FL_SINGLE | FL_PROPER;
|
|
/* result->startpos set from above */
|
|
result->length = prevpos - result->startpos;
|
|
result->partno = 1;
|
|
|
|
/* MIME message, let's continue */
|
|
*errcode = UURET_CONT;
|
|
|
|
if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
|
|
result->filename == NULL || result->sfname == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
/* MIME message, let's continue */
|
|
if (*errcode == UURET_OK)
|
|
*errcode = UURET_CONT;
|
|
|
|
/* otherwise, just return NULL */
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Read Epilogue, the plain text after the last boundary.
|
|
* This can either end with new headers from the next message of a
|
|
* mail folder or with a `parent' boundary if we are inside an
|
|
* encapsulated Multipart message. Oh yes, and of course the file
|
|
* may also simply end :-)
|
|
* Another possibility is that we might find plain encoded data
|
|
* without encapsulating message. We're not _too_ flexible here,
|
|
* we won't detect Base64, and require a proper `begin' line for
|
|
* uuencoding and xxencoding
|
|
* If uu_usepreamble is set, we produce a result from this one
|
|
*/
|
|
|
|
if (sstate.ismime && sstate.mimestate == MS_EPILOGUE) {
|
|
result->startpos = ftell (datei); /* remember start of epilogue */
|
|
prevpos = ftell (datei);
|
|
preheaders = ftell (datei);
|
|
preenc = ftell (datei);
|
|
hcount = lcount = 0;
|
|
ecount = bhflag = 0;
|
|
begflag = vflag = 0;
|
|
res = 0;
|
|
|
|
/*
|
|
* If we are in the outermost message and uu_fast_scanning, we
|
|
* know (or assume) that no more messages will follow, so there's
|
|
* no need to scan the rest.
|
|
*/
|
|
if (uu_fast_scanning && mssdepth == 0) {
|
|
/*
|
|
* check if the epilogue is empty
|
|
*/
|
|
while (!feof (datei) && !ferror (datei) && lcount<10 && res==0) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (!IsLineEmpty (line))
|
|
res++;
|
|
lcount++;
|
|
}
|
|
if (uu_usepreamble && res) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
result->filename = _FP_strdup (line);
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
result->mimetype = _FP_strdup ("text/plain");
|
|
result->mode = 0644;
|
|
result->uudet = PT_ENCODED; /* plain text */
|
|
result->sfname = _FP_strdup (fname);
|
|
result->flags = FL_SINGLE | FL_PROPER | FL_TOEND;
|
|
result->partno = 1;
|
|
/* result->startpos set from above */
|
|
result->length = progress.fsize - result->startpos;
|
|
|
|
if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
|
|
result->filename == NULL || result->sfname == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
if (mssdepth > 0)
|
|
blen = strlen (multistack[mssdepth-1].envelope.boundary);
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\0';
|
|
break;
|
|
}
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
line[255] = '\0';
|
|
/* check for parent boundary */
|
|
if (mssdepth > 0 && line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2,
|
|
multistack[mssdepth-1].envelope.boundary, blen) == 0)
|
|
break;
|
|
|
|
/* check for next headers only at outermost level */
|
|
if (mssdepth == 0 && IsKnownHeader (line)) {
|
|
(void) ScanHeaderLine (datei, line);
|
|
if (hcount == 0) {
|
|
preheaders = prevpos;
|
|
lcount = 0;
|
|
}
|
|
hcount++;
|
|
lcount++;
|
|
|
|
if (hcount >= hlcount.restart) {
|
|
/* okay, new headers */
|
|
break;
|
|
}
|
|
}
|
|
else if (lcount > WAITHEADER) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
}
|
|
else if (hcount) {
|
|
lcount++;
|
|
}
|
|
else {
|
|
hcount = lcount = 0;
|
|
}
|
|
|
|
/* check for begin and encoded data only at outermost level */
|
|
if (mssdepth == 0 && !uu_more_mime) {
|
|
if (strncmp (line, "begin ", 6) == 0 ||
|
|
_FP_strnicmp (line, "<pre>begin ", 11) == 0) {
|
|
preenc = prevpos;
|
|
begflag = 1;
|
|
}
|
|
else if (strncmp (line, "end", 3) == 0 && begflag) {
|
|
ecount = ELC_COUNT;
|
|
break;
|
|
}
|
|
else if ((vflag = UUValidData (line, 0, &bhflag)) != 0) {
|
|
if (vflag == BH_ENCODED && bhflag == 0) {
|
|
/* very short BinHex file follows */
|
|
preenc = prevpos;
|
|
break;
|
|
}
|
|
/* remember that XX can easily be mistaken as Base64 */
|
|
if ((vflag == UU_ENCODED || vflag == XX_ENCODED ||
|
|
vflag == B64ENCODED) && begflag) {
|
|
if (++ecount >= ELC_COUNT)
|
|
break;
|
|
}
|
|
else {
|
|
begflag = 0;
|
|
ecount = 0;
|
|
}
|
|
}
|
|
else {
|
|
begflag = 0;
|
|
ecount = 0;
|
|
}
|
|
}
|
|
|
|
if (!IsLineEmpty (line))
|
|
res++;
|
|
|
|
prevpos = ftell (datei);
|
|
}
|
|
|
|
if (mssdepth > 0 && line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2,
|
|
multistack[mssdepth-1].envelope.boundary, blen) == 0) {
|
|
/* restore previous state */
|
|
mssdepth--;
|
|
UUkillheaders (&sstate.envelope);
|
|
_FP_free (sstate.source);
|
|
memcpy (&sstate, &(multistack[mssdepth]), sizeof (scanstate));
|
|
|
|
ptr1 = line + 2 + strlen (sstate.envelope.boundary);
|
|
|
|
if (*ptr1 == '-' && *(ptr1+1) == '-') {
|
|
sstate.mimestate = MS_EPILOGUE;
|
|
}
|
|
else {
|
|
sstate.mimestate = MS_SUBPART;
|
|
}
|
|
result->length = prevpos - result->startpos;
|
|
*errcode = UURET_CONT;
|
|
}
|
|
else if (mssdepth > 0) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_B_NOT_FOUND));
|
|
/*
|
|
* restart and try again; don't restart if uu_fast_scanning
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
|
|
if (!uu_fast_scanning) {
|
|
*errcode = UURET_CONT;
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
}
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
else if (IsKnownHeader (line)) {
|
|
/* new message follows */
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
result->length = preheaders - result->startpos;
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
}
|
|
else if (ecount >= ELC_COUNT) {
|
|
/* new plain encoding */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
result->length = preenc - result->startpos;
|
|
fseek (datei, preenc, SEEK_SET);
|
|
}
|
|
|
|
/* produce result if uu_usepreamble is set */
|
|
if (uu_usepreamble && res) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
result->filename = _FP_strdup (line);
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
result->mimetype = _FP_strdup ("text/plain");
|
|
result->mode = 0644;
|
|
result->uudet = PT_ENCODED; /* plain text */
|
|
result->sfname = _FP_strdup (fname);
|
|
result->flags = FL_SINGLE | FL_PROPER;
|
|
result->partno = 1;
|
|
/* result->startpos set from above */
|
|
/* result->length set from above */
|
|
|
|
if ((sstate.envelope.subject != NULL && result->subject == NULL) ||
|
|
result->filename == NULL || result->sfname == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
/* otherwise, just return NULL */
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Scan a new part from a Multipart message. Check for a new local
|
|
* envelope (which defaults to `Content-Type: text/plain') and
|
|
* evaluate its Content-Type and Content-Transfer-Encoding. If this
|
|
* is another Multipart/something, push the current state onto our
|
|
* stack and dive into the new environment, starting with another
|
|
* preamble.
|
|
*/
|
|
|
|
if (sstate.ismime && sstate.mimestate == MS_SUBPART) {
|
|
memset (&localenv, 0, sizeof (headers));
|
|
result->startpos = ftell (datei);
|
|
prevpos = ftell (datei);
|
|
hcount = 0;
|
|
lcount = 0;
|
|
|
|
/*
|
|
* Duplicate some data from outer envelope
|
|
*/
|
|
|
|
localenv.mimevers = _FP_strdup (sstate.envelope.mimevers);
|
|
localenv.from = _FP_strdup (sstate.envelope.from);
|
|
localenv.subject = _FP_strdup (sstate.envelope.subject);
|
|
localenv.rcpt = _FP_strdup (sstate.envelope.rcpt);
|
|
localenv.date = _FP_strdup (sstate.envelope.date);
|
|
|
|
if ((sstate.envelope.mimevers != NULL && localenv.mimevers == NULL) ||
|
|
(sstate.envelope.from != NULL && localenv.from == NULL) ||
|
|
(sstate.envelope.subject != NULL && localenv.subject == NULL) ||
|
|
(sstate.envelope.rcpt != NULL && localenv.rcpt == NULL) ||
|
|
(sstate.envelope.date != NULL && localenv.date == NULL)) {
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
|
|
UUkillheaders (&localenv);
|
|
*errcode = UURET_NOMEM;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/* Scan subheader. But what if there is no subheader? */
|
|
hcount = 0;
|
|
lcount = 0;
|
|
preheaders = prevpos;
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
UUkillheaders (&localenv);
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
line[255] = '\0';
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (lcount > WAITHEADER && hcount == 0) {
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
prevpos = preheaders;
|
|
break;
|
|
}
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
if (ParseHeader (&localenv, ptr1) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
|
|
if (line[0] == '-' && line[1] == '-')
|
|
break;
|
|
|
|
prevpos = ftell (datei);
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
lcount++;
|
|
}
|
|
if (line[0] == '-' && line[1] == '-') {
|
|
/*
|
|
* this shouldn't happen, there must always be an empty line,
|
|
* but let's accept it anyway. Just skip back to before the
|
|
* boundary, so that it gets handled below
|
|
*/
|
|
fseek (datei, prevpos, SEEK_SET);
|
|
}
|
|
|
|
if (_FP_stristr (localenv.ctype, "multipart") != NULL) {
|
|
/* oh no, not again */
|
|
if (mssdepth >= MSMAXDEPTH) {
|
|
/* Argh, what an isane message. Treat as plain text */
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_MULTI_DEPTH));
|
|
}
|
|
else if (localenv.boundary == NULL) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_NO_BOUNDARY));
|
|
}
|
|
else {
|
|
memcpy (&multistack[mssdepth], &sstate, sizeof (scanstate));
|
|
memcpy (&sstate.envelope, &localenv, sizeof (headers));
|
|
memset (&localenv, 0, sizeof (headers));
|
|
sstate.mimestate = MS_PREAMBLE;
|
|
if ((sstate.source = _FP_strdup (sstate.source)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
|
|
if (*errcode == UURET_OK)
|
|
*errcode = UURET_CONT;
|
|
|
|
mssdepth++;
|
|
/* need a restart */
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* So this subpart is either plain text or something else. Check
|
|
* the Content-Type and Content-Transfer-Encoding. If the latter
|
|
* is a defined value, we know what to do and just copy everything
|
|
* up to the boundary.
|
|
* If Content-Transfer-Encoding is unknown or missing, look at the
|
|
* Content-Type. If it's "text/plain" or undefined, we subject the
|
|
* message to our encoding detection. Otherwise, treat as plain
|
|
* text.
|
|
* This is done because users might `attach' a uuencoded file, which
|
|
* would then be correctly typed as `text/plain'.
|
|
*/
|
|
|
|
if (_FP_stristr (localenv.ctenc, "base64") != NULL)
|
|
result->uudet = B64ENCODED;
|
|
else if (_FP_stristr (localenv.ctenc, "x-uue") != NULL) {
|
|
result->uudet = UU_ENCODED;
|
|
result->begin = result->end = 1;
|
|
}
|
|
else if (_FP_stristr (localenv.ctenc, "x-yenc") != NULL) {
|
|
result->uudet = YENC_ENCODED;
|
|
result->begin = result->end = 1;
|
|
}
|
|
else if (_FP_stristr (localenv.ctenc, "quoted-printable") != NULL)
|
|
result->uudet = QP_ENCODED;
|
|
else if (_FP_stristr (localenv.ctenc, "7bit") != NULL ||
|
|
_FP_stristr (localenv.ctenc, "8bit") != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
else if (_FP_stristr (localenv.ctype, "multipart") != NULL ||
|
|
_FP_stristr (localenv.ctype, "message") != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
|
|
/*
|
|
* If we're switched to MIME-only mode, handle as text
|
|
*/
|
|
|
|
if (uu_more_mime >= 2 && !result->uudet) {
|
|
result->uudet = PT_ENCODED;
|
|
}
|
|
|
|
if (result->uudet) {
|
|
/*
|
|
* Oh-kay, go ahead. Just read and wait for the boundary
|
|
*/
|
|
result->startpos = ftell (datei);
|
|
prevpos = ftell (datei);
|
|
blen = strlen (sstate.envelope.boundary);
|
|
lcount = 0;
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\0';
|
|
break;
|
|
}
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
line[255] = '\0';
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0)
|
|
break;
|
|
/*
|
|
* I've had a report of someone who tried to decode a huge file
|
|
* that had an early truncated multipart message and later another
|
|
* multipart message with the *same* boundary. Consequently, all
|
|
* some hundred messages inbetween were ignored ...
|
|
* This check here doesn't cover folded header lines, but we don't
|
|
* want to slow down scanning too much. We just check for
|
|
* Content-Type: multipart/... boundary="same-boundary"
|
|
*/
|
|
if (line[0] == 'C' && line[1] == 'o' &&
|
|
_FP_strnicmp (line, "Content-Type:", 13) == 0) {
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
ptr2 = (ptr1)?_FP_stristr(ptr1,"boundary"):NULL;
|
|
ptr1 = (ptr2)?ParseValue(ptr2):NULL;
|
|
if (ptr1 && strcmp (ptr1, sstate.envelope.boundary) == 0)
|
|
break;
|
|
for (res=0; ptr1 && res<mssdepth; res++)
|
|
if (strcmp (ptr1, multistack[res].envelope.boundary) == 0)
|
|
break;
|
|
if (res<mssdepth)
|
|
break;
|
|
}
|
|
if (!IsLineEmpty (line))
|
|
lcount++;
|
|
prevpos = ftell (datei);
|
|
}
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
|
|
ptr1 = line + 2 + blen;
|
|
if (*ptr1 == '-' && *(ptr1+1) == '-')
|
|
sstate.mimestate = MS_EPILOGUE;
|
|
else
|
|
sstate.mimestate = MS_SUBPART;
|
|
}
|
|
else {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_B_NOT_FOUND));
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
/*
|
|
* Don't retry if uu_fast_scanning
|
|
*/
|
|
|
|
if (uu_fast_scanning) {
|
|
UUkillheaders (&localenv);
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Retry, but listening to headers this time
|
|
*/
|
|
fseek (datei, result->startpos, SEEK_SET);
|
|
|
|
UUkillfread (result);
|
|
if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
memset (result, 0, sizeof (fileread));
|
|
|
|
if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result))==-1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
if (res == 1) {
|
|
/*
|
|
* new headers found
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
}
|
|
else {
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
}
|
|
/* produce result if uu_handletext is set */
|
|
/* or if the file is explicitely named */
|
|
if (result->uudet == B64ENCODED || lcount) {
|
|
if (localenv.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
|
|
result->filename == NULL && uu_handletext) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
if ((result->filename = _FP_strdup (line)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
result->subject = _FP_strdup (localenv.subject);
|
|
result->origin = _FP_strdup (localenv.from);
|
|
result->mimeid = _FP_strdup (localenv.mimeid);
|
|
result->mimetype = _FP_strdup (localenv.ctype);
|
|
result->mode = 0644;
|
|
result->sfname = _FP_strdup (fname);
|
|
result->flags = FL_SINGLE | FL_PROPER;
|
|
result->partno = 1;
|
|
/* result->uudet determined above */
|
|
/* result->startpos set from above */
|
|
result->length = prevpos - result->startpos;
|
|
|
|
if ((localenv.subject != NULL && result->subject == NULL) ||
|
|
result->filename == NULL || result->sfname == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
}
|
|
else {
|
|
/* don't produce a result */
|
|
_FP_free (result);
|
|
result = NULL;
|
|
}
|
|
if (*errcode == UURET_OK)
|
|
*errcode = UURET_CONT;
|
|
/*
|
|
* destroy local envelope
|
|
*/
|
|
UUkillheaders (&localenv);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* we're in a subpart, but the local headers don't give us any
|
|
* clue about what's to find here. So look for encoded data by
|
|
* ourselves.
|
|
*/
|
|
|
|
if ((res = ScanData (datei, fname, errcode,
|
|
sstate.envelope.boundary,
|
|
1, 0, result)) == -1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* we should really be at a boundary here, but check again
|
|
*/
|
|
blen = strlen (sstate.envelope.boundary);
|
|
prevpos = ftell (datei);
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\0';
|
|
break;
|
|
}
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
line[255] = '\0';
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0)
|
|
break;
|
|
if (line[0] == 'C' && line[1] == 'o' &&
|
|
_FP_strnicmp (line, "Content-Type:", 13) == 0) {
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
ptr2 = (ptr1)?_FP_stristr(ptr1,"boundary"):NULL;
|
|
ptr1 = (ptr2)?ParseValue(ptr2):NULL;
|
|
if (ptr1 && strcmp (ptr1, sstate.envelope.boundary) == 0)
|
|
break;
|
|
}
|
|
prevpos = ftell (datei);
|
|
}
|
|
/*
|
|
* check if this was the last subpart
|
|
*/
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
strncmp (line+2, sstate.envelope.boundary, blen) == 0) {
|
|
ptr1 = line + 2 + blen;
|
|
if (*ptr1 == '-' && *(ptr1+1) == '-')
|
|
sstate.mimestate = MS_EPILOGUE;
|
|
else
|
|
sstate.mimestate = MS_SUBPART;
|
|
}
|
|
else {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_B_NOT_FOUND));
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
|
|
if (uu_fast_scanning) {
|
|
UUkillheaders (&localenv);
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_BODY;
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Retry, listening to headers this time
|
|
*/
|
|
fseek (datei, result->startpos, SEEK_SET);
|
|
|
|
UUkillfread (result);
|
|
if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
|
|
*errcode = UURET_NOMEM;
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
memset (result, 0, sizeof (fileread));
|
|
|
|
if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result))==-1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
if (res == 1) {
|
|
/*
|
|
* new headers found
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
}
|
|
else {
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this file has been nicely MIME so far, then be very suspicious
|
|
* if ScanData reports anything else. So do a double check, and if
|
|
* it doesn't hold up, handle as plain text instead.
|
|
*/
|
|
|
|
if (strcmp (localenv.mimevers, "1.0") == 0 &&
|
|
_FP_stristr (localenv.ctype, "text") != NULL &&
|
|
sstate.ismime && sstate.mimestate == MS_SUBPART &&
|
|
!uu_desperate) {
|
|
if (result->uudet == UU_ENCODED && !(result->begin || result->end)) {
|
|
result->uudet = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* produce result
|
|
*/
|
|
|
|
if (result->uudet == 0) {
|
|
result->uudet = PT_ENCODED; /* plain text */
|
|
}
|
|
|
|
if (localenv.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if ((result->uudet==QP_ENCODED || result->uudet==PT_ENCODED) &&
|
|
result->filename==NULL && uu_handletext) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
if ((result->filename = _FP_strdup (line)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else {
|
|
/* assign a filename lateron */
|
|
}
|
|
if (result->mimetype) _FP_free (result->mimetype);
|
|
if (result->uudet) {
|
|
if (_FP_stristr (localenv.ctype, "text") != NULL &&
|
|
result->uudet != QP_ENCODED && result->uudet != PT_ENCODED)
|
|
result->mimetype = NULL; /* better don't set it */
|
|
else
|
|
result->mimetype = _FP_strdup (localenv.ctype);
|
|
}
|
|
if (result->origin) _FP_free (result->origin);
|
|
result->origin = _FP_strdup (localenv.from);
|
|
|
|
if (result->subject) _FP_free (result->subject);
|
|
result->subject = _FP_strdup (localenv.subject);
|
|
|
|
if (result->sfname == NULL)
|
|
if ((result->sfname = _FP_strdup (fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
|
|
result->length = prevpos - result->startpos;
|
|
result->flags = FL_SINGLE | FL_PROPER;
|
|
result->partno = 1;
|
|
|
|
if (result->mode == 0)
|
|
result->mode = 0644;
|
|
|
|
/*
|
|
* the other fields should already be set appropriately
|
|
*/
|
|
|
|
if (*errcode == UURET_OK)
|
|
*errcode = UURET_CONT;
|
|
|
|
/*
|
|
* kill local envelope
|
|
*/
|
|
UUkillheaders (&localenv);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* All right, so we're not in a Multipart message. Phew, took quite
|
|
* long to figure this out. But this might still be a MIME message
|
|
* body. And if it's a message/partial, we need more special handling
|
|
*/
|
|
|
|
if (sstate.isfolder && sstate.ismime && sstate.mimestate == MS_BODY &&
|
|
_FP_stristr (sstate.envelope.ctype, "message") != NULL &&
|
|
_FP_stristr (sstate.envelope.ctype, "partial") != NULL) {
|
|
|
|
result->startpos = ftell (datei);
|
|
|
|
if (sstate.envelope.partno == 1) {
|
|
/* read local envelope */
|
|
UUkillheaders (&localenv);
|
|
memset (&localenv, 0, sizeof (headers));
|
|
|
|
/* skip over blank lines first */
|
|
prevpos = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (!IsLineEmpty (line))
|
|
break;
|
|
prevpos = ftell (datei);
|
|
}
|
|
/* Next, read header. But what if there is no subheader? */
|
|
hcount = 0;
|
|
lcount = 0;
|
|
preheaders = prevpos;
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (lcount > WAITHEADER && hcount == 0) {
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
break;
|
|
}
|
|
ptr1 = ScanHeaderLine (datei, line);
|
|
if (ParseHeader (&localenv, ptr1) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
lcount++;
|
|
}
|
|
prevpos = ftell (datei);
|
|
/*
|
|
* Examine local header. We're mostly interested in the Content-Type
|
|
* and the Content-Transfer-Encoding.
|
|
*/
|
|
if (_FP_stristr (localenv.ctype, "multipart") != NULL) {
|
|
UUMessage (uuscan_id, __LINE__, UUMSG_WARNING,
|
|
uustring (S_MIME_PART_MULTI));
|
|
}
|
|
if (localenv.subject)
|
|
result->subject = _FP_strdup (localenv.subject);
|
|
else
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
|
|
if (localenv.from)
|
|
result->origin = _FP_strdup (localenv.from);
|
|
else
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
|
|
if (localenv.ctype)
|
|
result->mimetype = _FP_strdup (localenv.ctype);
|
|
else
|
|
result->mimetype = _FP_strdup ("text/plain");
|
|
|
|
if (_FP_stristr (localenv.ctenc, "quoted-printable") != NULL)
|
|
result->uudet = QP_ENCODED;
|
|
else if (_FP_stristr (localenv.ctenc, "base64") != NULL)
|
|
result->uudet = B64ENCODED;
|
|
else if (_FP_stristr (localenv.ctenc, "x-uue") != NULL) {
|
|
result->uudet = UU_ENCODED;
|
|
result->begin = result->end = 1;
|
|
}
|
|
else if (_FP_stristr (localenv.ctenc, "x-yenc") != NULL) {
|
|
result->uudet = YENC_ENCODED;
|
|
result->begin = result->end = 1;
|
|
}
|
|
else if (_FP_stristr (localenv.ctenc, "7bit") != NULL ||
|
|
_FP_stristr (localenv.ctenc, "8bit") != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
else if (_FP_stristr (localenv.ctype, "multipart") != NULL ||
|
|
_FP_stristr (localenv.ctype, "message") != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
|
|
/*
|
|
* If we're switched to MIME-only mode, handle as text
|
|
*/
|
|
|
|
if (uu_more_mime >= 2 && !result->uudet) {
|
|
result->uudet = PT_ENCODED;
|
|
}
|
|
}
|
|
else {
|
|
memset (&localenv, 0, sizeof (headers));
|
|
}
|
|
|
|
/*
|
|
* If this is Quoted-Printable or Plain Text, just try looking
|
|
* for the next message header. If uu_fast_scanning, and the
|
|
* encoding is known, there's no need to look below. Otherwise,
|
|
* we check the type of encoding first.
|
|
* The encoding type is determined on the first part; in all
|
|
* others, we also don't read on.
|
|
* If we have a partial multipart message, scan for headers, but
|
|
* do not react on standard MIME headers, as they are probably
|
|
* from the subparts. However, we're stuck if there's an embedded
|
|
* message/rfc822 :-(
|
|
* If it is a "trivial" (non-embedded) message/rfc822, skip over
|
|
* the message header and then start looking for the next header.
|
|
*/
|
|
if (uu_fast_scanning && (result->uudet!=0||sstate.envelope.partno!=1)) {
|
|
/* do nothing */
|
|
res = 0;
|
|
}
|
|
else if (result->uudet != 0) {
|
|
hcount = lcount = 0;
|
|
prevpos = ftell (datei);
|
|
|
|
if (_FP_stristr (localenv.ctype, "message") != NULL &&
|
|
_FP_stristr (localenv.ctype, "rfc822") != NULL) {
|
|
/*
|
|
* skip over empty lines and local header
|
|
*/
|
|
preheaders = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
if (!IsLineEmpty (line)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
lcount++;
|
|
if (lcount > WAITHEADER && hcount < hlcount.afternl)
|
|
break;
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
}
|
|
if (hcount < hlcount.afternl)
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
hcount = lcount = 0;
|
|
}
|
|
|
|
/*
|
|
* look for next header
|
|
*/
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (ferror (datei))
|
|
break;
|
|
line[255] = '\0';
|
|
|
|
if ((vflag = IsKnownHeader (line))) {
|
|
(void) ScanHeaderLine (datei, line);
|
|
|
|
if (result->uudet != PT_ENCODED || vflag == 1) {
|
|
if (hcount == 0)
|
|
preheaders = prevpos;
|
|
hcount++;
|
|
lcount++;
|
|
if (hcount >= hlcount.restart) {
|
|
/*
|
|
* Hey, a new header starts here
|
|
*/
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
prevpos = preheaders;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (lcount > WAITHEADER) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
}
|
|
else if (hcount) {
|
|
lcount++;
|
|
}
|
|
prevpos = ftell (datei);
|
|
}
|
|
res = 1;
|
|
}
|
|
else {
|
|
/*
|
|
* Otherwise, let's see what we can find ourself. No
|
|
* boundary (NULL) but MIME, and respect new headers.
|
|
*/
|
|
if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result)) == -1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
UUkillheaders (&localenv);
|
|
return NULL;
|
|
}
|
|
if (result->uudet == 0 && uu_handletext)
|
|
result->uudet = PT_ENCODED;
|
|
|
|
prevpos = ftell (datei);
|
|
}
|
|
/*
|
|
* produce result
|
|
*/
|
|
if (localenv.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (localenv.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if (sstate.envelope.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if ((result->uudet==QP_ENCODED || result->uudet==PT_ENCODED) &&
|
|
result->filename == NULL) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
if ((result->filename = _FP_strdup (line)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else {
|
|
/* assign a filename lateron */
|
|
}
|
|
if (result->subject == NULL) {
|
|
if (sstate.envelope.subject)
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
}
|
|
result->partno = sstate.envelope.partno;
|
|
result->maxpno = sstate.envelope.numparts;
|
|
result->flags = FL_PARTIAL |
|
|
((res==1 || uu_fast_scanning) ? FL_PROPER : 0) |
|
|
((uu_fast_scanning) ? FL_TOEND : 0);
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
if (result->partno == 1)
|
|
result->begin = 1;
|
|
|
|
if (uu_fast_scanning)
|
|
result->length = progress.fsize - result->startpos;
|
|
else
|
|
result->length = prevpos - result->startpos;
|
|
|
|
if (result->sfname == NULL)
|
|
result->sfname = _FP_strdup (fname);
|
|
|
|
if (result->mode == 0)
|
|
result->mode = 0644;
|
|
|
|
/*
|
|
* the other fields should already be set appropriately
|
|
*/
|
|
|
|
if (res == 1) {
|
|
/*
|
|
* new headers found
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
|
|
UUkillheaders (&sstate.envelope);
|
|
memset (&sstate.envelope, 0, sizeof (headers));
|
|
}
|
|
else {
|
|
/*
|
|
* otherwise, this can't be a mail folder
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
/*
|
|
* kill local envelope
|
|
*/
|
|
UUkillheaders (&localenv);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* If this is a MIME body, honor a Content-Type different than
|
|
* text/plain or a proper Content-Transfer-Encoding.
|
|
* We also go in here if we have an assigned filename - this means
|
|
* that we've had a Content-Disposition field, and we should probably
|
|
* decode a plain-text segment with a filename.
|
|
*/
|
|
|
|
if (sstate.isfolder && sstate.ismime &&
|
|
sstate.mimestate == MS_BODY &&
|
|
(_FP_stristr (sstate.envelope.ctenc, "quoted-printable") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctenc, "base64") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctenc, "x-uue") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctenc, "x-yenc") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctype, "message") != NULL ||
|
|
sstate.envelope.fname != NULL)) {
|
|
|
|
if (sstate.envelope.subject)
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
if (sstate.envelope.from)
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
|
|
if (sstate.envelope.ctype)
|
|
result->mimetype = _FP_strdup (sstate.envelope.ctype);
|
|
else
|
|
result->mimetype = _FP_strdup ("text/plain");
|
|
|
|
if (_FP_stristr (sstate.envelope.ctenc, "quoted-printable") != NULL)
|
|
result->uudet = QP_ENCODED;
|
|
else if (_FP_stristr (sstate.envelope.ctenc, "base64") != NULL)
|
|
result->uudet = B64ENCODED;
|
|
else if (_FP_stristr (sstate.envelope.ctenc, "x-uue") != NULL) {
|
|
result->uudet = UU_ENCODED;
|
|
result->begin = result->end = 1;
|
|
}
|
|
else if (_FP_stristr (sstate.envelope.ctenc, "x-yenc") != NULL) {
|
|
result->uudet = YENC_ENCODED;
|
|
}
|
|
else if (_FP_stristr (sstate.envelope.ctenc, "7bit") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctenc, "8bit") != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
else if (_FP_stristr (sstate.envelope.ctype, "multipart") != NULL ||
|
|
_FP_stristr (sstate.envelope.ctype, "message") != NULL ||
|
|
sstate.envelope.fname != NULL)
|
|
result->uudet = PT_ENCODED;
|
|
|
|
/*
|
|
* If we're switched to MIME-only mode, handle as text
|
|
*/
|
|
|
|
if (uu_more_mime >= 2 && !result->uudet) {
|
|
result->uudet = PT_ENCODED;
|
|
}
|
|
|
|
result->startpos = prevpos = ftell (datei);
|
|
|
|
/*
|
|
* If this is Quoted-Printable or Plain Text, just try looking
|
|
* for the next message header. If uu_fast_scanning, we know
|
|
* there won't be more headers.
|
|
* If it is a "trivial" (non-embedded) message/rfc822, skip over
|
|
* the message header and then start looking for the next header.
|
|
*/
|
|
if (result->uudet != 0 && uu_fast_scanning) {
|
|
/* do nothing */
|
|
res = 0;
|
|
}
|
|
else if (result->uudet != 0) {
|
|
hcount = lcount = 0;
|
|
prevpos = ftell (datei);
|
|
|
|
if (_FP_stristr (sstate.envelope.ctype, "message") != NULL &&
|
|
_FP_stristr (sstate.envelope.ctype, "rfc822") != NULL) {
|
|
/*
|
|
* skip over empty lines and local header
|
|
*/
|
|
preheaders = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
if (!IsLineEmpty (line)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (!feof (datei) && !IsLineEmpty (line)) {
|
|
if (IsKnownHeader (line))
|
|
hcount++;
|
|
lcount++;
|
|
if (lcount > WAITHEADER && hcount < hlcount.afternl)
|
|
break;
|
|
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
line[255] = '\0';
|
|
}
|
|
if (hcount < hlcount.afternl)
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
hcount = lcount = 0;
|
|
}
|
|
|
|
/*
|
|
* look for next header
|
|
*/
|
|
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL)
|
|
break;
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (ferror (datei))
|
|
break;
|
|
line[255] = '\0';
|
|
|
|
if (IsKnownHeader (line)) {
|
|
(void) ScanHeaderLine (datei, line);
|
|
if (hcount == 0)
|
|
preheaders = prevpos;
|
|
hcount++;
|
|
lcount++;
|
|
if (hcount >= hlcount.restart) {
|
|
/*
|
|
* Hey, a new header starts here
|
|
*/
|
|
fseek (datei, preheaders, SEEK_SET);
|
|
prevpos = preheaders;
|
|
break;
|
|
}
|
|
}
|
|
else if (lcount > WAITHEADER) {
|
|
hcount = 0;
|
|
lcount = 0;
|
|
}
|
|
else if (hcount) {
|
|
lcount++;
|
|
}
|
|
prevpos = ftell (datei);
|
|
}
|
|
res = 1;
|
|
}
|
|
else {
|
|
/*
|
|
* Otherwise, let's see what we can find ourself. No
|
|
* boundary (NULL) but MIME, and respect new headers.
|
|
*/
|
|
if ((res = ScanData (datei, fname, errcode, NULL, 1, 1, result)) == -1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
return NULL;
|
|
}
|
|
if (result->uudet == 0 && uu_handletext) {
|
|
result->startpos = before; /* display headers */
|
|
result->uudet = PT_ENCODED;
|
|
}
|
|
|
|
prevpos = ftell (datei);
|
|
}
|
|
/*
|
|
* produce result
|
|
*/
|
|
if (sstate.envelope.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
|
|
result->filename == NULL) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
if ((result->filename = _FP_strdup (line)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else {
|
|
/* assign a filename lateron */
|
|
}
|
|
if (result->subject == NULL) {
|
|
if (sstate.envelope.subject)
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
}
|
|
result->flags = ((res==1||uu_fast_scanning)?FL_PROPER:0) |
|
|
((uu_fast_scanning) ? FL_TOEND : 0);
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
|
|
if (uu_fast_scanning)
|
|
result->length = progress.fsize - result->startpos;
|
|
else
|
|
result->length = prevpos - result->startpos;
|
|
|
|
if (result->sfname == NULL)
|
|
result->sfname = _FP_strdup (fname);
|
|
|
|
if (result->mode == 0)
|
|
result->mode = 0644;
|
|
|
|
/*
|
|
* the other fields should already be set appropriately
|
|
*/
|
|
|
|
if (res == 1) {
|
|
/*
|
|
* new headers found
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
|
|
UUkillheaders (&sstate.envelope);
|
|
memset (&sstate.envelope, 0, sizeof (headers));
|
|
}
|
|
else {
|
|
/*
|
|
* otherwise, this can't be a mail folder
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Some files have reduced headers, and what should be a multipart
|
|
* message is missing the proper Content-Type. If the first thing
|
|
* we find after a couple of empty lines is a boundary, try it!
|
|
* But make sure that this is indeed intended as being a boundary.
|
|
*
|
|
* Only accept it if there was indeed no Content-Type header line
|
|
* and if the following line is a proper Content-Type header. BTW,
|
|
* we know that sstate.envelope.boundary is NULL, or we wouldn't
|
|
* be here!
|
|
*/
|
|
|
|
if ((sstate.envelope.ctype == NULL ||
|
|
_FP_stristr (sstate.envelope.ctype, "multipart") != NULL) &&
|
|
!uu_more_mime) {
|
|
prevpos = ftell (datei);
|
|
while (!feof (datei)) {
|
|
if (_FP_fgets (line, 255, datei) == NULL) {
|
|
line[0] = '\0';
|
|
break;
|
|
}
|
|
if (UUBUSYPOLL(ftell(datei),progress.fsize)) SPCANCEL();
|
|
if (!IsLineEmpty (line))
|
|
break;
|
|
}
|
|
if (line[0] == '-' && line[1] == '-' &&
|
|
!IsLineEmpty (line+2) && !feof (datei)) {
|
|
ptr1 = _FP_strrstr (line+2, "--");
|
|
ptr2 = ScanHeaderLine (datei, NULL);
|
|
if ((ptr1 == NULL || (*(ptr1+2) != '\012' && *(ptr1+2) != '\015')) &&
|
|
ptr2 && _FP_strnicmp (ptr2, "Content-", 8) == 0) {
|
|
/*
|
|
* hmm, okay, let's do it!
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 1;
|
|
sstate.mimestate = MS_PREAMBLE;
|
|
/*
|
|
* get boundary
|
|
*/
|
|
ptr1 = line+2;
|
|
while (*ptr1 && !isspace(*ptr1))
|
|
ptr1++;
|
|
*ptr1 = '\0';
|
|
|
|
sstate.envelope.mimevers = _FP_strdup ("1.0");
|
|
sstate.envelope.boundary = _FP_strdup (line+2);
|
|
|
|
/*
|
|
* need restart
|
|
*/
|
|
|
|
fseek (datei, prevpos, SEEK_SET);
|
|
|
|
_FP_free (result);
|
|
return NULL;
|
|
}
|
|
}
|
|
fseek (datei, prevpos, SEEK_SET);
|
|
}
|
|
|
|
/*
|
|
* Hmm, we're not in a ''special'' state, so it's more or less
|
|
* Freestyle time. Anyway, if this seems to be a Mime message,
|
|
* don't allow the minimal Base64 handling.
|
|
*/
|
|
|
|
if (sstate.envelope.subject)
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
if (sstate.envelope.from)
|
|
result->origin = _FP_strdup (sstate.envelope.from);
|
|
|
|
if (sstate.envelope.ctype)
|
|
result->mimetype = _FP_strdup (sstate.envelope.ctype);
|
|
|
|
if ((res=ScanData (datei, fname, errcode, NULL,
|
|
sstate.ismime, 1, result))==-1) {
|
|
/* oops, something went wrong */
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
UUkillfread (result);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* produce result
|
|
*/
|
|
|
|
if (result->uudet == 0 && uu_handletext) {
|
|
result->startpos = before; /* display headers */
|
|
result->uudet = PT_ENCODED;
|
|
result->partno = 1;
|
|
}
|
|
|
|
if (result->uudet == YENC_ENCODED && result->filename != NULL) {
|
|
/*
|
|
* prevent replacing the filename found on the =ybegin line
|
|
*/
|
|
}
|
|
else if (sstate.envelope.fname) {
|
|
_FP_free (result->filename);
|
|
if ((result->filename = _FP_strdup (sstate.envelope.fname)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else if ((result->uudet==QP_ENCODED||result->uudet==PT_ENCODED) &&
|
|
result->filename == NULL) {
|
|
sprintf (line, "%04d.txt", ++mimseqno);
|
|
if ((result->filename = _FP_strdup (line)) == NULL)
|
|
*errcode = UURET_NOMEM;
|
|
}
|
|
else {
|
|
/* assign a filename lateron */
|
|
}
|
|
|
|
if (result->subject == NULL) {
|
|
if (sstate.envelope.subject)
|
|
result->subject = _FP_strdup (sstate.envelope.subject);
|
|
}
|
|
|
|
result->flags = (result->uudet==PT_ENCODED)?FL_SINGLE:0;
|
|
result->mimeid = _FP_strdup (sstate.envelope.mimeid);
|
|
result->length = ftell (datei) - result->startpos;
|
|
|
|
if (result->mode == 0)
|
|
result->mode = 0644;
|
|
|
|
if (result->sfname == NULL)
|
|
result->sfname = _FP_strdup (fname);
|
|
|
|
if (res == 1) {
|
|
/*
|
|
* new headers found
|
|
*/
|
|
sstate.isfolder = 1;
|
|
sstate.ismime = 0;
|
|
sstate.mimestate = MS_HEADERS;
|
|
|
|
UUkillheaders (&sstate.envelope);
|
|
memset (&sstate.envelope, 0, sizeof (headers));
|
|
}
|
|
else {
|
|
/*
|
|
* otherwise, this can't be a mail folder
|
|
*/
|
|
sstate.isfolder = 0;
|
|
sstate.ismime = 0;
|
|
}
|
|
|
|
return result;
|
|
|
|
/*
|
|
* Emergency handling. Set errcode before jumping here.
|
|
*/
|
|
ScanPartEmergency:
|
|
UUkillfread (result);
|
|
UUkillheaders (&localenv);
|
|
|
|
while (mssdepth) {
|
|
mssdepth--;
|
|
UUkillheaders (&(multistack[mssdepth].envelope));
|
|
_FP_free (multistack[mssdepth].source);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|