/*
 *	UTIL.C  --  this module contains some general purpose
 *		    routines used with the IP/TCP/POP2 server.
 *
 *      (C) Copyright 1991 Regents of the University of California
 *
 *      Permission to use, copy, modify, and distribute this program
 *      for any purpose and without fee is hereby granted, provided
 *      that this copyright and permission notice appear on all copies
 *      and supporting documentation, the name of University of California
 *      not be used in advertising or publicity pertaining to distribution
 *      of the program without specific prior permission, and notice be
 *      given in supporting documentation that copying and distribution is
 *      by permission of the University of California.
 *      The University of California makes no representations about
 *      the suitability of this software for any purpose.  It is provided
 *      "as is" without express or implied warranty.
 *
 *	REVISIONS:
 *			12-87	[ks]	original implementation
 *		1.000	 5-88	[ks]
 *
 */

#include "globdefs.h"
#include "faildefs.h"

/************************ STRING HANDLING ************************************/

char *
fgetl(str,n,fp)
	/* This procedure reads a line from a text file. If  */
	/*    more than n-1 characters are read without a    */
	/*    line terminator (LF), characters are discarded */
	/*    until an LF is found.			     */
    char *str;					/* Ptr to char storage */
    int n;					/* Max# chars to load */
    FILE *fp;					/* FILE to load from */
{
    int r;					/* Temp integer storage */

    if (fgets(str,n,fp) == NULLCHAR) {		/* Get a line (with LF) */
	return(NULLCHAR);			/* Return if EOF or error */
    }
    r = strlen(str);				/* Count #chars this line */
    if (r == (n-1)) {				/* Got max # chars? */
	if (str[n-1] != LF_CHAR) {		/* Yes, got an LF too? */
	    str[n-1] = LF_CHAR;			/* No, put an LF at the end */
						/* Drop the rest of this line */
	    while (fgets(flash_buf,n,fp) != NULLCHAR) {
		r = strlen(str);		/* Get length of this segment */
						/* Got last of long line? */
		if ((r != (n-1)) || (str[n-1] == LF_CHAR)) {
		    break;			/* Yes, ready for next line */
		}
	    }
	}
    }
    return(str);				/* Return ptr to char storage */
}

strip_nl(str)
    char *str;					/* Ptr to str to strip */
	/* This routine strips a CR and/or LF from the end of a string. */
	/*   The string to strip is passed in as str. The string is     */
	/*   assumed to contain no more than one CR and/or no more than */
	/*   one LF. It is also assumed that any chars following a CR   */
	/*   or LF may be truncated.					*/
{
    char *pt;					/* Temp pointer */

    if (str == NULL) return;			/* Check for NULL string */
    if (pt = strchr(str,CR_CHAR)) *pt = NULL_CHAR;	/* Strip all after CR */
    if (pt = strchr(str,LF_CHAR)) *pt = NULL_CHAR;	/* Strip all after LF */
}

strn_nocase(str,cmp_str,cmp_len)
    char *str;					/* Ptr to test string */
    char *cmp_str;				/* Ptr to comparison string */
    int cmp_len;				/* # of chars to compare */
	/* This routine tests if two strings match (case independent) */
	/*   for a given length. The test string is passed in as str; */
	/*   the comparison string is passed in as cmp_str; the # of  */
	/*   chars to compare is passed as cmp_len. If the strings    */
	/*   match for the given length, the routine returns 0; if    */
	/*   the test string is lexically greater than the compare    */
	/*   string, the routine returns 1; otherwise, the routine    */
	/*   returns -1.					      */
{
    int i;					/* Temp counter */

						/* Handle NULL strings */
    if ((str == NULL) && (cmp_str == NULL) && (cmp_len == 0)) return(0);
    if (str     == NULL) return(-1);
    if (cmp_str == NULL) return( 1);

    for (i=0; i<cmp_len; ++i) {			/* Check each char for length */
						/* Loop when chars match */
	if (_toupper(str[i]) == _toupper(cmp_str[i])) continue;
						/* Return when no match */
	if (_toupper(str[i]) >  _toupper(cmp_str[i])) return(1);
	return(-1);
    }
    return(0);					/* Strs matched for length */
}

