/*
** mhlistsbr.c -- routines to list information about the
**             -- contents of MIME messages
**
** This code is Copyright (c) 2002, by the authors of nmh.  See the
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
*/

#include <h/mh.h>
#include <fcntl.h>
#include <errno.h>
#include <h/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
#include <h/utils.h>
#include <sys/stat.h>
#include <sysexits.h>

/* mhmisc.c */
int part_ok(CT, int);
int type_ok(CT, int);
void flush_errors(void);

/*
** prototypes
*/
void list_all_messages(CT *, int, int);
int list_switch(CT, int, int, int);
int list_content(CT, int, int, int);

/*
** static prototypes
*/
static void list_single_message(CT, int, int);
static int list_debug(CT);
static int list_multi(CT, int, int, int);
static int list_partial(CT, int, int, int);
static int list_encoding(CT);


/*
** various formats for -list option
*/
#define LSTFMT1    "%4s %-5s %-24s %5s %-36s\n"
#define LSTFMT2a   "%4d "
#define LSTFMT2b   "%-5s %-24.24s "
#define LSTFMT2c1  "%5lu"
#define LSTFMT2c2  "%4lu%c"
#define LSTFMT2c3  "huge "
#define LSTFMT2c4  "     "
#define LSTFMT2d1  " %-36.36s"
#define LSTFMT2d2  "\t     %-65.65s\n"


/*
** Top level entry point to list group of messages
*/
void
list_all_messages(CT *cts, int verbose, int debug)
{
	CT ct, *ctp;

	printf(LSTFMT1, "msg", "part", "type/subtype", "size", "description");
	for (ctp = cts; *ctp; ctp++) {
		ct = *ctp;
		list_single_message(ct, verbose, debug);
	}
	flush_errors();
}


/*
** Entry point to list a single message
*/
static void
list_single_message(CT ct, int verbose, int debug)
{
	if (type_ok(ct, 1)) {
		umask(ct->c_umask);
		list_switch(ct, 1, verbose, debug);
		if (ct->c_fp) {
			fclose(ct->c_fp);
			ct->c_fp = NULL;
		}
		if (ct->c_ceclosefnx)
			(*ct->c_ceclosefnx) (ct);
	}
}


/*
** Primary switching routine to list information about a content
*/
int
list_switch(CT ct, int toplevel, int verbose, int debug)
{
	switch (ct->c_type) {
	case CT_MULTIPART:
		return list_multi(ct, toplevel, verbose, debug);
		break;

	case CT_MESSAGE:
		if (ct->c_subtype == MESSAGE_PARTIAL) {
			return list_partial(ct, toplevel, verbose, debug);
		} else {
			return list_content(ct, toplevel, verbose, debug);
		}
		break;

	case CT_TEXT:
	case CT_AUDIO:
	case CT_IMAGE:
	case CT_VIDEO:
	case CT_APPLICATION:
		return list_content(ct, toplevel, verbose, debug);
		break;

	default:
		/* list_debug (ct); */
		adios(EX_DATAERR, NULL, "unknown content type %d", ct->c_type);
		break;
	}

	return 0;  /* NOT REACHED */
}


#define empty(s) ((s) ? (s) : "")

/*
** Method for listing information about a simple/generic content
*/
int
list_content(CT ct, int toplevel, int verbose, int debug)
{
	unsigned long size;
	char *cp, buffer[BUFSIZ];
	CI ci = &ct->c_ctinfo;

	printf(toplevel > 0 ? LSTFMT2a : toplevel < 0 ? "part " : "     ",
			atoi(mhbasename(empty(ct->c_file))));
	snprintf(buffer, sizeof(buffer), "%s/%s", empty(ci->ci_type),
			empty(ci->ci_subtype));
	printf(LSTFMT2b, empty(ct->c_partno), buffer);

	if (ct->c_cesizefnx)
		size = (*ct->c_cesizefnx) (ct);
	else
		size = ct->c_end - ct->c_begin;

	/* find correct scale for size (Kilo/Mega/Giga/Tera) */
	for (cp = " KMGT"; size > 9999; size >>= 10)
		if (!*++cp)
			break;

	/* print size of this body part */
	switch (*cp) {
	case ' ':
		if (size > 0 || ct->c_encoding != CE_EXTERNAL)
			printf(LSTFMT2c1, size);
		else
			printf(LSTFMT2c4);
		break;

	default:
		printf(LSTFMT2c2, size, *cp);
		break;

	case '\0':
		printf(LSTFMT2c3);
	}

	/* print Content-Description */
	if (ct->c_descr) {
		char *dp;
		dp = trimcpy(cp = mh_xstrdup(ct->c_descr));
		mh_free0(&cp);
		printf(LSTFMT2d1, dp);
		mh_free0(&dp);
	}

	printf("\n");

	if (verbose) {
		char **ap, **ep;
		CI ci = &ct->c_ctinfo;

		for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
			printf("\t\t%s=\"%s\"\n", *ap, *ep);
		}

		/*
		** If verbose, print any RFC-822 comments in the
		** Content-Type line.
		*/
		if (ci->ci_comment) {
			char *dp;

			dp = trimcpy(cp = add(ci->ci_comment, NULL));
			free (cp);
			snprintf(buffer, sizeof(buffer), "(%s)", dp);
			mh_free0(&dp);
			printf(LSTFMT2d2, buffer);
		}
	}

	if (debug)
		list_debug(ct);

	return OK;
}


