/* main.c
 *
 * Copyright (c) 1992-2001 by Mike Gleason.
 * All rights reserved.
 *
 */

#include "syshdrs.h"
#include "ls.h"
#include "bookmark.h"
#include "shell.h"
#include "cmds.h"
#include "main.h"
#include "getopt.h"
#include "progress.h"
#include "pref.h"
#include "readln.h"
#include "trace.h"
#include "log.h"
#include "spool.h"
#include "util.h"

#if defined(WIN32) || defined(_WINDOWS)
	WSADATA wsaData;
	int wsaInit = 0;

	__inline void DisposeWinsock(int aUNUSED) { if (wsaInit > 0) WSACleanup(); wsaInit--; }
#else
#	define DisposeWinsock(a)
#endif

int gStartupUrlParameterGiven = 0;
int gIsTTY, gIsTTYr;
int gScreenColumns;

FTPLibraryInfo gLib;
FTPConnectionInfo gConn;
LineList gStartupURLCdList;
int gTransferTypeInitialized = 0;
int gTransferType;
int gURLMode = 0;
extern int gUnprocessedJobs;
char gLocalCWD[512], gPrevLocalCWD[512];

extern char *gOptArg;
extern int gOptInd;
extern char gRemoteCWD[512], gPrevRemoteCWD[512];
extern Bookmark gBm;
extern int gLoadedBm;
extern int gFirstTimeUser;
extern int gFirewallType;
extern char gAutoAscii[];
extern char gFirewallHost[64];
extern char gFirewallUser[32];
extern char gFirewallPass[32];
extern char gFirewallExceptionList[];
extern char gCopyright[], gVersion[];
extern unsigned int gFirewallPort;
extern int gConnTimeout, gXferTimeout, gCtrlTimeout;
extern int gDataPortMode, gRedialDelay;
extern int gDebug;
extern int gNumProgramRuns, gDoNotDisplayAds;
extern int gSOBufsize;
extern FTPProgressMeterProc gProgressMeter;

static void
Usage(void)
{
	FILE *fp;
#ifdef UNAME
	char s[80];
#endif

	fp = stderr;
	(void) fprintf(fp, "\nUsage:  ncftp [flags] [<host> | <directory URL to browse>]\n");
	(void) fprintf(fp, "\nFlags:\n\
  -u XX  Use username XX instead of anonymous.\n\
  -p XX  Use password XX with the username.\n\
  -P XX  Use port number XX instead of the default FTP service port (21).\n\
  -j XX  Use account XX with the username (rarely needed).\n\
  -F     Dump a sample $HOME/.ncftp/firewall prefs file to stdout and exit.\n");

	(void) fprintf(fp, "\nProgram version:  %s\nLibrary version:  %s\n", gVersion + 5, gLibNcFTPVersion + 5);
#ifdef UNAME
	AbbrevStr(s, UNAME, 60, 1);
	(void) fprintf(fp, "System:           %s\n", s);
#endif
	(void) fprintf(fp, "\nThis is a freeware program by Mike Gleason (ncftp@ncftp.com).\n");
	(void) fprintf(fp, "Use ncftpget and ncftpput for command-line FTP.\n\n");
	exit(2);
}	/* Usage */




static void
DumpFirewallPrefsTemplate(void)
{
	WriteDefaultFirewallPrefs(stdout);
}	/* DumpFirewallPrefsTemplate */




/* This resets our state information whenever we are ready to open a new
 * host.
 */
void
InitConnectionInfo(void)
{
	int result;

	result = FTPInitConnectionInfo(&gLib, &gConn, kDefaultFTPBufSize);
	if (result < 0) {
		(void) fprintf(stderr, "ncftp: init connection info error %d (%s).\n", result, FTPStrError(result));
		exit(1);
	}

	gConn.debugLog = NULL;
	gConn.errLog = stderr;
	SetDebug(gDebug);
	UseTrace();
	(void) STRNCPY(gConn.user, "anonymous");
	gConn.host[0] = '\0';
	gConn.progress = gProgressMeter;
	gTransferTypeInitialized = 0;
	gTransferType = kTypeBinary;
	gConn.leavePass = 1;		/* Don't let the lib zap it. */
	gConn.connTimeout = gConnTimeout;
	gConn.xferTimeout = gXferTimeout;
	gConn.ctrlTimeout = gCtrlTimeout;
	gConn.dataPortMode = gDataPortMode;
	gConn.maxDials = (-1);	/* Dial forever, until they hit ^C. */
	gUnprocessedJobs = 0;
	gPrevRemoteCWD[0] = '\0';
	gConn.dataSocketRBufSize = gConn.dataSocketSBufSize = gSOBufsize;
	if (gRedialDelay >= 10)
		gConn.redialDelay = gRedialDelay;
	if ((gAutoAscii[0] == '\0') || (ISTREQ(gAutoAscii, "no")) || (ISTREQ(gAutoAscii, "off")) || (ISTREQ(gAutoAscii, "false"))) {
		gConn.asciiFilenameExtensions = NULL;
	} else {
		gConn.asciiFilenameExtensions = gAutoAscii;
	}
}	/* InitConnectionInfo */




