//////////////////////////////////////////////////////////
//
//         FTP Jr  -- A Poor Man's FTP Client
//         Sample WinInet Application to demonstrate
//         The FTP APIs
//         Microsoft Corporation (C)
//
//////////////////////////////////////////////////////////

#include <iostream.h>
#include <windows.h>
#include <direct.h>
#include <wininet.h>

#define PROMPT  "FTP Jr>"

BOOL ParseCommand(char *, char *, char *);
BOOL Open (CHAR *);
BOOL Close ();
BOOL ErrorOut ( DWORD, CHAR *);
void FtpEnumDirectory ();
void lcd (char *);
void rcd (char *);
void type (char *);
void DisplayHelp();
void ShowUsage();

char arg1[256], arg2[256], arg3[256];
char szProxy[256] = "";
char szServer[256] = "";
char szCmdLine[256] = "";
HINTERNET hOpen, hConnect;
BOOL bActiveSession = FALSE;
DWORD dwType = FTP_TRANSFER_TYPE_BINARY;

void main(int argc, char *argv[])
{
	
	// Display our banner
	cout << "FTP Jr. WinInet FTP Sample Application" << endl;
	cout << "Microsoft Corporation" << endl << endl;
	
	// Do they want the display syntax?
	if ((lstrcmp(argv[1], "/?") == 0) ||
        (lstrcmp(argv[1], "?") == 0) ||
		(lstrcmp(argv[1], "-?") == 0) ||
		(lstrcmp(argv[1], "help") == 0) ||
        (lstrcmp(argv[1], "/help") == 0) ||
		(lstrcmp(argv[1], "/HELP") == 0) ||
		(lstrcmp(argv[1], "HELP") == 0))
	{
		ShowUsage();
		return;
	}

	switch (argc)
	{
	case 2:
		// Specified proxy, Open WININET.DLL w/ Proxy
		lstrcpy(szProxy, argv[1]);
        if ( !(hOpen = InternetOpen ( "FTP Jr (C)",  CERN_PROXY_INTERNET_ACCESS, szProxy, NULL, 0) ) )
        {
            ErrorOut ( GetLastError(), "InternetOpen");
           return ;
        }
        break;
    case 1:
		// No proxy specified.  Open WININET.DLL w/ local access
        if ( !(hOpen = InternetOpen ( "FTP Jr (C)",  LOCAL_INTERNET_ACCESS , NULL, 0, 0) ) )
        {
            ErrorOut ( GetLastError(), "InternetOpen");
            return ;
        }
        break;    
    default:
		ShowUsage();
		return;
	}

	cout << PROMPT;

	// Parse commands 1 line at a time
	while(TRUE)
	{
		char *pszArg1, *pszArg2, *pszArg3, *pszTemp;

		// Read line
		cin.getline(szCmdLine, 256);
		pszArg1 = szCmdLine;

		//Remove white space before 1st arg
		while((*pszArg1 != 0) &&
			  (*pszArg1 == ' ')) pszArg1++;

		pszArg2 = pszArg1;
		
		//Find end of 1st arg and zero terminate
		while ((*pszArg2 != 0) &&
			   (*pszArg2 != ' ')) pszArg2++;
		
		if (*pszArg2 != 0) *pszArg2++ = 0;

		//Remove white space before 2nd arg
		while ((*pszArg2 !=0) &&
			   (*pszArg2 == ' ')) pszArg2++;

		pszArg3 = pszArg2;

		//Find end of 2nd arg and zero terminate
		while ((*pszArg3 != 0) &&
			   (*pszArg3 != ' ')) pszArg3++;

		if (*pszArg3 != 0) *pszArg3++ = 0;

		//Remove white space before 3rd Arg
		while ((*pszArg3 !=0) &&
			   (*pszArg3 == ' ')) pszArg3++;

		pszTemp = pszArg3;

		//Find end of 3rd arg and zero terminate
		while((*pszTemp != 0) &&
			  (*pszTemp != ' ')) pszTemp++;

		if (*pszTemp != 0) *pszTemp = 0;

		//Loop indefintely until ParseCommand returns false
		if (ParseCommand(pszArg1, pszArg2, pszArg3) == FALSE)
			return;

	}
}