str_nocase(src_str,cmp_str)
    char *src_str,				/* Str to be matched */
	 *cmp_str;				/* Str to match against */
	/* This procedure attempts to match two strings byte for byte */
	/*   (case independent). The procedure returns 0 if the       */
	/*   strings match in contents and length.		      */
{
	/* Check for NULL strings */
    if (src_str == NULL) {		/* Is source str empty? */
					/* Yes, source str is empty */
        if (cmp_str == NULL) {		/* Is compare str empty too? */
	    return(0);			/* Yes, return, indicate match */
        } else {			/* No, compare str is non-empty */
	    return(1);			/* Return no match */
        }
    } else {				/* Source string is non-empty */
        if (cmp_str == NULL) {		/* Is compare str emtpy? */
	    return(1);			/* Yes, return, indicate no match */
        }
    }

	/* Loop until at the end if either string */
    while ( *src_str && *cmp_str ) {
					/* Chars match (case independent) ? */
	if (_tolower(*src_str) != _tolower(*cmp_str)) {
	    return(1);			/* No, return not matched */
	}
	++src_str;			/* Increment pointers */
	++cmp_str;
    }

	/* No-success unless at end of both strings */
    if (*src_str || *cmp_str) return(1);

	/* Strings match exactly (case independent) return success */
    return(0);
}

/*************************** ARRAY HANDLING *********************************/

isin_carray(str,str_list)
    char  *str;					/* Ptr to string to test */
    char **str_list;				/* Array of match strings */
	/* This routine tests if a test string starts with any of a */
	/*   list of comparison strings in a string array. If a     */
	/*   match in the array is found, the routine returns the   */
	/*   index of the match; otherwise, the routine returns -1. */
{
    int i;					/* Temp index counter */

    if (str == NULL) return(UNDEFINED);		/* Shouldnt happen */
    if (str_list == NULL) return(UNDEFINED);	/* Shouldnt happen either */

    for (i=0; (str_list[i] != NULL); ++i) {	/* Check each array string */
						/* Test matches array str? */
	if (!strn_nocase(str,str_list[i],strlen(str_list[i]))) {
	    return(i);				/* Yes, return this index */
	}
    }
						/* Entire str array checked */
    return(UNDEFINED);				/* Return, indicate no match */
}


/*********************** RCPT NAME PARSING ***********************************/

#define RFC_SCAN_SPECIALS	"(),.<>@"	/* Special chars in addrs */

char *rfc_token(token_str,token_delim)
    char **token_str;
    char  *token_delim;
	/* This routine parses out the next RFC822 address specific */
	/*   token from a string.				    */
{
    char *rest_pt;				/* Temp pointers */
    char *token_sav;

    rest_pt = *token_str;			/* Ease pointer use */

    token_sav = rest_pt;			/* Set up for next call */
    *rest_pt = *token_delim;			/* Restore delimiter */

    while (isspace(*rest_pt)) {			/* Skip over any LWSP */
	++rest_pt;
    }

    if (*rest_pt == NULL_CHAR) {		/* End of command line? */
	*token_str = token_sav;			/* Yes,(may be called again) */
	return(NULL);				/* Indicate end of command */
    }

    token_sav = rest_pt;			/* Save ptr start of token */

    if (*rest_pt == DQUOTE_CHAR) {		/* Parsing quoted literal? */
						/* Yes, search for end quote */
	while ((*rest_pt != DQUOTE_CHAR) && (*rest_pt != NULL_CHAR)
					&& (*rest_pt != LF_CHAR)) ++rest_pt;
	if (*rest_pt == DQUOTE_CHAR) ++rest_pt;
    } else {
						/* No, not quoted literal */
	    /* Scan until the end of the token is found. Loop until LWSP */
	    /*   is encountered, or until end of command line is found.	 */
	while ( (!isspace(*rest_pt)) && (*rest_pt != NULL_CHAR) ) {
						/* Special scan token found? */
	    if (index(RFC_SCAN_SPECIALS,*rest_pt) != NULL) {
		++rest_pt;			/* Yes, increment pointer */
		break;				/* Return 1 char token */
	    }
						/* No, not special scan */
	    ++rest_pt;				/* Increment pointer */

						/* Special scan token now? */
						/* Yes, it is a delimiter */
	    if (index(RFC_SCAN_SPECIALS,*rest_pt) != NULL) break;
	}
    }

	/* A token has been parsed */
    *token_delim = *rest_pt;			/* Save token delimiter */
    *rest_pt = NULL_CHAR;			/* NULL terminate token str */
    *token_str = rest_pt;

    return(token_sav);				/* Return ptr to token */
}


