/*
 * $Id: www.c,v 1.2 1996/01/26 16:36:39 daveho Exp $
 * library routines for cgi-bin programs
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <pwd.h>
#include <unistd.h>

#include "www.h"

/*
 * Private routines.
 */
static int readn(FILE *fp, char *buf, int n);
static int advance(const char **pos, const char *string);
static char *copyval(const char *val);

/***********************************************************************
 * 
 * public library routines
 * 
 ***********************************************************************/

/*
 * Print some text as html.
 */
void print_html(const char *fmt, ...)
{
    va_list args;

    va_start(args,fmt);
    printf("Content-type: text/html\n\n");
    vprintf(fmt, args);
    va_end(args);
}

/*
 * Return 1 if given password is the correct password for given
 * username and uid, 0 otherwise.
 */
int verify_password(const char *password, const char *username)
{
    struct passwd *pwd;
    char *ep;

    /*
     * Note: under FreeBSD, getpwnam will not return the encrypted
     * password for users other than root. (Due to NIS?)  This is fine.
     */
    pwd = getpwnam(username);
    if (!pwd) {
	return 0;
/*    
    if (!pwd || pwd->pw_uid != uid) {
	return 0;
*/
    } 
    ep = crypt(password, pwd->pw_passwd);
    if (strcmp(ep, pwd->pw_passwd) != 0) {
	return 0;
    }

    /* Successful match. */
    return 1;
}

/*
 * Extract args passed by the POST method, copying them into one
 * large string.
 */
char *get_post_args(void)
{
    char buf[1024];
    int total = 0, old, n;
    char *ret = NULL;

    do {
	n = readn(stdin, buf, 1024);
	if (n == 0) {
	    break;
	}
	old = total;
	total += n;
	ret = (char *)realloc(ret, total);
	memcpy(ret + old, buf, n+1);
    } while (n == 1023);

    return ret;
}

/*
 * Get value corresponding to given variable, or NULL if no such
 * variable exists.  Returned string is malloced, so caller is
 * responsible for freeing it.
 */
char *getval(const char *args, const char *var)
{
    const char *pos = NULL;
    int varlen;

    varlen = strlen(var);

    for (;;) {
	if (!advance(&pos, args))
	  return NULL;
	if (strncmp(var, pos, varlen) == 0) {
	    /* If this is a match, the character following
	     * matched text must be '='. */
	    if (*(pos + varlen) == '=') {
		/* It's a match, copy out the value. */
		return copyval(pos + varlen + 1);
	    } else {
		/* Not a match. */
		continue;
	    }
	}
    }
}

/*
 * Unescape HTML escape sequences of the form %XX.
 * Also turn literal '+' characters into spaces.
 */
char *html_unescapify(const char *str)
{
    char *ret, *ptr;
    unsigned ch;

    /*
     * This routine can only make the string shorter, so allocating
     * a string of the same length will work fine.
     */
    ret = (char *)malloc(strlen(str) + 1);
    if (ret) {
	ptr = ret;
	while (*str) {
	    if (*str == '%') {
		/*
		 * Just to be on the safe side, make sure that this
		 * is really an escape sequence.
		 */
		if (!*(str + 1) || !isxdigit(*(str + 1)) ||
		    !*(str + 2) || !isxdigit(*(str + 2))) {
		    /* This doesn't seem to be an escape, so copy as
		     * a literal. */
		    *ptr++ = *str++;
		} else {
		    /* Unpack. */
		    char buf[3];
		    buf[0] = *(str + 1);
		    buf[1] = *(str + 2);
		    buf[2] = '\0';
		    sscanf(buf, "%x", &ch);
		    *ptr++ = ch;
		    str += 3;
		}
	    } else if (*str == '+') {
		*ptr++ = ' ';
		str++;
	    } else {
		*ptr++ = *str++;
	    }
	}
	*ptr = '\0';
    }
    return ret;
}

/*
 * Verify that the real uid of this process is that of given username.
 */
int verify_real_uid(const char *username)
{
    struct passwd *pwd;

    pwd = getpwnam(username);
    if (!pwd) {
	return 0;
    }
    return pwd->pw_uid == getuid();
}

/***********************************************************************
 * 
 * private library routines
 * 
 ***********************************************************************/

/*
 * Read up to n-1 bytes for given file, storing read bytes in buf
 * and '\0' terminating it. Returns number of chars read.
 */
static int readn(FILE *fp, char *buf, int n)
{
    int nread = 0, ch;

    n--;
    while (nread < n) {
	ch = getc(fp);
	if (ch == EOF)
	  break;
	else {
	    *buf++ = ch;
	    nread++;
	}
    }

    *buf = 0;
    return nread;
}

/*
 * Advance pointer pointed to by pos to the next var=val pair
 * in given argument string.
 * Return 1 if found, 0 if not.
 */
static int advance(const char **pos, const char *string)
{
    if (!*pos) {
	*pos = string;
	if (!**pos)
	  /* Empty string. */
	  return 0;
	return 1;
    }

    while (**pos && **pos != '&')
      (*pos)++;
    if (!**pos)
      return 0;
    else {
	(*pos)++;
	return **pos != '\0';
    }
}

/*
 * Copy the value pointed to by val and delimited by a '&' character.
 * Unescapify any HTML escape sequences.
 */
static char *copyval(const char *val)
{
    const char *end = val;
    int len;
    char *tmp, *ret = NULL;

    while (*end && *end != '&')
      end++;

    len = end - val;
    tmp = (char *)malloc(len + 1);
    if (tmp) {
	memcpy(tmp, val, len);
	tmp[len] = '\0';
	ret = html_unescapify(tmp);
	free(tmp);
    }

    return ret;
}

