/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1987-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped

/*
 * pax mime format
 */

#include "format.h"

#define MIME_HEADER	1024

typedef struct Mime_s
{
	size_t		fill;		/* last member filler size	*/
	size_t		length;		/* separator magic length	*/
	char		magic[1];	/* separator magic		*/
} Mime_t;

static int
mime_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	register Mime_t*	mime;
	char*			s;
	char*			t;
	size_t			n;

	s = state.tmp.buffer;
	if (paxread(pax, ap, s, (off_t)0, (off_t)MIME_HEADER, 0) <= 0)
		return 0;
	paxunread(pax, ap, s, MIME_HEADER);
	if (s[0] != '-' || s[1] != '-' || !(t = (char*)memchr(s, '\n', MIME_HEADER - 2)) || (t - s + 8) >= MIME_HEADER || strncasecmp(t + 1, "Content", 7))
		return 0;
	n = t - s + 1;
	if (t > s && *(t - 1) == '\r')
		t--;
	n = t - s;
	if (!(mime = newof(0, Mime_t, 1, n)))
	{
		nospace();
		return -1;
	}
	ap->data = mime;
	mime->length = n;
	memcpy(mime->magic, s, mime->length);
	message((-1, "mime magic `%s'", mime->magic));
	paxread(pax, ap, NiL, (off_t)0, n, 0);
	return 1;
}

static int
mime_done(Pax_t* pax, register Archive_t* ap)
{
	if (ap->data)
	{
		free(ap->data);
		ap->data = 0;
	}
	return 0;
}

static int
mime_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	register Mime_t*	mime = (Mime_t*)ap->data;
	register char*		s;
	register char*		t;
	register char*		v;
	off_t			m;
	off_t			b;
	size_t			n;
	int			loop;

	if (paxread(pax, ap, s = state.tmp.buffer, mime->length + 2, mime->length + 2, 1) <= 0 || memcmp(s, mime->magic, mime->length))
		error(3, "%s: corrupt %s format member header -- separator not found", ap->name, ap->format->name);
	else if (*(s += mime->length) == '-' && *(s + 1) == '-')
	{
		while (paxread(pax, ap, s, 1, 1, 1) > 0 && *s != '\n');
		return 0;
	}
	else if (*s == '\n')
		paxunread(pax, ap, s + 1, 1);
	else if (*s != '\r' && *(s + 1) != '\n')
		error(3, "%s: corrupt %s format member header -- separator line not found", ap->name, ap->format->name);
	f->name = 0;
	for (;;)
	{
		for (t = (s = state.tmp.buffer) + state.buffersize - 1; s < t; s++)
			if (paxread(pax, ap, s, 1, 1, 1) <= 0)
				error(3, "%s: unexpected %s format EOF", ap->name, ap->format->name);
			else if (*s == '\n')
			{
				if (s > state.tmp.buffer && *(s - 1) == '\r')
					s--;
				*s = 0;
				break;
			}
		s = state.tmp.buffer;
		if (!*s)
			break;
		if (strncasecmp(s, "content-", 8))
			error(3, "%s: corrupt %s format member header", ap->name, ap->format->name);
		if (t = strchr(s, ':'))
			s = t + 1;
		while (isspace(*s))
			s++;
		for (;;)
		{
			for (t = s; *s && *s != ';' && *s != '='; s++);
			if (!(n = s - t))
				break;
			if (*s == '=')
			{
				if (*++s == '"')
					for (v = ++s; *s && *s != '"'; s++);
				else
					for (v = s; *s && *s != ';'; s++);
			}
			else
				v = s;
			m = s - v;
			if (*s)
				*s++ = 0;
			for (; *s == ';' || isspace(*s); s++);
			if (!f->name && n == 4 && !strncasecmp(t, "name", 4) || n == 8 && !strncasecmp(t, "filename", 8))
				f->name = paxstash(pax, &ap->stash.head, v, m);
		}
	}
	if (!f->name)
	{
		if (s = strrchr(ap->name, '/'))
			s++;
		else
			s = ap->name;
		f->name = paxstash(pax, &ap->stash.head, s, strlen(s) + 16);
		sfsprintf(f->name, ap->stash.head.size, "%s-%d", s, ap->entries + 1);
	}
	if (!ap->io->seekable)
		seekable(ap);
	f->st->st_size = 0;
	loop = 0;
	b = paxseek(pax, ap, 0, SEEK_CUR, 0);
	while (s = paxget(pax, ap, 0, &m))
	{
		if (m < mime->length)
		{
			if (loop++)
				error(3, "%s: corrupt %s format member header [too short]", ap->name, ap->format->name);
			paxseek(pax, ap, -m, SEEK_CUR, 0);
			paxsync(pax, ap, 0);
			continue;
		}
		v = s;
		for (t = s + m - mime->length; s = memchr(s, '-', t - s); s++)
			if (!memcmp(s, mime->magic, mime->length))
			{
				paxseek(pax, ap, b, SEEK_CUR, 0);
				paxsync(pax, ap, 0);
				if (s > v && *(s - 1) == '\n')
				{
					mime->fill++;
					if (s > (v + 1) && *(s - 2) == '\r')
						mime->fill++;
				}
				f->st->st_size += (s - v) - mime->fill;
				f->st->st_mtime = NOW;
				f->st->st_mode = X_IFREG|X_IRUSR|X_IRGRP|X_IROTH;
				f->st->st_uid = state.uid;
				f->st->st_gid = state.gid;
				f->st->st_dev = 0;
				f->st->st_ino = 0;
				f->st->st_nlink = 1;
				IDEVICE(f->st, 0);
				f->linktype = NOLINK;
				f->linkpath = 0;
				f->uidname = 0;
				f->gidname = 0;
				return 1;
			}
		paxseek(pax, ap, -(off_t)mime->length, SEEK_CUR, 0);
		paxsync(pax, ap, 0);
		f->st->st_size += m;
	}
	return 0;
}

Format_t	pax_mime_format =
{
	"mime",
	0,
	"encapsulated mime",
	0,
	ARCHIVE|DOS|NOHARDLINKS|IN,
	DEFBUFFER,
	DEFBLOCKS,
	0,
	PAXNEXT(pax_mime_next),
	0,
	mime_done,
	mime_getprologue,
	mime_getheader,
};

PAXLIB(&pax_mime_format)
