/* ftprc.c */

/*  $RCSfile: ftprc.c,v $
 *  $Revision: 1.4 $
 *  $Date: 1994/06/01 22:20:14 $
 */

#include "sys.h"

#include <sys/stat.h>

#include <ctype.h>

#include "util.h"
#include "ftprc.h"
#include "main.h"
#include "cmds.h"
#include "set.h"
#include "defaults.h"
#include "copyright.h"

/* ftprc.c global variables */
siteptr					firstsite = NULL, lastsite = NULL;
recentsite				recents[dMAXRECENTS];
int						nRecents = 0;
int						nSites = 0;
int						keep_recent = dRECENT_ON;
longstring				rcname;
longstring				recent_file;
int						parsing_rc = 0;

extern char				*line, *margv[];
extern int				margc, fromatty;
extern string			anon_password;		/* most likely your email address */
extern struct userinfo	uinfo;

int thrash_rc(void)
{
	struct stat			st;
	string				word, str;
	longstring			cwd;
	char				*cp, *dp, *rc;
	FILE				*fp;
	int					i;

	(void) get_cwd(cwd, sizeof(cwd));
	if (cwd[strlen(cwd) - 1] != '/')
		(void) Strncat(cwd, "/");

	/* Because some versions of regular ftp complain about ncftp's
	 * #set commands, FTPRC takes precedence over NETRC.
	 */
	cp = getenv("DOTDIR");
	for (i=0; i<2; i++) {
		rc = (i == 0) ? FTPRC : NETRC;

		(void) sprintf(rcname, "%s%s", cwd, rc);
		if (stat(rcname, &st) == 0)
			goto foundrc;
		
		(void) sprintf(rcname, "%s.%s", cwd, rc);
		if (stat(rcname, &st) == 0)
			goto foundrc;

		if (cp != NULL) {
			(void) sprintf(rcname, "%s/.%s", cp, rc);
			if (stat(rcname, &st) == 0)
				goto foundrc;
		}

		(void) sprintf(rcname, "%s/.%s", uinfo.homedir, rc);
		if (stat(rcname, &st) == 0)
			goto foundrc;
	}

	return (0);	/* it's OK not to have an rc. */
	
foundrc:	
	if ((st.st_mode & 077) != 0)				/* rc must be unreadable by others. */
		(void) chmod(rcname, 0600);

	if ((fp = fopen(rcname, "r")) == NULL) {
		PERROR("thrash_rc", rcname);
		return -1;
	}
	
	parsing_rc = 1;
	while ((cp = FGets(str, fp)) != 0) {
		while (isspace(*cp)) ++cp;		/* skip leading space. */
		if (*cp == '#') {
			if ((strncmp("set", ++cp, (size_t)3) == 0) || (strncmp("unset", cp, (size_t)5) == 0)) {
				(void) strcpy(line, cp);
				makeargv();
				(void) set(margc, margv);			
				/* setting or unsetting a variable. */
			} /* else a comment. */
		} else {
			if (strncmp(cp, "machine", (size_t) 7) == 0) {
				/* We have a new machine record. */
				cp += 7;
				while (isspace(*cp)) ++cp;	/* skip delimiting space. */
				dp = word;
				while (*cp && !isspace(*cp)) *dp++ = *cp++;	/* copy the name. */
				*dp = 0;
				AddNewSitePtr(word);
			}
		}
	}
	(void) fclose(fp);
	parsing_rc = 0;
	return 1;
}	/* thrash_rc */




void AddNewSitePtr(char *word)
{
	siteptr			s;

	if ((s = (siteptr) malloc(sizeof(site))) != 0) {
		s->next = NULL;
		if ((s->name = malloc(strlen(word) + 1)) != 0) {
			(void) strcpy(s->name, word);
			if (firstsite == NULL)
				firstsite = lastsite = s;
			else {
				lastsite->next = s;
				lastsite = s;
			}
			++nSites;
		} else {
			Free(s);
		}
	}
}	/* AddNewSitePtr */




