/*
**  Copyright 2000-2004 University of Illinois Board of Trustees
**  Copyright 2000-2004 Mark D. Roth
**  All rights reserved.
**
**  ftpstat.c - FTP file attribute checking code
**
**  Mark D. Roth <roth@feep.net>
*/

#include <internal.h>

#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>

#ifdef STDC_HEADERS
# include <string.h>
#endif


static int
_ftp_stat_aux(FTP *ftp, char *path, struct ftpstat *fsp, unsigned short flags)
{
	file_info_t *fip = NULL;

#ifdef DEBUG
	printf("==> _ftp_stat_aux(ftp=0x%lx, path=\"%s\", fsp=0x%lx, "
	       "flags=%hu)\n", ftp, path, fsp, flags);
#endif

	if (_ftp_dircache_find_file(ftp, path, flags, &fip) == -1
	    || fip == NULL)
		return -1;

	memcpy(fsp, &(fip->fi_stat), sizeof(struct ftpstat));
	return 0;
}


/* stat a symlink */
int
ftp_lstat(FTP *ftp, char *path, struct ftpstat *fsp)
{
	return _ftp_stat_aux(ftp, path, fsp, DC_NOFOLLOWLINKS);
}


/* stat a file, dereferencing symlinks */
int
ftp_stat(FTP *ftp, char *path, struct ftpstat *fsp)
{
	return _ftp_stat_aux(ftp, path, fsp, 0);
}


/* read the contents of a symbolic link */
int
ftp_readlink(FTP *ftp, char *path, char *buf, size_t bufsize)
{
	file_info_t *fip = NULL;

	if (_ftp_dircache_find_file(ftp, path, DC_NOFOLLOWLINKS, &fip) == -1
	    || fip == NULL)
		return -1;

	if (! S_ISLNK(fip->fi_stat.fs_mode))
	{
		errno = EINVAL;
		return -1;
	}

	strlcpy(buf, fip->fi_linkto, bufsize);
	return 0;
}


/* determine the absolute pathname of the file or dir specified by path */
int
_ftp_abspath(FTP *ftp, char *path, char *abspath, size_t abspathlen)
{
	char buf[MAXPATHLEN];

	if (path[0] == '/')
		strlcpy(buf, path, sizeof(buf));
	else
		snprintf(buf, sizeof(buf), "%s/%s", ftp_getcwd(ftp), path);

	return fget_cleanpath(buf, abspath, abspathlen);
}


/* resolve symlinks */
static int
_ftp_realpath_aux(FTP *ftp, char *path,
		 char *resolved_path, size_t resolvelen, int level)
{
	char pathbuf[MAXPATHLEN];
	char buf[MAXPATHLEN];
	char link_target[MAXPATHLEN];
	char *nextp, *dirp = NULL;
	struct ftpstat fs;

#ifdef DEBUG
	printf("==> _ftp_realpath_aux(ftp=(%s), path=\"%s\", "
	       "resolved_path=0x%lx, resolvelen=%d, level=%d)\n",
	       ftp->ftp_host, path, resolved_path, resolvelen, level);
#endif

	if (level >= MAXSYMLINKS)
	{
		errno = ELOOP;
		return -1;
	}

	/* initialize to empty string */
	resolved_path[0] = '\0';

	_ftp_abspath(ftp, path, pathbuf, sizeof(pathbuf));

	nextp = pathbuf + 1;	/* skip leading slash */

	/* iterate through path components */
	while ((dirp = strsep(&nextp, "/")) != NULL)
	{
		strlcat(resolved_path, "/", resolvelen);
		strlcat(resolved_path, dirp, resolvelen);

#ifdef DEBUG
		printf("dirp=\"%s\"\n", dirp);
		printf("nextp=\"%s\"\n", nextp ? nextp : "[NULL]");
		printf("resolved_path=\"%s\"\n", resolved_path);
#endif

		if (ftp_lstat(ftp, resolved_path, &fs) == -1)
			return -1;

		if (S_ISLNK(fs.fs_mode))
		{
			if (ftp_readlink(ftp, resolved_path, link_target,
					 sizeof(link_target)) == -1)
				return -1;

			/*
			** determine absolute path that the link points to
			*/

			if (link_target[0] != '/')
				snprintf(buf, sizeof(buf), "%s/%s",
					 dirname(path), link_target);
			else
				strlcpy(buf, link_target, sizeof(buf));

			/*
			** append the remaining components from the
			** original path
			*/

			if (nextp != NULL)
			{
				strlcat(buf, "/", sizeof(buf));
				strlcat(buf, nextp, sizeof(buf));
			}

			/*
			** call ourself recursively
			*/

			return _ftp_realpath_aux(ftp, buf,
						resolved_path, resolvelen,
						level + 1);
		}
	}

#ifdef DEBUG
	printf("<== _ftp_realpath_aux(): returning \"%s\"\n", resolved_path);
#endif

	return 0;
}


int
ftp_realpath(FTP *ftp, char *path, char *resolved_path, size_t resolvelen)
{
	return _ftp_realpath_aux(ftp, path, resolved_path, resolvelen, 0);
}




syntax highlighted by Code2HTML, v. 0.9.1