/*
** Print debugging information about a content
*/
static int
list_debug(CT ct)
{
	char **ap, **ep;
	CI ci = &ct->c_ctinfo;

	fflush(stdout);
	fprintf(stderr, "  partno \"%s\"\n", empty(ct->c_partno));

	/* print MIME-Version line */
	if (ct->c_vrsn)
		fprintf(stderr, "  %s:%s\n", VRSN_FIELD, ct->c_vrsn);

	/* print Content-Type line */
	if (ct->c_ctline)
		fprintf(stderr, "  %s:%s\n", TYPE_FIELD, ct->c_ctline);

	/* print parsed elements of content type */
	fprintf(stderr, "    type    \"%s\"\n", empty(ci->ci_type));
	fprintf(stderr, "    subtype \"%s\"\n", empty(ci->ci_subtype));
	fprintf(stderr, "    comment \"%s\"\n", empty(ci->ci_comment));
	fprintf(stderr, "    magic   \"%s\"\n", empty(ci->ci_magic));

	/* print parsed parameters attached to content type */
	fprintf(stderr, "    parameters\n");
	for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
		fprintf(stderr, "      %s=\"%s\"\n", *ap, *ep);

	/* print internal flags for type/subtype */
	fprintf(stderr, "    type 0x%x subtype 0x%x params 0x%x\n",
			 ct->c_type, ct->c_subtype,
			 (unsigned int)(unsigned long) ct->c_ctparams);

	fprintf(stderr, "    charset  \"%s\"\n", empty(ct->c_charset));
	fprintf(stderr, "    showproc  \"%s\"\n", empty(ct->c_showproc));
	fprintf(stderr, "    storeproc \"%s\"\n", empty(ct->c_storeproc));

	/* print transfer encoding information */
	if (ct->c_celine)
		fprintf(stderr, "  %s:%s", ENCODING_FIELD, ct->c_celine);

	/* print internal flags for transfer encoding */
	fprintf(stderr, "    transfer encoding 0x%x params 0x%x\n",
			ct->c_encoding,
			(unsigned int)(unsigned long) ct->c_cefile);

	/* print Content-ID */
	if (ct->c_id)
		fprintf(stderr, "  %s:%s", ID_FIELD, ct->c_id);

	/* print Content-Description */
	if (ct->c_descr)
		fprintf(stderr, "  %s:%s", DESCR_FIELD, ct->c_descr);

	fprintf(stderr, "    read fp 0x%x file \"%s\" begin %ld end %ld\n",
			(unsigned int)(unsigned long) ct->c_fp,
			empty(ct->c_file), ct->c_begin, ct->c_end);

	/* print more information about transfer encoding */
	list_encoding(ct);

	return OK;
}

#undef empty


/*
** list content information for type "multipart"
*/
static int
list_multi(CT ct, int toplevel, int verbose, int debug)
{
	struct multipart *m = (struct multipart *) ct->c_ctparams;
	struct part *part;

	/* list the content for toplevel of this multipart */
	list_content(ct, toplevel, verbose, debug);

	/* now list for all the subparts */
	for (part = m->mp_parts; part; part = part->mp_next) {
		CT p = part->mp_part;

		if (part_ok(p, 1) && type_ok(p, 1))
			list_switch(p, 0, verbose, debug);
	}

	return OK;
}


/*
** list content information for type "message/partial"
*/
static int
list_partial(CT ct, int toplevel, int verbose, int debug)
{
	struct partial *p = (struct partial *) ct->c_ctparams;

	list_content(ct, toplevel, verbose, debug);
	if (verbose) {
		printf("\t     [message %s, part %d",
				p->pm_partid, p->pm_partno);
		if (p->pm_maxno)
			printf(" of %d", p->pm_maxno);
		printf("]\n");
	}

	return OK;
}


/*
** list information about the Content-Transfer-Encoding
** used by a content.
*/
static int
list_encoding(CT ct)
{
	CE ce;

	if ((ce = ct->c_cefile))
		fprintf(stderr, "    decoded fp 0x%x file \"%s\"\n",
			(unsigned int)(unsigned long) ce->ce_fp,
			ce->ce_file ? ce->ce_file : "");

	return OK;
}