static int RecentCmp(recentsite *a, recentsite *b)
{
	int i = 1;
	
	if (a->lastcall > b->lastcall)
		i = -1;
	else if (a->lastcall == b->lastcall)
		i = 0;
	return i;
}	/* RecentCmp */




static siteptr FindNetrcSite(char *host, int exact)
{
	register siteptr s, s2;
	string str, host2;

	(void) Strncpy(host2, host);
	StrLCase(host2);
	
	/* see if 'host' is in our list of favorite sites (in NETRC). */
	for (s = firstsite; s != NULL; s2=s->next, s=s2) {
		(void) Strncpy(str, s->name);
		StrLCase(str);
		if (exact) {
			if (strcmp(str, host2) == 0)
				return s;
		} else {
			if (strstr(str, host2) != NULL) 
				return s;
		}
	}
	return NULL;
}	/* FindNetrcSite */




static recentsite *FindRecentSite(char *host, int exact)
{
	register recentsite		*r;
	register int			i;
	string					str, host2;

	(void) Strncpy(host2, host);
	StrLCase(host2);

	/* see if 'host' is in our list of favorite sites (in recent-log). */
	for (i=0; i<nRecents; i++) {
		r = &recents[i];
		(void) Strncpy(str, r->name);
		StrLCase(str);
		if (exact) {
			if (strcmp(str, host2) == 0)
				return r;
		} else {
			if (strstr(str, host2) != NULL)
				return r;
		}
	}
	return NULL;
}	/* FindRecentSite */




void ReadRecentSitesFile(void)
{
	FILE *rfp;
	recentsite *r;
	char name[64];
	int offset;
	longstring str;

	nRecents = 0;
	if (recent_file[0] != 0 && keep_recent) {
		rfp = fopen(recent_file, "r");
		if (rfp != NULL) {
			for (; nRecents < dMAXRECENTS; ) {
				r = &recents[nRecents];
				if (FGets(str, rfp) == NULL)
					break;
				(void) RemoveTrailingNewline(str, NULL);
				name[0] = 0;
				offset = 45;
				if (sscanf(str, "%s %lu %n",
					name,
					(unsigned long *) &r->lastcall,
					&offset) >= 2)
				{
					if ((r->name = NewString(name)) != NULL) {
						r->dir = NewString(str + offset);
						if (r->dir != NULL)
							nRecents++;
						else {
							free(r->name);
							r->name = r->dir = NULL;
						}
					}
				}
			}
			(void) fclose(rfp);
		}
	}
}	/* ReadRecentSitesFile */



static void SortRecentList(void)
{
	QSort(recents, nRecents, sizeof(recentsite), RecentCmp);
}	/* SortRecentList */




void WriteRecentSitesFile(void)
{
	FILE			*rfp;
	recentsite		*r;
	int				i;

	if ((recent_file[0] != 0) && (nRecents > 0) && (keep_recent)) {
		rfp = fopen(recent_file, "w");
		SortRecentList();
		if (rfp != NULL) {
			for (i=0; i<nRecents; i++) {
				r = &recents[i];
				(void) fprintf(rfp, "%-32s %11lu %s\n", r->name,
					(unsigned long) r->lastcall, r->dir);
			}
			(void) fclose(rfp);
			(void) chmod(recent_file, 0600);
		}
	}
}	/* WriteRecentSitesFile */




void AddRecentSite(char *host, char *lastdir)
{
	char			*nhost, *ndir;
	recentsite		*r;
	
	if (keep_recent) {
		nhost = NewString(host);
		/* Use '/' to denote that the current directory wasn't known,
		 * because we won't try to cd to the root directory.
		 */
		ndir = NewString(*lastdir ? lastdir : "/");
		
		/* Don't bother if we don't have the memory, or if it is already
		 * in our NETRC.
		 */
		if ((ndir != NULL) && (nhost != NULL) &&
			(FindNetrcSite(host, 1) == NULL)) {
			if (nRecents == dMAXRECENTS) {
				SortRecentList();
				r = &recents[dMAXRECENTS - 1];
				if (r->name != NULL)
					free(r->name);
				if (r->dir != NULL)
					free(r->dir);
			} else {
				r = &recents[nRecents];
				nRecents++;
			}
			r->name = nhost;			
			r->dir = ndir;
			(void) time(&r->lastcall);
			SortRecentList();
		}
	}
}	/* AddRecentSite */




