/***********************************************************************
*                                                                      *
*               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 tnef format
 */

#include "format.h"

#include <tm.h>

#define TNEF_MAGIC	0x223e9f78

typedef struct Tnef_s
{
	char*		format;
	off_t		offset;
} Tnef_t;

static int
tnef_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	Tnef_t*			tnef;
	unsigned _ast_int4_t	magic;

	if (size < 6)
		return 0;
	magic = TNEF_MAGIC;
	if ((ap->swap = swapop(&magic, buf, 4)) < 0 || paxread(pax, ap, NiL, (off_t)6, (off_t)6, 0) != 6)
		return 0;
	if (!(tnef = newof(0, Tnef_t, 1, 0)))
		nospace();
	ap->data = (void*)tnef;
	return 1;
}

static int
tnef_getheader(Pax_t* pax, Archive_t* ap, register File_t* f)
{
	Tnef_t*			tnef = (Tnef_t*)ap->data;
	char*			s;
	char*			e;
	unsigned char*		x;
	size_t			n;
	size_t			m;
	size_t			z;
	unsigned int		level;
	unsigned int		type;
	unsigned int		name;
	unsigned int		checksum;
	size_t			size;
	size_t			part;
	Tm_t			tm;

	*(f->name = paxstash(pax, &ap->stash.head, NiL, PATH_MAX)) = 0;
	f->st->st_dev = 0;
	f->st->st_ino = 0;
	f->st->st_mode = X_IFREG|X_IRUSR|X_IWUSR|X_IRGRP|X_IROTH;
	f->st->st_uid = state.uid;
	f->st->st_gid = state.gid;
	f->st->st_nlink = 1;
	IDEVICE(f->st, 0);
	f->st->st_mtime = f->st->st_ctime = f->st->st_atime = NOW;
	f->st->st_size = 0;
	while (paxread(pax, ap, s = state.tmp.buffer, (off_t)0, (off_t)9, 0) > 0)
	{
		level = swapget(ap->swap, s + 0, 1);
		n = swapget(ap->swap, s + 1, 4);
		type = n >> 16;
		name = n & ((1<<16)-1);
		n = 0;
		size = swapget(ap->swap, s + 5, 4);
		part = ap->io->buffersize;
		if (size > part)
		{
			if (level == 2)
			{
				level = 0;
				if (name == 0x800f)
				{
					f->st->st_size = size;
					tnef->offset = paxseek(pax, ap, (off_t)0, SEEK_CUR, 0);
				}
				else
					error(1, "%s: %s format 0x%04x attribute ignored", ap->name, ap->format->name, name);
			}
			while (size > part)
			{
				size -= part;
				if (!(s = paxget(pax, ap, part, NiL)))
					error(3, "%s: %s format 0x%04x attribute data truncated -- %d expected", ap->name, ap->format->name, name, part);
				x = (unsigned char*)s;
				e = s + part;
				while (x < (unsigned char*)e)
					n += *x++;
			}
		}
		if (!(s = paxget(pax, ap, size + 2, NiL)))
			error(3, "%s: %s format 0x%04x attribute data truncated -- %d expected", ap->name, ap->format->name, name, size);
		checksum = swapget(ap->swap, s + size, 2);
		x = (unsigned char*)s;
		e = s + size;
		while (x < (unsigned char*)e)
			n += *x++;
		n &= ((1<<16)-1);
		if (checksum != n)
			error(3, "%s: %s format attribute data checksum error", ap->name, ap->format->name);
		if (level == 2)
			switch (name)
			{
			case 0x800f: /* data */
				f->st->st_size = size;
				tnef->offset = paxseek(pax, ap, (off_t)0, SEEK_CUR, 0) - size - 2;
				break;
			case 0x8010: /* title */
				if (!f->name)
					f->name = paxstash(pax, &ap->stash.head, s, size);
				break;
			case 0x8013: /* date */
				if (type == 0x0003 && size >= 12)
				{
					tm.tm_year = swapget(ap->swap, s +  0, 2) - 1900;
					tm.tm_mon  = swapget(ap->swap, s +  2, 2) - 1;
					tm.tm_mday = swapget(ap->swap, s +  4, 2);
					tm.tm_hour = swapget(ap->swap, s +  6, 2);
					tm.tm_min  = swapget(ap->swap, s +  8, 2);
					tm.tm_sec  = swapget(ap->swap, s + 10, 2);
					f->st->st_mtime = tmtime(&tm, TM_LOCALZONE);
				}
				break;
			case 0x9005: /* attachment */
				if (size >= 4)
				{
					e = s + size - 4;
					n = swapget(ap->swap, s, 4); s += 4;
					while (n-- && s < e)
					{
						type = swapget(ap->swap, s, 2); s += 2;
						name = swapget(ap->swap, s, 2); s += 2;
						switch (type)
						{
						case 0x0002:
						case 0x000b:
							s += 2;
							break;
						case 0x0003:
						case 0x0004:
						case 0x0007:
						case 0x000a:
						case 0x000d:
						case 0x0048:
							s += 4;
							break;
						case 0x0005:
						case 0x0006:
						case 0x0014:
						case 0x0040:
							s += 8;
							break;
						case 0x001e:
						case 0x001f:
						case 0x0102:
							m = swapget(ap->swap, s, 4); s += 4;
							while (m--)
							{
								z = swapget(ap->swap, s, 4); s += 4;
								z = roundof(z, 4);
								if (name == 0x3707)
									f->name = paxstash(pax, &ap->stash.head, s, z);
								s += z;
							}
							break;
						}
					}
				}
				n = tnef->offset;
				tnef->offset = paxseek(pax, ap, (off_t)0, SEEK_CUR, 0);
				if (paxseek(pax, ap, n, SEEK_SET, 1) != n)
					error(3, "%s: %s input must be seekable", ap->name, ap->format->name);
				return 1;
			}
	}
	if (ap->io->eof && ap->entry == 1)
		error(1, "%s: no members in %s file", ap->name, ap->format->name);
	return 0;
}

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

static int
tnef_gettrailer(Pax_t* pax, Archive_t* ap, File_t* f)
{
	register Tnef_t*	tnef = (Tnef_t*)ap->data;

	if (paxseek(pax, ap, tnef->offset, SEEK_SET, 1) != tnef->offset)
		error(3, "%s: %s format seek error", ap->name, ap->format->name);
	return 1;
}

Format_t	pax_tnef_format =
{
	"tnef",
	0,
	"MS outlook transport neutral encapsulation format",
	0,
	ARCHIVE|DOS|NOHARDLINKS|IN,
	DEFBUFFER,
	DEFBLOCKS,
	0,
	PAXNEXT(pax_tnef_next),
	0,
	tnef_done,
	tnef_getprologue,
	tnef_getheader,
	0,
	tnef_gettrailer,
};

PAXLIB(&pax_tnef_format)
