/* spoolutil.c
 *
 * Copyright (c) 1992-2005 by Mike Gleason.
 * All rights reserved.
 * 
 */

#include "syshdrs.h"
#ifdef PRAGMA_HDRSTOP
#	pragma hdrstop
#endif

#ifdef HAVE_LONG_FILE_NAMES

#include "spool.h"
#include "util.h"

int gSpoolSerial = 0;

extern FTPLibraryInfo gLib;
extern char gOurDirectoryPath[];
#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
extern char gOurInstallationPath[];
#endif
#ifdef ncftp
extern int gUnprocessedJobs;
#endif

int
MkSpoolDir(char *sdir, size_t size)
{
	struct stat st;
	*sdir = '\0';

	/* Don't create in root directory. */
	if (gOurDirectoryPath[0] != '\0') { 
		(void) OurDirectoryPath(sdir, size, kSpoolDir);
		if ((stat(sdir, &st) < 0) && (MkDirs(sdir, 00700) < 0)) {
			perror(sdir);
			return (-1);
		} else {
			return (0);
		}
	}
	return (-1);
}	/* MkSpoolDir */



void
SpoolName(char *const sp, const size_t size, const int flag, const int serial, time_t when)
{
	char dstr[32];
	struct tm lt;

	if ((when == (time_t) 0) || (when == (time_t) -1))
		(void) time(&when);
	if (Gmtime(when, &lt) == NULL) {
		/* impossible */
		(void) Strncpy(dstr, "20010101-000000", size);
	} else {
		(void) strftime(dstr, sizeof(dstr), "%Y%m%d-%H%M%S", &lt);
	}
#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
	(void) sprintf(sp, "%c-%s-%08X-%d",
		flag,
		dstr,
		(unsigned int) getpid(),
		serial
	);
#else
#	ifdef HAVE_SNPRINTF
	(void) snprintf(sp, size - 1,
#	else
	(void) sprintf(sp,
#	endif
		"%c-%s-%06d-%d",
		flag,
		dstr,
		(unsigned int) getpid(),
		serial
	);
#endif
}	/* SpoolName */




static int
WriteSpoolLine(FILE *const ofp, const char *const line)
{
	int c;
	const char *cp;

	c = 0;
	for (cp = line; *cp; cp++) {
		c = (int) *cp;
		if ((c == '\n') && (cp[1] != '\0')) {
			if (putc('\\', ofp) == EOF)
				return (-1);
		}
		if (putc(c, ofp) == EOF)
			return (-1);
	}
	if (c != '\n') {
		c = '\n';
		if (putc(c, ofp) == EOF)
			return (-1);
	}
	return (0);
}	/* WriteSpoolLine */



int
SpoolX(
	FILE *const ofp,
	const char *sdir,
	const char *const op,
	const char *const rfile,
	const char *const rdir,
	const char *const lfile,
	const char *const ldir,
	const char *const host,
	const char *const ip,
	const unsigned int port,
	const char *const user,
	const char *const passclear,
	const char *const xacct,
	const int xtype,
	const int recursive,
	const int deleteflag,
	const int passive,
	const char *const preftpcmd,
	const char *const perfileftpcmd,
	const char *const postftpcmd,
	const char *const preshellcmd,
	const char *const postshellcmd,
	const time_t when,
	const unsigned int delaySinceLastFailure,
	const char *const manualOverrideFeatures,
	const char *const preferredLocalAddrStr,
	const int UNUSED(reserved)
	)
{
	char sdir2[256];
	char pass[160];
	char sname[64], sname2[64];
	char spathname[256];
	char spathname2[256];
	char ldir2[256];
	char *ldir3;
	FILE *fp;
#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
#else
	mode_t um;
#endif

	LIBNCFTP_USE_VAR(reserved);
	gSpoolSerial++;
	SpoolName(sname2, sizeof(sname2), op[0], gSpoolSerial, when);

	if (ofp == NULL) {
		if (sdir == NULL) {
			if (MkSpoolDir(sdir2, sizeof(sdir2)) < 0)
				return (-1);
			sdir = sdir2;
		}
		Path(spathname2, sizeof(spathname2), sdir, sname2);
		STRNCPY(sname, sname2);
		sname[0] = 'z';
		Path(spathname, sizeof(spathname), sdir, sname);
	}

#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
	fp = (ofp == NULL) ? fopen(spathname, FOPEN_WRITE_TEXT) : ofp;
#else
	um = umask(077);
	fp = (ofp == NULL) ? fopen(spathname, FOPEN_WRITE_TEXT) : ofp;
	(void) umask(um);
#endif
	if (fp == NULL)
		return (-1);

	if (fprintf(fp, "# This is a NcFTP spool file entry.\n") < 0)
		goto err;
	if ((ofp == NULL) && (fprintf(fp, "# Run the \"ncftpbatch\" program to process the spool directory.\n#\n") < 0))
		goto err;
	if (fprintf(fp, "job-name=%s\n", sname2) < 0)
		goto err;
	if (fprintf(fp, "op=%s\n", op) < 0)
		goto err;
	if ((delaySinceLastFailure != 0) && (fprintf(fp, "delay-since-last-failure=%u\n", delaySinceLastFailure) < 0))
		goto err;
	if (fprintf(fp, "hostname=%s\n", host) < 0)
		goto err;
	if ((ip != NULL) && (ip[0] != '\0') && (fprintf(fp, "host-ip=%s\n", ip) < 0))
		goto err;
	if ((port != 0) && (port != (unsigned int) kDefaultFTPPort) && (fprintf(fp, "port=%u\n", port) < 0))
		goto err;
	if ((user != NULL) && (user[0] != '\0') && (strcmp(user, "anonymous") != 0) && (fprintf(fp, "user=%s\n", user) < 0))
		goto err;
	if ((strcmp(user, "anonymous") != 0) && (passclear != NULL) && (passclear[0] != '\0')) {
		(void) memcpy(pass, kPasswordMagic, kPasswordMagicLen);
		ToBase64(pass + kPasswordMagicLen, passclear, strlen(passclear), 1);
		if (fprintf(fp, "pass=%s\n", pass) < 0)
			goto err;
	} else if ((strcmp(user, "anonymous") == 0) && (gLib.defaultAnonPassword[0] != '\0')) {
		if (fprintf(fp, "pass=%s\n", gLib.defaultAnonPassword) < 0)
			goto err;
	}
	if ((xacct != NULL) && (xacct[0] != '\0') && (fprintf(fp, "acct=%s\n", xacct) < 0))
		goto err;
	if (fprintf(fp, "xtype=%c\n", xtype) < 0)
		goto err;
	if ((recursive != 0) && (fprintf(fp, "recursive=%s\n", YESNO(recursive)) < 0))
		goto err;
	if ((deleteflag != 0) && (fprintf(fp, "delete=%s\n", YESNO(deleteflag)) < 0))
		goto err;
	if (fprintf(fp, "passive=%d\n", passive) < 0)
		goto err;
	if (fprintf(fp, "remote-dir=%s\n", rdir) < 0)
		goto err;
	if ((ldir == NULL) || (ldir[0] == '\0') || (strcmp(ldir, ".") == 0)) {
		/* Use current process' working directory. */
		FTPGetLocalCWD(ldir2, sizeof(ldir2));
		if (fprintf(fp, "local-dir=%s\n", ldir2) < 0)
			goto err;
#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
	} else if ((ldir[0] != '/') && (ldir[0] != '\\')) {
		FTPGetLocalCWD(ldir2, sizeof(ldir2));
		if (DPathCat(&ldir3, ldir2, ldir, 1) == 0) {
			if (fprintf(fp, "local-dir=%s\n", ldir3) < 0)
				goto err;
			free(ldir3);
		}
#else
	} else if (ldir[0] != '/') {
		FTPGetLocalCWD(ldir2, sizeof(ldir2));
		if (DPathCat(&ldir3, ldir2, ldir, 0) == 0) {
			if (fprintf(fp, "local-dir=%s\n", ldir3) < 0)
				goto err;
			free(ldir3);
		}
#endif
	} else {
		if (fprintf(fp, "local-dir=%s\n", ldir) < 0)
			goto err;
	}
	if (fprintf(fp, "remote-file=%s\n", rfile) < 0)
		goto err;
	if (fprintf(fp, "local-file=%s\n", lfile) < 0)
		goto err;
	if ((manualOverrideFeatures != NULL) && (manualOverrideFeatures[0] != '\0')) {
		if (fprintf(fp, "manual-override-features=") < 0)
			goto err;
		if (WriteSpoolLine(fp, manualOverrideFeatures) < 0)
			goto err;
	}
	if ((preferredLocalAddrStr != NULL) && (preferredLocalAddrStr[0] != '\0')) {
		if (fprintf(fp, "source-address=") < 0)
			goto err;
		if (WriteSpoolLine(fp, preferredLocalAddrStr) < 0)
			goto err;
	}
	if ((preftpcmd != NULL) && (preftpcmd[0] != '\0')) {
		if (fprintf(fp, "pre-ftp-command=") < 0)
			goto err;
		if (WriteSpoolLine(fp, preftpcmd) < 0)
			goto err;
	}
	if ((perfileftpcmd != NULL) && (perfileftpcmd[0] != '\0')) {
		if (fprintf(fp, "per-file-ftp-command=") < 0)
			goto err;
		if (WriteSpoolLine(fp, perfileftpcmd) < 0)
			goto err;
	}
	if ((postftpcmd != NULL) && (postftpcmd[0] != '\0')) {
		if (fprintf(fp, "post-ftp-command=") < 0)
			goto err;
		if (WriteSpoolLine(fp, postftpcmd) < 0)
			goto err;
	}
	if ((preshellcmd != NULL) && (preshellcmd[0] != '\0')) {
		if (fprintf(fp, "pre-shell-command=") < 0)
			goto err;
		if (WriteSpoolLine(fp, preshellcmd) < 0)
			goto err;
	}
	if ((postshellcmd != NULL) && (postshellcmd[0] != '\0')) {
		if (fprintf(fp, "post-shell-command=") < 0)
			goto err;
		if (WriteSpoolLine(fp, postshellcmd) < 0)
			goto err;
	}

	if ((fp != ofp) && (fclose(fp) < 0))
		goto err2;

	/* Move the spool file into its "live" name. */
	if ((fp != ofp) && (rename(spathname, spathname2) < 0)) {
		perror("rename spoolfile failed");
		goto err3;
	}
#ifdef ncftp
	gUnprocessedJobs++;
#endif
	return (0);

err:
	if (fp != ofp)
		(void) fclose(fp);
err2:
	perror("write to spool file failed");
err3:
	if (fp != ofp)
		(void) unlink(spathname);
	return (-1);
}	/* SpoolX */




void
RunBatch(void)
{
#if (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__)
	char ncftpbatch[260];
	const char *prog;
	int winExecResult;

	if (gOurInstallationPath[0] == '\0') {
		(void) fprintf(stderr, "Cannot find path to %s.  Please re-run Setup.\n", "ncftpbatch.exe");
		return;
	}
	prog = ncftpbatch;
	OurInstallationPath(ncftpbatch, sizeof(ncftpbatch), "ncftpbatch.exe");
	
	winExecResult = WinExec(prog, SW_SHOWNORMAL);
	if (winExecResult <= 31) switch (winExecResult) {
		case ERROR_BAD_FORMAT:
			fprintf(stderr, "Could not run %s: %s\n", prog, "The .EXE file is invalid");
			return;
		case ERROR_FILE_NOT_FOUND:
			fprintf(stderr, "Could not run %s: %s\n", prog, "The specified file was not found.");
			return;
		case ERROR_PATH_NOT_FOUND:
			fprintf(stderr, "Could not run %s: %s\n", prog, "The specified path was not found.");
			return;
		default:
			fprintf(stderr, "Could not run %s: Unknown error #%d.\n", prog, winExecResult);
			return;
	}
#else
	char *argv[8];
	pid_t pid = 0;
#if (defined(BINDIR) || defined(PREFIX_BINDIR))
	char ncftpbatch[256];

#ifdef BINDIR
	STRNCPY(ncftpbatch, BINDIR);
#else
	STRNCPY(ncftpbatch, PREFIX_BINDIR);
#endif
	STRNCAT(ncftpbatch, "/");
	STRNCAT(ncftpbatch, "ncftpbatch");
#endif	/* BINDIR */

	pid = fork();
	if (pid < 0) {
		perror("fork");
	} else if (pid == 0) {
		argv[0] = strdup("ncftpbatch");
		argv[1] = strdup("-d");
		argv[2] = NULL;
#if (defined(BINDIR) || defined(PREFIX_BINDIR))
		(void) execv(ncftpbatch, argv);
		(void) fprintf(stderr, "Could not run %s.  Is it in installed as %s?\n", argv[0], ncftpbatch);
#else	/* BINDIR */
		(void) execvp(argv[0], argv);
		(void) fprintf(stderr, "Could not run %s.  Is it in your $PATH?\n", argv[0]);
#endif	/* BINDIR */
		perror(argv[0]);
		exit(1);
	}

	if (pid > 1) {
#ifdef HAVE_WAITPID
		(void) waitpid(pid, NULL, 0);
#else
		(void) wait(NULL);
#endif	/* HAVE_WAITPID */
	}
#endif	/* UNIX */
}	/* RunBatch */

#else	/* ! HAVE_LONG_FILE_NAMES */

int
SpoolX(
	FILE *const ofp,
	const char *sdir,
	const char *const op,
	const char *const rfile,
	const char *const rdir,
	const char *const lfile,
	const char *const ldir,
	const char *const host,
	const char *const ip,
	const unsigned int port,
	const char *const user,
	const char *const passclear,
	const char *const xacct,
	const int xtype,
	const int recursive,
	const int deleteflag,
	const int passive,
	const char *const preftpcmd,
	const char *const perfileftpcmd,
	const char *const postftpcmd,
	const char *const preshellcmd,
	const char *const postshellcmd,
	const time_t when,
	const unsigned int delaySinceLastFailure)
{
	return (-1);
}

void
RunBatch(void)
{
	fprintf(stderr, "Background processing not available on this platform.\n");
}	/* RunBatch */

#endif	/* HAVE_LONG_FILE_NAMES */