/*
 * After you are done with a site (by closing it or quitting), we
 * need to update the list of recent sites called.
 */
void UpdateRecentSitesList(char *host, char *lastdir)
{
	recentsite *r;
	char *ndir;

	if (keep_recent) {	
		r = FindRecentSite(host, 1);
		if (r == NULL)
			AddRecentSite(host, lastdir);
		else {
			/* Update the last time connected, and the directory we left in. */
			if ((ndir = NewString(*lastdir ? lastdir : "/")) != NULL) {
				free(r->dir);
				r->dir = ndir;
			}
			(void) time(&r->lastcall);
		}
	}
}	/* UpdateRecentSitesList */



/*
 * Prints out the number of sites we know about, so the user can figure out
 * an abbreviation or type it's number to open (setpeer).
 */
void PrintSiteList(void)
{
	int						i, j;
	siteptr					s, s2;

	if (fromatty) {
		j = 0;
		i = 1;
		if (nRecents > 0) {
			j++;
			(void) printf("Recently called sites:\n");
			for (; i<=nRecents; i++) {
				(void) printf("%4d. %-32s", i, recents[i-1].name);
				i++;
				if (i <= nRecents) {
					(void) printf("%5d. %-32s", i, recents[i-1].name);
				} else {
					(void) printf("\n");
					break;
				}
				(void) printf("\n");
			}
		}
		if (nSites > 0) {
			j++;
			(void) printf("Sites in your netrc (%s):\n", rcname);
			for (s = firstsite; s != NULL; s2=s->next, s=s2, ++i) {
				(void) printf("%4d. %-32s", i, s->name);
				s2=s->next;
				s=s2;
				i++;
				if (s != NULL) {
					(void) printf("%5d. %-32s", i, s->name);
				} else {
					(void) printf("\n");
					break;
				}
				(void) printf("\n");
			}
		}
		if (j > 0) {
			(void) printf("\
Note that you can specify an abbreviation of any name, or #x, where x is the\n\
number of the site you want to connect to.\n\n");
		}
	}
}	/* PrintRecentSiteList */




/*
 * Given a sitename, check to see if the name was really an abbreviation
 * of a site in the NETRC, or a site in our list of recently connected
 * sites.  Also check if the name was in the format #x, where x which
 * would mean to use recents[x].name as the site; if x was greater than
 * the number of sites in the recent list, then index into the netrc
 * site list.
 */