char *pars_rfcname(str)
    char *str;
	/* This routine parses an arbitrary RFC822 address string   */
	/*   and returns its best guess at the translated user@host */
{
    int i,ti;				/* Temp index counter */
    int ti_save,count;
    char delim_char;
    char *name_pt;			/* Ptr for command line parsing */
    char *pt;
    char *v_pt;				/* Ptr to parsed <name> */
    char *more_pt;
    char **temp_pt;			/* Ptr to interim token list */


    more_pt = str;			/* Setup for first call to rfc_token */
    delim_char = *str;

	/* Return NULL if header contains no names */
    name_pt = rfc_token(&more_pt,&delim_char);
    if (name_pt == NULL) return(NULL);

    v_pt = NULL;				/* Start with NULL list */
    while (name_pt != NULL) {			/* Any more tokens found? */
						/* Yes, another token found */
	    /* Attempt parse a full user address from the remaining string */
        get_c_array(temp_pt,8);			/* Get a small char array */
	ti = 0;					/* Index from array base */

	    /* Gather tokens until end of command line or until */
	    /*   address delimiter (comma) is found             */
	while ((name_pt != NULL) && (*name_pt != COMMA_CHAR)) {

						/* Discard < > delimiters */
	    if ((*name_pt == LANKLE_CHAR) || (*name_pt == RANKLE_CHAR)) {
		name_pt = rfc_token(&more_pt,&delim_char);
	 	continue;
	    }

	    if (*name_pt == LPAREN_CHAR) {	/* Eat comments */
		count = 0;			/* Allow nested comments */

		while (name_pt != NULL) {
		    if (*name_pt == LPAREN_CHAR) ++count;
		    if (*name_pt == RPAREN_CHAR) --count;
		    name_pt = rfc_token(&more_pt,&delim_char);
		    if (!count) break;
		}
	    }
	    if (name_pt == NULL) break;		/* Watch out for end of line */

      	    chk_c_size(temp_pt,8,ti);		/* Add token to small list */
	    temp_pt[ti] = malloc(strlen(name_pt) + 1);
	    if (temp_pt[ti] == NULL) fail(FAIL_OUT_OF_MEMORY);
	    strcpy(temp_pt[ti],name_pt);
	    ++ti;
						/* Gather next token */
	    name_pt = rfc_token(&more_pt,&delim_char);
 	}
	temp_pt[ti] = NULL;			/* NULL terminate small list */

	     /* Rebuild the address spec without all the comments and LWSP */
	flash_buf[0] = NULL_CHAR;
	for (ti=0; (temp_pt[ti] != NULL); ++ti) {
	    strcat(flash_buf,temp_pt[ti]);	/* Cat all good tokens */
	}
        free_carray(temp_pt);			/* Free up small temp list */

						/* Get next token */
	name_pt = rfc_token(&more_pt,&delim_char);

	if (flash_buf[0] == NULL_CHAR) {	/* Was a address parsed? */
	    continue;				/* No, try again */
	}
						/* Yes, save parsed name */
	v_pt = malloc(strlen(flash_buf) + 1);
	if (v_pt == NULL) fail(FAIL_OUT_OF_MEMORY);
	strcpy(v_pt,flash_buf);
	break;					/* Got one name, done here */
    }

    return(v_pt);				/* Return ptr to parsed addr */
}

free_carray(c_list)
    char **c_list;
	/* This procedure frees the alloced memory associated */
	/*   with an array of char arrays.                    */
{
    int i;					/* Temp counter */

    if (c_list == NULL) return;			/* Watch out for NULL array */

    for (i=0; c_list[i] != NULL; ++i) {		/* Free each element in array */
	free(c_list[i]);
    }
    free(c_list);				/* Free ptr array itself */
}
