#include "util.h"               /* from ../utildir */
#include "conf.h"               /* from ../mmdf/h */
#include "ch.h"
#include "ap.h"
#include "dm.h"
#include "ll_log.h"

/*  Format one address from pointers to constitutents, in a tree
 *
 *  Returns:    pointer to string if successful or
 *              NOTOK if error
 *
 *  SEK - using ap_p2s to output for ap_t2s has the general problem
 *              of losing comments.   Perhaps ap_t2s should be
 *              separate?
 */

extern LLog *logptr;
extern int ap_outtype;
extern char *multcat();
extern char *strdup();
extern char *ap_dmflip();
extern Domain *dm_v2route();

LOCFUN val2str();

char *
ap_p2s (group, name, local, domain, route)
    AP_ptr  group,             /* beginning of group name  */
	    name,              /* beginning of person name  */
	    local,             /* beginning of local-part */
	    domain,            /* basic domain reference */
	    route;             /* beginning of 733 forward routing */
{
    Dmn_route   dmnroute;
    AP_ptr      lastptr;
    char        *routp;         /* 822 -> 733 route string */
    int         inperson,
		ingroup;
    register char *strp;        /* The string we are building */
    register char *cp;
    register AP_ptr curptr;
    char *flipptr;
    char *stripptr;
    char tmpbuf [LINESIZE];
    char *tmpdomain [DM_NFIELD];
    int tmpcnt;
    char buf[LINESIZE];         /* buf for dm_v2route                 */
    char *drefptr;              /* pointer to string to be output     */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ap_p2s()");

    if ((ap_outtype & AP_822) == AP_822)   /* AP_733 is implicit default */
	ll_log (logptr, LLOGFTR, "AP_822 on");
    ll_log (logptr, LLOGFTR, (ap_outtype & AP_BIG)
	? "AP_BIG on" : "AP_LITTLE on");
    if ((ap_outtype & AP_NODOTS) == AP_NODOTS)  /* AP_DOTS is implicit def. */
	ll_log (logptr, LLOGFTR, "AP_NODOTS on");
#endif

    inperson = ingroup = FALSE;
    strp = strdup("");
    routp = strdup("");

    if (group != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  group is '%s'", group -> ap_obvalue);
#endif
	for (curptr = group; curptr != (AP_ptr)0; curptr = curptr -> ap_chain)
	{                             /* print munged addr                  */
	    switch (curptr -> ap_obtype) {
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* Output value as comment */
		    if (name != (AP_ptr) 0) {
				    /* only output comments for pretty forms */
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_NGRP:
		    ingroup = TRUE;
		case APV_GRUP:
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_GRUP);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
	if (ingroup) {
	    cp = multcat(strp, ": ", (char *)0);
	    free (strp);
	    if(cp == (char *)0)
		return( (char *)NOTOK);
	    strp = cp;
	}
    }

    if (name != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  name is '%s'", name -> ap_obvalue);
#endif
	for (curptr = name; curptr != (AP_ptr)0; curptr = curptr -> ap_chain)
	{                         /* print munged addr                  */
	    switch (curptr -> ap_obtype) {
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* Output value as comment */
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
		    cp = multcat(strp, (strp[0]?" " : ""), "(", tmpbuf, ")", (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;

		case APV_NPER:
		    inperson = TRUE;
		case APV_PRSN:
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_PRSN);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
    }

    if (inperson) {
	cp = multcat(strp, " <", (char *)0);
	free (strp);
	if(cp == (char *)0)
	    return( (char *)NOTOK);
	strp = cp;
    }

    if (route != (AP_ptr) 0)      /* we have routing info */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  route is '%s'", route -> ap_obvalue);
#endif
	for (lastptr = curptr = route; ; curptr = curptr -> ap_chain) {
	    if (curptr == (AP_ptr)0)    /* Grot Grot !!!!!!! */
		goto defcase1;
	    switch (curptr -> ap_obtype) {
		case APV_EPER:
		continue;

		defcase1:;	/* YEUCH !! */
		default:
		case APV_NIL:
		    if ((ap_outtype & AP_822) == AP_822) {
					/* piece of cake */
			cp = multcat(strp, ":", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    break;

		case APV_CMNT:        /* Output value as comment */
		    if (name != (AP_ptr) 0) {
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_DLIT:
		case APV_DOMN:
		    val2str (tmpbuf, curptr -> ap_obvalue, curptr -> ap_obtype);
		    flipptr = stripptr = (char *) 0;
		    drefptr = tmpbuf;
		    if (((ap_outtype & AP_BIG) == AP_BIG) ||
		       (((ap_outtype & AP_NODOTS) == AP_NODOTS) &&
				(curptr == lastptr)) ) {
		        /* check domain ref in either case */
			Domain  *lrval = dm_v2route (tmpbuf, buf, &dmnroute);

			if(lrval == (Domain *)MAYBE)
			    return( (char *)MAYBE);

			if (lrval != (Domain *) NOTOK) {
			    if ((ap_outtype & AP_NODOTS) == AP_NODOTS
				&& curptr == lastptr) {
				/* only strip domain on next-hop in route */
				tmpcnt = cstr2arg (dmnroute.dm_argv[0],
					DM_NFIELD, tmpdomain, '.');
				stripptr = strdup(tmpdomain[0]);
				drefptr = stripptr;
			    } 
			    else 
				if ((ap_outtype & AP_BIG) == AP_BIG) {
				    flipptr = ap_dmflip (buf);
				    drefptr = flipptr;
				}
			}
		    }

		    if ((ap_outtype & AP_822) == AP_822) {
					/* piece of cake */
		        cp = multcat(strp,(curptr!=lastptr?",@":"@"),drefptr,(char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    } else {
			if(routp[0] == '\0')
			    cp = multcat("@", drefptr, (char *)0);
			else
			    cp = multcat("%", drefptr, routp, (char *)0);
			free (routp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			routp = cp;
		    }
		    if (flipptr != (char *)0)
			free (flipptr);
		    if (stripptr != (char *)0)
			free (stripptr);
		    continue;
	    }
	    break;
	}
    }

    if (local != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  local is '%s'", local -> ap_obvalue);
#endif
	for (curptr = local; curptr != (AP_ptr)0; curptr = curptr -> ap_chain) {
	    switch (curptr -> ap_obtype) {		/* print munged addr */
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* SEK - don't skip these */
		    if (name != (AP_ptr) 0) {
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_WORD:
		case APV_MBOX:
					/* SEK - YUK                    */
		    if (strindex (":Include:", curptr -> ap_obvalue) == 0)
			(void) strcpy (tmpbuf, curptr -> ap_obvalue);
		    else
			val2str (tmpbuf, curptr -> ap_obvalue, APV_MBOX);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
    }

    if (domain != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  domain is '%s'",
		domain -> ap_obvalue);
#endif
	val2str (tmpbuf, domain -> ap_obvalue, domain -> ap_obtype);
	flipptr = stripptr = (char *) 0;
	drefptr = tmpbuf;
	if (((ap_outtype & AP_BIG) == AP_BIG) ||
	   (((ap_outtype & AP_NODOTS) == AP_NODOTS)) && route == (AP_ptr) 0) {
	    /* check domain ref in either case */
	    Domain  *lrval = dm_v2route (tmpbuf, buf, &dmnroute);

	    if(lrval == (Domain *)MAYBE)
		return( (char *)MAYBE);

	    if (lrval != (Domain *) NOTOK) {
		if (((ap_outtype & AP_NODOTS) == AP_NODOTS) &&
			(route == (AP_ptr) 0)) {
		    /* If there is no route, this domain is next hop: strip */
		    tmpcnt = cstr2arg (dmnroute.dm_argv[0], DM_NFIELD, 
					tmpdomain, '.');
		    stripptr = strdup(tmpdomain[0]);
		    drefptr = stripptr;
		}
		else
		    if ((ap_outtype & AP_BIG) == AP_BIG) {
			flipptr = ap_dmflip (buf);
			drefptr = flipptr;
		    }
	    }
	}

	if ((ap_outtype & AP_822) == AP_822 || routp[0] == '\0')  /* easy */
	    cp = multcat (strp, "@", drefptr, routp, (char *)0);
	else
	    cp = multcat (strp, "%", drefptr, routp, (char *)0);

	if(cp == (char *)0)
		return(cp);
	free (strp);
	strp = cp;
	if (flipptr != (char *) 0)
	    free (flipptr);
	if (stripptr != (char *) 0)
	    free (stripptr);
    }
    free (routp);

    if (inperson) {
	cp = multcat(strp, ">", (char *)0);
	free (strp);
	if(cp == (char *)0)
	    return( (char *)NOTOK);
	strp = cp;
    }
    if (ingroup) {
	cp = multcat(strp, ";", (char *)0);
	free (strp);
	if (cp == (char *)0)
    	    return( (char *)NOTOK);
	strp = cp;
    }
    return (strp);
}


/*
 *  This function is just barely usable.  The hole problem of
 *  quoted strings is hard to get right especially when the
 *  mail system is trying to make up for human forgetfulness.
 *                              -DPK-
 *  SEK - have improved this somewhat by giving knowledge of
 *  the various object types.  Does not handle strings of spaces.
 */
LOCFUN
	val2str (buf, value, obtype)      /* convert to canonical string */
    char *buf,
	 *value,
	 obtype;
{
    int gotspcl;
    int inquote;
    register char *fromptr,
		  *toptr;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "val2str ('%s', %d)", value, obtype);
#endif


    if (obtype == APV_CMNT) {
	for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++)
	    switch (*fromptr) {
		case '\r':
		case '\n':
		case '\\':
		case '(':
		case ')':
		    *toptr++ = '\\';
	    }
	*toptr = '\0';
	return;
    }

    if (obtype == APV_DLIT) {
	for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++)
	    switch (*fromptr) {
		case '\r':
		case '\n':
		case '\\':
		    *toptr++ = '\\';
		    continue;
		case '[':
		    if (fromptr != value)
		       *toptr++ = '\\';
		    continue;
		case ']':
		    if (*(fromptr + 1) != (char) 0)
			*toptr++ = '\\';
	    }
	*toptr = '\0';
	return;
    }

    inquote = FALSE;
    for (gotspcl = FALSE, fromptr = value; *fromptr != '\0'; fromptr++) {
	switch (*fromptr) {
	    case '"':
		inquote = (inquote == TRUE ? FALSE : TRUE);     /* Flip-Flop */
		break;

	    case '\\':
	    case '\r':
	    case '\n':
		if (inquote == FALSE) {
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;

	    case '<':
	    case '>':
	    case '@':
	    case ',':
	    case ';':
	    case ':':
	    case '\t':
	    case '[':
	    case ']':
	    case '(':
	    case ')':
		if( inquote == FALSE) {
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;

	    case ' ':
		if (inquote == FALSE)
		    if ((obtype == APV_DOMN) || (obtype == APV_MBOX)) {
			gotspcl = TRUE;
			goto copyit;
		    } else {
				/* SEK hack to handle " at "              */
				/* yes - this really is needed          */
			if ((uptolow(*(fromptr + 1)) == 'a') &&
			    (uptolow(*(fromptr + 2)) == 't') &&
			    (*(fromptr + 3) == ' ')) {
			    gotspcl = TRUE;
			    goto copyit;
			}
		    }
		break;

	    case '.':
		if (inquote == FALSE && ((obtype == APV_GRUP) || (obtype == APV_PRSN)))
		{
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;
	}
    }

    gotspcl = inquote;          /* If were in a quote, something's wrong */
copyit:
    toptr = buf;
    if (gotspcl)
	*toptr++ = '"';
    for (fromptr = value; *fromptr != '\0'; *toptr++ = *fromptr++)
	switch (*fromptr) {
	    case '\r':
	    case '\n':
	    case '\\':
	    case '"':
		if (gotspcl)
		    *toptr++ = '\\';
	}
    if (gotspcl)
	*toptr++ = '"';
    *toptr = '\0';
}