#include <netdb.h>
void GetFullSiteName(char *host, char *lastdir)
{
	register siteptr		s, s2;
	register recentsite		*r;
	char					*ndir, *nhost, *cp;
	int						x, i, isAllDigits, exact;
	struct hostent			*hostentp;

	ndir = nhost = NULL;
	x = exact = 0;

	/* First, see if the "abbreviation" is really just the name of
	 * a machine in the local domain, or if it was a full hostname
	 * already.  That way we can avoid problems associated with
	 * short names, such as having "ce" as a machine in the local
	 * domain, but having "faces.unl.edu", where we would most likely
	 * want to use ce instead of faces if the user said "open ce".
	 * This will also prevent problems when you have a host named
	 * xx.yy.unl.edu, and another host named yy.unl.edu.  If the user
	 * said "open yy.unl.edu" we should use yy.unl.edu, and not look
	 * for matches containing yy.unl.edu.
	 */
	if ((hostentp = gethostbyname(host)) != NULL) {
		strcpy(host, hostentp->h_name);
		exact = 1;
	}

	/* Don't allow just numbers as abbreviations;  "open 2" could be
	 * confused between site numbers in the open 'menu,' like
	 * "2. unlinfo.unl.edu" and IP numbers "128.93.2.1" or even numbers
	 * in the site name like "simtel20.army.mil."
	 */
	
	for (isAllDigits = 1, cp = host; *cp != 0; cp++) {
		if (!isdigit(*cp)) {
			isAllDigits = 0;
			break;
		}
	}

	if (!isAllDigits) {
		if (host[0] == '#')
			(void) sscanf(host + 1, "%d", &x);
		/* Try matching the abbreviation, since it isn't just a number. */
		/* see if 'host' is in our list of favorite sites (in NETRC). */

		if (x == 0) {
			if ((s = FindNetrcSite(host, exact)) != NULL) {
				nhost = s->name;
			} else if ((r = FindRecentSite(host, exact)) != NULL) {
				nhost = r->name;
				ndir = r->dir;
			}
		}
	} else if (sscanf(host, "%d", &x) != 1) {
		x = 0;
	}

	if (--x >= 0) {
		if (x < nRecents) {
			nhost = recents[x].name;
			ndir = recents[x].dir;
		} else {
			x -= nRecents;
			if (x < nSites) {
				for (i = 0, s = firstsite; i < x; s2=s->next, s=s2)
					++i;				
				nhost = s->name;
			}
		}
	}

	if (nhost != NULL) {
		(void) strcpy(host, nhost);
		if (lastdir != NULL) {
			*lastdir = 0;
			/* Don't cd if the dir is the root directory. */
			if (ndir != NULL && (strcmp("/", ndir) != 0))
				(void) strcpy(lastdir, ndir);
		}
	}
}	/* GetFullSiteName */




int ruserpass2(char *host, char **username, char **pass, char **acct)
{
	FILE			*fp;
	char			*cp, *dp, *dst, *ep;
	str32			macname;
	char			*varname;
	int				site_found;
	string			str;
	static string	auser;
	static str32	apass, aacct;

	site_found = 0;

	if ((fp = fopen(rcname, "r")) != NULL) {
		parsing_rc = 1;
		while (FGets(str, fp)) {
			if ((cp = strstr(str, "machine")) != 0) {
				/* Look for the machine token. */
				cp += 7;
				while (isspace(*cp))
					cp++;
			} else
				continue;
			if (strncmp(host, cp, strlen(host)) == 0) {
				site_found = 1;
				while (!isspace(*cp))
					++cp;		/* skip the site name. */
				do {
					/* Skip any comments ahead of time. */
					for (dp=cp; *dp; dp++) {
						if (*dp == '#') {
							*dp = 0;
							break;
						}
					}

					ep = cp;
					while (1) {
						varname = strtok(ep, RC_DELIM);
						if (!varname) break;
						dst = ep = NULL;
						switch (*varname) {
							case 'u':	/* user */
								*username = dst = auser;
								break;
							case 'l':	/* login */
								*username = dst = auser;
								break;
							case 'p':	/* password */
								*pass = dst = apass;
								break;
							case 'a':	/* account */
								*acct = dst = aacct;
								break;
						/*	case 'd':  /o default */
						/* unused -- use 'set anon_password.' */
							case 'm':	/* macdef or machine */
								if (strcmp(varname, "macdef"))
									goto done;	/* new machine record... */
								dst = macname;
								break;
							default:
								(void) fprintf(stderr, "Unknown .netrc keyword \"%s\"\n",
									varname
								);
						}
						if (dst) {
							dp = strtok(ep, RC_DELIM);
							if (dp)
								(void) strcpy(dst, dp);
							if (dst == macname) {
								/*
								 *	Read in the lines of the macro.
								 *	The macro's end is denoted by
								 *	a blank line.
								 */
								(void) make_macro(macname, fp);
								goto nextline;
							}
						}
					}
nextline: ;
				} while ((cp = FGets(str, fp)) != 0);
				break;
			}		/* end if we found the machine record. */
		}
done:
		parsing_rc = 0;
		(void) fclose(fp);
	}

	if (!site_found) {
		/* didn't find it in the rc. */
		return (0);
	}

	return (1);	/* found */
}	/* ruserpass2 */

/* eof ftprc.c */