/* This lets us do things with our state information just before the
 * host is closed.
 */
void
CloseHost(void)
{
	if (gConn.connected != 0) {
		if (gConn.loggedIn != 0) {
			SaveUnsavedBookmark();
		}
		RunBatchIfNeeded(&gConn);
	}
	gConn.ctrlTimeout = 3;
	(void) FTPCloseHost(&gConn);
}	/* CloseHost */




/* If the user specified a URL on the command-line, this initializes
 * our state information based upon it.
 */
static void
SetStartupURL(const char *const urlgiven)
{
	int rc;
	char url[256];
	char urlfile[128];

	gLoadedBm = 0;
	(void) STRNCPY(url, urlgiven);

	rc = DecodeDirectoryURL(&gConn, url, &gStartupURLCdList, urlfile, sizeof(urlfile));
	if (rc == kMalformedURL) {
		(void) fprintf(stderr, "Malformed URL: %s\n", url);
		exit(1);
	} else if (rc == kNotURL) {
		/* This is what should happen most of the time. */
		(void) STRNCPY(gConn.host, urlgiven);
		gURLMode = 2;
		if (GetBookmark(gConn.host, &gBm) >= 0) {
			gLoadedBm = 1;
			(void) STRNCPY(gConn.host, gBm.name);
			(void) STRNCPY(gConn.user, gBm.user);
			(void) STRNCPY(gConn.pass, gBm.pass);
			(void) STRNCPY(gConn.acct, gBm.acct);
			gConn.hasSIZE = gBm.hasSIZE;
			gConn.hasMDTM = gBm.hasMDTM;
			gConn.hasPASV = gBm.hasPASV;
			gConn.hasUTIME = gBm.hasUTIME;
			gConn.port = gBm.port;
		} else {
			SetBookmarkDefaults(&gBm);
		}

		if (MayUseFirewall(gConn.host, gFirewallType, gFirewallExceptionList) != 0) {
			gConn.firewallType = gFirewallType;
			(void) STRNCPY(gConn.firewallHost, gFirewallHost);
			(void) STRNCPY(gConn.firewallUser, gFirewallUser);
			(void) STRNCPY(gConn.firewallPass, gFirewallPass);
			gConn.firewallPort = gFirewallPort;
		}
	} else {
		/* URL okay */
		if (urlfile[0] != '\0') {
			/* It was obviously not a directory */
			(void) fprintf(stderr, "Use ncftpget or ncftpput to handle file URLs.\n");
			exit(1);
		}
		gURLMode = 1;
		if (MayUseFirewall(gConn.host, gFirewallType, gFirewallExceptionList) != 0) {
			gConn.firewallType = gFirewallType;
			(void) STRNCPY(gConn.firewallHost, gFirewallHost);
			(void) STRNCPY(gConn.firewallUser, gFirewallUser);
			(void) STRNCPY(gConn.firewallPass, gFirewallPass);
			gConn.firewallPort = gFirewallPort;
		}
	}
}	/* SetStartupURL */




static void
OpenURL(void)
{
	LinePtr lp;
	int result;

	if (gURLMode == 1) {
		SetBookmarkDefaults(&gBm);
		if (DoOpen() >= 0) {
			for (lp = gStartupURLCdList.first; lp != NULL; lp = lp->next) {
				result = FTPChdir(&gConn, lp->line);
				if (result != kNoErr) {
					FTPPerror(&gConn, result, kErrCWDFailed, "Could not chdir to", lp->line);
					break;
				}
			}
			result = FTPGetCWD(&gConn, gRemoteCWD, sizeof(gRemoteCWD));
			if (result != kNoErr) {
				FTPPerror(&gConn, result, kErrPWDFailed, NULL, NULL);
			} else {
				(void) printf("Current remote directory is %s.\n", gRemoteCWD);
			}
		}
	} else if (gURLMode == 2) {
		(void) DoOpen();
	}
}	/* OpenURL */




/* These things are done first, before we even parse the command-line
 * options.
 */
static void
PreInit(void)
{
	int result;

#if defined(WIN32) || defined(_WINDOWS)
	if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
		fprintf(stderr, "could not initialize winsock\n");
		exit(1);
	}
	wsaInit++;
#endif

#ifdef HAVE_SETLOCALE
	setlocale(LC_ALL, "");
#endif
#if defined(WIN32) || defined(_WINDOWS)
	gIsTTY = 1;
	gIsTTYr = 1;
#else
	gIsTTY = ((isatty(2) != 0) && (getppid() > 1)) ? 1 : 0;
	gIsTTYr = ((isatty(0) != 0) && (getppid() > 1)) ? 1 : 0;
#endif
#ifdef SIGPOLL
	(void) NcSignal(SIGPOLL, (FTPSigProc) SIG_IGN);