///////////////////////////////////////////////////////////
//
//      ParseCommand Function
//
//      Input:   FTP command arguments
//      Returns: TRUE if any command called but quit
//               FALSE if command is quit
//
//////////////////////////////////////////////////////////
BOOL ParseCommand(char *pszArg1, char *pszArg2, char *pszArg3)
{
	_strupr(pszArg1);
    CHAR szFile1[256] ="", szFile2[256] ="", szTemp[256];

	// Quit Command
	if (lstrcmp(pszArg1, "QUIT") == 0)
		return FALSE;
	
	// Get Command
	else if (lstrcmp(pszArg1, "GET") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter RemoteFile LocalFile: ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpGetFile (hConnect,szFile1, szFile2, FALSE,INTERNET_FLAG_RELOAD, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpGetFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	}
	
	// Put Command
	else if (lstrcmp(pszArg1, "PUT") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter LocalFile RemoteFile : ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpPutFile (hConnect,szFile1, szFile2, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpPutFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	}
	
	// Del Command
	else if (lstrcmp(pszArg1, "DEL") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter RemoteFile : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpDeleteFile (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpDelFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	}
	
	// Ren Command
	else if (lstrcmp(pszArg1, "REN") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter ExistingFile NewFile : ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpRenameFile (hConnect,szFile1, szFile2 ) )
                ErrorOut (GetLastError(), "FtpRenameFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	
	}
	
	// ls command
	else if (lstrcmp(pszArg1, "LS") == 0)
	{
	    if (bActiveSession)
            FtpEnumDirectory();
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
	}
	
	// md command
	else if (lstrcmp(pszArg1, "MD") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter Directory : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpCreateDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpCreateDirectory");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	}

	// rd command
	else if (lstrcmp(pszArg1, "RD") == 0)
	{
	    if (bActiveSession)
        {
            cout << "Enter Directory : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpRemoveDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpRemoveDirectory");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;		
	}

	// type command
	else if (lstrcmp(pszArg1, "TYPE") == 0)
	{
        if (bActiveSession)
            type (pszArg2);
        else
            cout << "No active session" << endl << PROMPT;
	}   
    
	// cd command
	else if (lstrcmp(pszArg1, "CD") == 0)
	{
        if (bActiveSession)
            rcd (pszArg2);
        else
            cout << "No active session" << endl << PROMPT;
	}

	// lcd command
	else if (lstrcmp(pszArg1, "LCD") == 0)
	{
		lcd(pszArg2);
	}
	
	// open command
	else if (lstrcmp(pszArg1, "OPEN") == 0)
	{
        if (!bActiveSession)
        {
    	    if (Open (pszArg2))
                cout << "Connected to " << pszArg2 << endl << PROMPT;
            else
                cout << "Session closed" << endl << PROMPT;    
        } 
        else
            cout << "Already connected to " << szServer << endl << PROMPT;
           
	}

	// close command
	else if (lstrcmp(pszArg1, "CLOSE") == 0)
	{
        if (bActiveSession)
        {
    	    Close();
            cout << "Session closed" << endl << PROMPT;
        }
        else
            cout << "No active session" << endl << PROMPT;
	}
	
	// help command
	else if (lstrcmp(pszArg1, "HELP") == 0)
	{
		DisplayHelp();
	}
	
	// error Unknown command
	else
	{
		cout << "?Unknown Command" << endl << PROMPT;
	}
	return TRUE;
}

void DisplayHelp()
{
	cout << "Commands are:" << endl;
	cout << "   cd" << endl;
	cout << "   close" << endl;
	cout << "   del" << endl;
	cout << "   get" << endl;
	cout << "   help" << endl;
	cout << "   lcd" << endl;
	cout << "   ls" << endl;
	cout << "   md" << endl;
	cout << "   open" << endl;
	cout << "   put" << endl;
	cout << "   quit" << endl;
	cout << "   rd" << endl;
	cout << "   ren" << endl;
	cout << "   type" << endl;
    ShowUsage ();
	cout << endl << PROMPT;
}

void lcd(char *pszDir)
{
	if ((*pszDir != 0) && (_chdir(pszDir) == -1))
		cout << "?Invalid Local Directory" << endl << PROMPT;
	else
	{
		char dir[256];
		GetCurrentDirectory(256, dir);
		cout << "Local Directory is " << dir << endl << PROMPT;
	}
}

void rcd(char *pszDir)
{
    char dir[256];
	if (*pszDir != 0)
    {
		if (!FtpSetCurrentDirectory (hConnect, pszDir))
        {
            ErrorOut (GetLastError(), "FtpSetCurrentDirectory");
            cout <<  PROMPT;
        }
        else    
    		cout << "Remote Directory is " << pszDir << endl << PROMPT;
    }    
	else
	{
        DWORD dwSize = sizeof(dir);
		if (!FtpGetCurrentDirectory (hConnect, dir, &dwSize))
            cout << "Can not determine current remote directory " << endl << PROMPT;
        else    
    		cout << "Remote Directory is " << dir << endl << PROMPT;
	}
}

void type (char *szType)
{
    
	_strupr(szType);    
	if (*szType != 0)
    {
	    if (lstrcmp(szType, "BIN") == 0)
        {
		    dwType = FTP_TRANSFER_TYPE_BINARY;
            cout << "BIN mode selected";
        }
    	else if (lstrcmp(szType, "ASCII") == 0)
        {
		    dwType = FTP_TRANSFER_TYPE_ASCII;
            cout << "ASCII mode selected";
        }
        else
            cout << "Invalid mode";
        cout << endl << PROMPT;    
    }    
	else
	{
		if ( dwType == FTP_TRANSFER_TYPE_BINARY)
            cout << "BIN ";
        else    
    		cout << "ASCII ";
        cout << "mode selected " << endl << PROMPT;    
	}
}


void ShowUsage()
{
	cout << "Syntax:" << endl << "  FTPJR <proxy>" << endl ;
    cout << "Note: a CERN style proxy will not work. You will need to "<< endl;
    cout << "specify a TIS style FTP proxy in the form:" << endl << endl;
	cout << "     FTPJR ftp-gw" << endl << endl;
	cout << "where ftp-gw is the name of the host that is running the gateway" << endl;
}

BOOL Open (CHAR * szHost)
{
    CHAR szUser[256], szPass[256];
    if ( *szHost == 0)
    {
        cout << "Enter host, user, pass: ";
        cin >> szHost >> szUser >> szPass;
    }
    else
    {
        cout << "Enter user, pass: ";
        cin >> szUser >> szPass;
    }
    CHAR szTemp[256];
    cin.getline(szTemp, 256);
    if ( !(hConnect = InternetConnect ( hOpen, szHost , INTERNET_INVALID_PORT_NUMBER, szUser,  szPass, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE , 0) ) )
    {
    	ErrorOut (GetLastError(), "InternetConnect");
        return FALSE;
    }
    bActiveSession = TRUE;
    lstrcpy (szServer, szHost);
    return TRUE;
}

BOOL Close ()
{
    if (!InternetCloseHandle (hConnect) )
    {
        ErrorOut (GetLastError(), "InternetCloseHandle");
        return FALSE;
    }
    bActiveSession = FALSE;
    return TRUE;
}
    
void FtpEnumDirectory ()
{
	WIN32_FIND_DATA pData;
	HINTERNET hFind;
	DWORD dError;

	// Start enumeration and get file handle
	if ( !(hFind = FtpFindFirstFile (hConnect, TEXT ("*.*"), &pData, 0, 0) ))
		if (GetLastError()  == ERROR_NO_MORE_FILES) 
		{
			cout << "This directory is empty" << endl;
			return ;
		}
		else
		{
			ErrorOut (GetLastError (), "FindFirst error: ");
			return ;
		}

	cout << pData.cFileName;
	if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
		// Entry is a directory, mark it as such
		cout << " <DIR> " << endl;
	else
		cout << endl;

	dError = NO_ERROR;
	do
	{
		 if (!InternetFindNextFile (hFind, &pData))
		 {
			 dError = GetLastError();
			 if ( dError == ERROR_NO_MORE_FILES ) 
				break;
			 else
			 {
				 ErrorOut (dError, "InternetFindNextFile");
				 return ;
			 }
		 }
		 else
		 {
				cout << pData.cFileName;
				if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
					cout << " <DIR> " << endl;
				else
					cout << endl;
			 }
	}
	while ( TRUE);
	if (!InternetCloseHandle (hFind) )
	{
		ErrorOut (GetLastError(), "InternetCloseHandle error");
		return ;
	}
	return ;
}


/****************************************************************************
*
*    FUNCTION: ErrorOut
*
*    PURPOSE: This function is used to get extended Internet error.
*
*    COMMENTS:  Function returns TRUE on success and FALSE on failure.
*
****************************************************************************/

BOOL ErrorOut ( DWORD dError, TCHAR * szCallFunc)
{
    TCHAR szTemp[100] = "", *szBuffer=NULL, *szBufferFinal = NULL;
    DWORD  dwIntError , dwLength = 0; 
    wsprintf (szTemp,  "%s error %d\n ", szCallFunc, dError );
	if (dError == ERROR_INTERNET_EXTENDED_ERROR)
	{
		InternetGetLastResponseInfo (&dwIntError, NULL, &dwLength);
		if (dwLength)
		{
			if ( !(szBuffer = (TCHAR *) LocalAlloc ( LPTR,  dwLength) ) )
			{
				lstrcat (szTemp, TEXT ( "Unable to allocate memory to display Internet error code. Error code: ") );
				lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
				lstrcat (szTemp, TEXT ("\n") );
				cerr << szTemp << endl;
				return FALSE;
			}
			if (!InternetGetLastResponseInfo (&dwIntError, (LPTSTR) szBuffer, &dwLength))
			{
				lstrcat (szTemp, TEXT ( "Unable to get Intrnet error. Error code: ") );
				lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
				lstrcat (szTemp, TEXT ("\n") );
				cerr << szTemp << endl;
				return FALSE;
			}
			if ( !(szBufferFinal = (TCHAR *) LocalAlloc ( LPTR,  (strlen (szBuffer) +strlen (szTemp) + 1)  ) )  )
			{
				lstrcat (szTemp, TEXT ( "Unable to allocate memory. Error code: ") );
				lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
				lstrcat (szTemp, TEXT ("\n") );
				cerr << szTemp << endl;
				return FALSE;
			}
			lstrcpy (szBufferFinal, szTemp);
			lstrcat (szBufferFinal, szBuffer);
			LocalFree (szBuffer);
			cerr <<  szBufferFinal  << endl;
			LocalFree (szBufferFinal);
		}
	}
	else
        cerr << szTemp << endl;
    return TRUE;
}
 