433 lines
8 KiB
C
433 lines
8 KiB
C
|
/*
|
||
|
* mime.c
|
||
|
*
|
||
|
* Copyright (c) 1997, John Kilburg <john@cs.unlv.edu>
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
#include "port_before.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#ifdef HAVE_STRING_H
|
||
|
#include <string.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_STDLIB_H
|
||
|
#include <stdlib.h>
|
||
|
#endif
|
||
|
|
||
|
#include "port_after.h"
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "mime.h"
|
||
|
|
||
|
typedef struct MIMEFieldP *MIMEField;
|
||
|
typedef struct MIMEParamP *MIMEParam;
|
||
|
|
||
|
/*
|
||
|
* MIMEish parsing thing.
|
||
|
*
|
||
|
* Tries to deal with stuff of the form:
|
||
|
*
|
||
|
* field-name: field-value; param[=value]; param[=value]; ...
|
||
|
*/
|
||
|
|
||
|
struct MIMEHeaderP
|
||
|
{
|
||
|
GList list; /* list of MIMEField */
|
||
|
MemPool mp;
|
||
|
size_t i;
|
||
|
size_t len;
|
||
|
bool data_found;
|
||
|
};
|
||
|
|
||
|
struct MIMEFieldP
|
||
|
{
|
||
|
char *name;
|
||
|
char *value;
|
||
|
GList list; /* list of param */
|
||
|
};
|
||
|
|
||
|
struct MIMEParamP
|
||
|
{
|
||
|
char *name;
|
||
|
char *value;
|
||
|
};
|
||
|
|
||
|
static MIMEParam parse_param _ArgProto((char *));
|
||
|
static MIMEField parse_line _ArgProto((MemPool, char *));
|
||
|
|
||
|
MIMEHeader
|
||
|
MIMECreateHeader()
|
||
|
{
|
||
|
MemPool mp;
|
||
|
MIMEHeader mh;
|
||
|
|
||
|
mp = MPCreate();
|
||
|
mh = MPCGet(mp, sizeof(struct MIMEHeaderP));
|
||
|
mh->mp = mp;
|
||
|
mh->list = GListCreateX(mp);
|
||
|
mh->data_found = false;
|
||
|
|
||
|
return(mh);
|
||
|
}
|
||
|
|
||
|
static MIMEParam
|
||
|
parse_param(line)
|
||
|
char *line;
|
||
|
{
|
||
|
MIMEParam p = NULL;
|
||
|
|
||
|
return(p);
|
||
|
}
|
||
|
|
||
|
static MIMEField
|
||
|
parse_line(mp, line)
|
||
|
MemPool mp;
|
||
|
char *line;
|
||
|
{
|
||
|
MIMEField mf = NULL;
|
||
|
MIMEParam param;
|
||
|
char *cp;
|
||
|
char *ps = NULL;
|
||
|
bool inquote = false;
|
||
|
char *t;
|
||
|
|
||
|
for (cp = line; *cp != '\0'; cp++)
|
||
|
{
|
||
|
if (mf != NULL)
|
||
|
{
|
||
|
if (inquote)
|
||
|
{
|
||
|
if (*cp == '"') inquote = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (*cp == '"') inquote = true;
|
||
|
else if (*cp == ';' || *cp == '\0')
|
||
|
{
|
||
|
while (isspace8(*ps) && ps < cp) ps++;
|
||
|
if (ps < cp)
|
||
|
{
|
||
|
t = (char *)MPGet(mp, cp - ps + 1);
|
||
|
strncpy(t, ps, cp - ps);
|
||
|
t[cp - ps] = '\0';
|
||
|
|
||
|
if (mf->value == NULL) mf->value = t;
|
||
|
else
|
||
|
{
|
||
|
if (mf->list == NULL) mf->list = GListCreateX(mp);
|
||
|
if ((param = parse_param(t)) != NULL)
|
||
|
{
|
||
|
GListAddTail(mf->list, param);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ps = cp + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (*cp == ':')
|
||
|
{
|
||
|
mf = (MIMEField)MPCGet(mp, sizeof(struct MIMEFieldP));
|
||
|
mf->name = (char *)MPGet(mp, cp - line + 1);
|
||
|
strncpy(mf->name, line, cp - line);
|
||
|
mf->name[cp - line] = '\0';
|
||
|
ps = cp + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Catch the value or parameter that is terminated with \0
|
||
|
*/
|
||
|
if (mf != NULL && ps < cp)
|
||
|
{
|
||
|
while (isspace8(*ps) && ps < cp) ps++;
|
||
|
if (ps < cp)
|
||
|
{
|
||
|
t = (char *)MPGet(mp, cp - ps + 1);
|
||
|
strncpy(t, ps, cp - ps);
|
||
|
t[cp - ps] = '\0';
|
||
|
|
||
|
if (mf->value == NULL) mf->value = t;
|
||
|
else
|
||
|
{
|
||
|
if (mf->list == NULL) mf->list = GListCreateX(mp);
|
||
|
if ((param = parse_param(t)) != NULL)
|
||
|
{
|
||
|
GListAddTail(mf->list, param);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(mf);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEParseBuffer
|
||
|
*
|
||
|
* This is the entry point for code to parse a buffer that contains
|
||
|
* a MIME-like thing chunk of chars.
|
||
|
*/
|
||
|
int
|
||
|
MIMEParseBuffer(mh, buf, len)
|
||
|
MIMEHeader mh;
|
||
|
char *buf;
|
||
|
size_t len;
|
||
|
{
|
||
|
MIMEField mf;
|
||
|
size_t j;
|
||
|
char *cp, *pcp, *ls, *le;
|
||
|
char *line;
|
||
|
size_t usedlen;
|
||
|
size_t linelen;
|
||
|
|
||
|
myassert(mh->data_found, "Must call MIMEFindData before MIMEParseBuffer");
|
||
|
|
||
|
usedlen = 0;
|
||
|
linelen = BUFSIZ;
|
||
|
line = (char *)alloc_mem(linelen);
|
||
|
|
||
|
le = NULL;
|
||
|
ls = cp = buf;
|
||
|
pcp = "";
|
||
|
for (j = 0; j < len; j++, pcp = cp, cp++)
|
||
|
{
|
||
|
if (*cp != '\n') continue;
|
||
|
|
||
|
/*
|
||
|
* If line ends with \r\n then shift EOL back one character so that
|
||
|
* \r doesn't get included in the line.
|
||
|
*/
|
||
|
if (*pcp == '\r') le = pcp;
|
||
|
else le = cp;
|
||
|
|
||
|
/*
|
||
|
* If the line start is the same as the line end then it must be
|
||
|
* a blank line and its time to bail out.
|
||
|
*/
|
||
|
if (ls == le) break;
|
||
|
|
||
|
/*
|
||
|
* If the line doesn't begin with a space character then flush the
|
||
|
* previous line (if there was a previous) as a complete field.
|
||
|
* Otherwise remove the blank space from the continuation line.
|
||
|
*/
|
||
|
if (!isspace8(*ls))
|
||
|
{
|
||
|
if (usedlen > 0)
|
||
|
{
|
||
|
if ((mf = parse_line(mh->mp, line)) != NULL)
|
||
|
{
|
||
|
GListAddTail(mh->list, mf);
|
||
|
}
|
||
|
usedlen = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while(isspace8(*ls) && ls < le) ls++;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If there is something besides whitespace then stick it in the line
|
||
|
* buffer.
|
||
|
*/
|
||
|
if (ls < le)
|
||
|
{
|
||
|
if (linelen < usedlen + (le - ls))
|
||
|
{
|
||
|
linelen += ((le - ls) / BUFSIZ + 1) * BUFSIZ;
|
||
|
line = (char *)realloc(line, linelen);
|
||
|
}
|
||
|
strncpy(line + usedlen, ls, le - ls);
|
||
|
usedlen += le - ls;
|
||
|
line[usedlen] = '\0';
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Start the next line after the \n
|
||
|
*/
|
||
|
ls = cp + 1;
|
||
|
}
|
||
|
|
||
|
if (usedlen > 0)
|
||
|
{
|
||
|
if ((mf = parse_line(mh->mp, line)) != NULL)
|
||
|
{
|
||
|
GListAddTail(mh->list, mf);
|
||
|
}
|
||
|
usedlen = 0;
|
||
|
}
|
||
|
|
||
|
free_mem(line);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEDestroyHeader
|
||
|
*/
|
||
|
void
|
||
|
MIMEDestroyHeader(mh)
|
||
|
MIMEHeader mh;
|
||
|
{
|
||
|
MPDestroy(mh->mp);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEFindData
|
||
|
*
|
||
|
* Searches for \n\n and \r\n\r\n in a string. Returns the offset after
|
||
|
* the pattern;
|
||
|
*/
|
||
|
int
|
||
|
MIMEFindData(mh, data, len, doff)
|
||
|
MIMEHeader mh;
|
||
|
char *data;
|
||
|
size_t len;
|
||
|
size_t *doff;
|
||
|
{
|
||
|
char *cp;
|
||
|
char n[4];
|
||
|
size_t j;
|
||
|
|
||
|
myassert(mh->i < len, "MIMEFindData: Inconsistent buffer sizes");
|
||
|
|
||
|
memset(n, 0, sizeof(n));
|
||
|
j = 0;
|
||
|
for (cp = data + mh->i; mh->i < len; mh->i++, j++, cp++)
|
||
|
{
|
||
|
n[j % 4] = *cp;
|
||
|
|
||
|
/*
|
||
|
* Mmmm fun. If there are two or more characters and the current
|
||
|
* and previous character are '\n' (i.e. blank line) then the end
|
||
|
* of the header has been reached. If there are 4 or more characters
|
||
|
* and the sequence of characters (in reverse order) is \n\r\n\r then
|
||
|
* the end of header has been reached.
|
||
|
*/
|
||
|
if (j >= 2 && n[j % 4] == '\n' &&
|
||
|
(n[(j - 1) % 4] == '\n' ||
|
||
|
(j >= 4 && n[(j - 1) % 4] == '\r' && n[(j - 2) % 4] == '\n' &&
|
||
|
n[(j - 3) % 4] == '\r')))
|
||
|
{
|
||
|
mh->data_found = true;
|
||
|
*doff = mh->i + 1;
|
||
|
return(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEGetField
|
||
|
*/
|
||
|
int
|
||
|
MIMEGetField(mh, name, value)
|
||
|
MIMEHeader mh;
|
||
|
char *name;
|
||
|
char **value;
|
||
|
{
|
||
|
MIMEField f;
|
||
|
|
||
|
for (f = (MIMEField)GListGetHead(mh->list); f != NULL;
|
||
|
f = (MIMEField)GListGetNext(mh->list))
|
||
|
{
|
||
|
if (strlen(f->name) == strlen(name) && strcasecmp(f->name, name) == 0)
|
||
|
{
|
||
|
*value = f->value;
|
||
|
return(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEAddLine
|
||
|
*
|
||
|
* Adds a field to a header. Parses out parameters into structures.
|
||
|
*/
|
||
|
void
|
||
|
MIMEAddLine(mh, line)
|
||
|
MIMEHeader mh;
|
||
|
char *line;
|
||
|
{
|
||
|
MIMEField f;
|
||
|
|
||
|
if ((f = parse_line(mh->mp, line)) != NULL)
|
||
|
{
|
||
|
GListAddTail(mh->list, f);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEAddField
|
||
|
*/
|
||
|
void
|
||
|
MIMEAddField(mh, name, value)
|
||
|
MIMEHeader mh;
|
||
|
char *name;
|
||
|
char *value;
|
||
|
{
|
||
|
MIMEField mf;
|
||
|
mf = (MIMEField)MPCGet(mh->mp, sizeof(struct MIMEFieldP));
|
||
|
mf->name = MPStrDup(mh->mp, name);
|
||
|
mf->value = MPStrDup(mh->mp, value);
|
||
|
GListAddTail(mh->list, mf);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* MIMEWriteHeader
|
||
|
*
|
||
|
* This doesn't break long lines.
|
||
|
*/
|
||
|
int
|
||
|
MIMEWriteHeader(mh, fp)
|
||
|
MIMEHeader mh;
|
||
|
FILE *fp;
|
||
|
{
|
||
|
MIMEField mf;
|
||
|
MIMEParam param;
|
||
|
|
||
|
for (mf = (MIMEField)GListGetHead(mh->list); mf != NULL;
|
||
|
mf = (MIMEField)GListGetNext(mh->list))
|
||
|
{
|
||
|
fprintf (fp, "%s: %s", mf->name, mf->value);
|
||
|
if (mf->list != NULL)
|
||
|
{
|
||
|
for (param = (MIMEParam)GListGetHead(mf->list); param != NULL;
|
||
|
param = (MIMEParam)GListGetNext(mf->list))
|
||
|
{
|
||
|
fprintf (fp, "; %s", param->name);
|
||
|
if (param->value != NULL) fprintf (fp, "=%s", param->value);
|
||
|
}
|
||
|
}
|
||
|
fprintf (fp, "\n");
|
||
|
}
|
||
|
fprintf (fp, "\n");
|
||
|
|
||
|
return(0);
|
||
|
}
|