#endif
	InitUserInfo();
	result = FTPInitLibrary(&gLib);
	if (result < 0) {
		(void) fprintf(stderr, "ncftp: init library error %d (%s).\n", result, FTPStrError(result));
		exit(1);
	}
#if defined(WIN32) || defined(_WINDOWS)
	srand((unsigned int) (GetTickCount() & 0x7FFF));
#else
	srand((unsigned int) getpid());
#endif
	InitLineList(&gStartupURLCdList);
	CheckForNewV3User();
	InitLog();
	InitPrefs();
	LoadFirewallPrefs(0);
	LoadPrefs();
	InitConnectionInfo();
	InitCommandList();
	InitLs();
	TruncBatchLog();
	GetoptReset();
	GetScreenColumns();
}	/* PreInit */





/* These things are done at startup, but after we parse the command-line
 * options.
 */
static void
PostInit(void)
{
	PostInitPrefs();
	OpenTrace();
	InitTermcap();
	InitReadline();
	(void) FTPGetLocalCWD(gLocalCWD, sizeof(gLocalCWD));
	gPrevLocalCWD[0] = '\0';
	PrintStartupBanner();
	if (gNumProgramRuns <= 1)
		(void) printf("\n%s\n", gCopyright + 5);

	Trace(0, "Fw: %s  Type: %d  User: %s  Pass: %s  Port: %u\n",
		gFirewallHost,
		gFirewallType,
		gFirewallUser,
		(gFirewallPass[0] == '\0') ? "(none)" : "********",
		gFirewallPort
	);
	Trace(0, "FwExceptions: %s\n", gFirewallExceptionList);
	if (strchr(gLib.ourHostName, '.') == NULL) {
		Trace(0, "NOTE:  Your domain name could not be detected.\n");
		if (gConn.firewallType != kFirewallNotInUse) {
			Trace(0, "       Make sure you manually add your domain name to firewall-exception-list.\n");
		}
	}
}	/* PostInit */




/* Try to get the user to evaluate my commercial offerings. */
static void
Plug(void)
{
#if defined(WIN32) || defined(_WINDOWS)
	/* NcFTPd hasn't been ported to Windows. */
#else
	if ((gDoNotDisplayAds == 0) && ((gNumProgramRuns % 7) == 2)) {
		(void) printf("\n\n\n\tThank you for using NcFTP Client.\n\tAsk your system administrator to try NcFTPd Server!\n\thttp://www.ncftpd.com\n\n\n\n");
	}
#endif
}	/* Plug */




/* These things are just before the program exits. */
static void
PostShell(void)
{
	SetXtermTitle("RESTORE");
	CloseHost();
	DisposeReadline();
	CloseTrace();
	SavePrefs();
	EndLog();
	Plug();
	DisposeWinsock(0);
}	/* PostShell */




int
main(int argc, const char **const argv)
{
	int c;
	int n;

	PreInit();
	while ((c = Getopt(argc, argv, "P:u:p:J:rd:g:FVLD")) > 0) switch(c) {
		case 'P':
		case 'u':
		case 'p':
		case 'J':
			gStartupUrlParameterGiven = 1;
			break;
		case 'r':
		case 'g':
		case 'd':
		case 'V':
		case 'L':
		case 'D':
		case 'F':
			break;
		default:
			Usage();
	}

	if (gOptInd < argc) {
		LoadFirewallPrefs(0);
		SetStartupURL(argv[gOptInd]);
	} else if (gStartupUrlParameterGiven != 0) {
		/* One of the flags they specified
		 * requires a URL or hostname to
		 * open.
		 */
		Usage();
	}

	GetoptReset();
	/* Allow command-line parameters to override
	 * bookmark settings.
	 */

	while ((c = Getopt(argc, argv, "P:u:p:j:J:rd:g:FVLD")) > 0) switch(c) {
		case 'P':
			gConn.port = atoi(gOptArg);
			break;
		case 'u':
			(void) STRNCPY(gConn.user, gOptArg);
			break;
		case 'p':
			(void) STRNCPY(gConn.pass, gOptArg);	/* Don't recommend doing this! */
			break;
		case 'J':
		case 'j':
			(void) STRNCPY(gConn.acct, gOptArg);
			break;
		case 'r':
			/* redial is always on */
			break;
		case 'g':
			gConn.maxDials = atoi(gOptArg);
			break;
		case 'd':
			n = atoi(gOptArg);
			if (n >= 10)
				gConn.redialDelay = n;
			break;
		case 'F':
			DumpFirewallPrefsTemplate();
			exit(0);
			/*NOTREACHED*/
			break;
		case 'V':
			/*FALLTHROUGH*/
		case 'L':
			/*FALLTHROUGH*/
		case 'D':
			/* silently ignore these legacy options */
			break;
		default:
			Usage();
	}

	PostInit();
	OpenURL();
	CommandShell();
	PostShell();
	exit(0);
}	/* main */
