/* netrc.c and netrc.h are for processing the ~/.netrc file
   This has nothing to do with cryptography.
   Copyright (C) 1998 Egil Kvaleberg

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "mostincludes.h"
#include "netrc.h"
#include "diffie/compat.h"
#include "mad.h"

#define DEBUG 0

static void name_netrc (char *line, int len);
static char *make_token (char *p, char **next);
static int lookup_token (char *p);

#define netrc_isspace(x) ((x) == ' ' || (x) == '\n' || (x) == '\t')

extern int verbose;

char *lookup_netrc (char *host, char *user)
{
    FILE *f;
    char *p;
    char *next;
    char *val = NULL;
    int n;
    int state = ST_IDLE;
    int state_save = ST_IDLE;
    char line[MAXLEN];
    char machine_name[MAXLEN];
    char login_name[MAXLEN];

    if (!host || !user)
	return NULL;

    name_netrc (line, sizeof (line));
    if (!(f = fopen (line + strspn (line, PATH_SEP_STR) - 1, "r"))) {
/* since --netrc is now default, we don't want this to print an error */
	if (verbose)
	    progmess_strerror (".netrc", line);
	return NULL;
    }
    machine_name[0] =
	login_name[0] = '\0';

    while (fgets (line, sizeof (line), f)) {

	/* strip trailing newline and blanks */
	if ((n = strlen (line)) > 0) {
	    p = line + n - 1;
	    while (*p && netrc_isspace (*p))
		*p-- = '\0';
	}
	p = line;
	if (!*p) {
	    /* blank line */
	    if (state == ST_MACDEF)
		state = ST_IDLE;
	}
	while (!val && (p = make_token (p, &next))) {
	    switch (state) {
	    default:
	    case ST_IDLE:
		state_save = state;
		state = lookup_token (p);
		/* immediate action */
		switch (state) {
		case ST_DEFAULT:
		    strcpy (machine_name, "*");
		    login_name[0] = '\0';
#if DEBUG
		    printf ("default\n", p);
#endif
		    state = ST_IDLE;
		    break;
		}
		break;
	    case ST_COMMENT:
		/* skip untill newline */
		break;
	    case ST_ACCOUNT:
		/* ignore */
		state = ST_IDLE;
		break;
	    case ST_LOGIN:
		/* user name on remote machine */
		strcpy (login_name, p);
#if DEBUG
		printf ("login %s\n", p);
#endif
		state = ST_IDLE;
		break;
	    case ST_MACDEF:
		/* skip untill blank line */
		break;
	    case ST_MACHINE:
		strcpy (machine_name, p);
		login_name[0] = '\0';
#if DEBUG
		printf ("machine %s\n", p);
#endif
		state = ST_IDLE;
		break;
	    case ST_PASSWORD:
#if DEBUG
		printf ("password %s\n", p);
#endif
		if ((strcmp (machine_name, host) == 0
		     || strcmp (machine_name, "*") == 0)
		    && strcmp (login_name, user) == 0) {
		    /* bingo: this is us */
#if DEBUG
		    printf ("password OK\n", p);
#endif
		    val = (char *) strdup (p);
		}
		state = ST_IDLE;
		break;
	    }
	    p = next;
	}
	if (state == ST_COMMENT)
	    state = state_save;
    }
    /* clear any trace */
    memset (line, 0, sizeof (line));
    fclose (f);
    return val;
}

struct passwd *cached_getpwuid (pid_t pid);

#if 0	/* huh? can't use the uid for the home directory -- what about $HOME ? */
static void name_netrc (char *line, int len)
{
    struct passwd *pw;
    int idx;

    idx = getuid ();
    pw = cached_getpwuid (idx);
    line[len - 1] = '\0';
    strncpy (line, pw->pw_dir, len - 1);
    strncat (line, NETRC, len - 1);
}
#else
extern char *home_dir;

static void name_netrc (char *line, int len)
{
    memset (line, 0, len);
    strncpy (line, home_dir, len - 2 - sizeof (NETRC));
    strcat (line, PATH_SEP_STR);
    strcat (line, NETRC);
}
#endif

static char *make_token (char *p, char **next)
{
    char *q;

    if (!p) {
	*next = '\0';
	return NULL;
    }
    /* skip leading blanks */
    while (netrc_isspace (*p))
	++p;

    if (!*p) {
	*next = '\0';
	return NULL;
    }
    /* skip to next token */
    for (q = p; *q && !netrc_isspace (*q); ++q);

    if (*q) {
	/* terminate */
	*q++ = '\0';
	*next = q;
    } else {
	*next = NULL;
    }
    return p;
}

static int lookup_token (char *p)
{
    switch (*p) {
    case '#':
	return ST_COMMENT;
	break;
    case 'a':
	if (strcmp (p, "account") == 0)
	    return ST_ACCOUNT;
	break;
    case 'd':
	if (strcmp (p, "default") == 0)
	    return ST_DEFAULT;
	break;
    case 'l':
	if (strcmp (p, "login") == 0)
	    return ST_LOGIN;
	break;
    case 'm':
	if (strcmp (p, "machine") == 0)
	    return ST_MACHINE;
	else if (strcmp (p, "macdef") == 0)
	    return ST_MACDEF;
	break;
    case 'p':
	if (strcmp (p, "password") == 0)
	    return ST_PASSWORD;
	break;
    }
    /* silently ignore errors */
    return ST_IDLE;
}
