/*
A simple application of the internet sdk. An ftp client.
Adapted from the example provided by Microsoft Corp
*/
#include <windows.h>
#include <wininet.h>
#include <direct.h>
#include <stdio.h>
#include <string.h>
#define PROMPT  "FTP> "

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

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[])
{
	
	printf("FTP Sample Application\n");
	
        if ( !(hOpen = InternetOpen ( "FTP", LOCAL_INTERNET_ACCESS ,NULL,0,0)))
        {
            ErrorOut ( GetLastError(), "InternetOpen");
            return ;
        }

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

		printf(PROMPT);
		// Read line
		gets(szCmdLine);
		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)
{
    CHAR szFile1[256], szFile2[256];
    strupr(pszArg1);

	// Quit Command
	if (lstrcmp(pszArg1, "QUIT") == 0)
		return FALSE;
	
	// Get Command
	else if (lstrcmp(pszArg1, "GET") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter RemoteFile: ");
            gets(szFile1);
            printf("Enter local file: ");
            gets(szFile2);
            if (!FtpGetFile (hConnect,szFile1, szFile2, FALSE,INTERNET_FLAG_RELOAD, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpGetFile");
        }
        else
            printf("No active session\n") ;
	}
	
	// Put Command
	else if (lstrcmp(pszArg1, "PUT") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter LocalFile: ");
            gets(szFile1 );
            printf("Enter remote file: ");
            gets(szFile2);
            if (!FtpPutFile (hConnect,szFile1, szFile2, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpPutFile");
        }
        else
            printf("No active session\n") ;
	}
	
	// Del Command
	else if (lstrcmp(pszArg1, "DEL") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter RemoteFile : \n");
            gets(szFile1);
            if (!FtpDeleteFile (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpDelFile");
        }
        else
            printf("No active session\n");
	}
	
	// Ren Command
	else if (lstrcmp(pszArg1, "REN") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter ExistingFile NewFile : \n");
            gets(szFile1);
            gets(szFile2);
            if (!FtpRenameFile (hConnect,szFile1, szFile2 ) )
                ErrorOut (GetLastError(), "FtpRenameFile");
        }
        else
            printf("No active session\n");
	
	}
	
	// ls command
	else if (lstrcmp(pszArg1, "LS") == 0)
	{
	    if (bActiveSession)
            FtpEnumDirectory();
        else
            printf("No active session\n");
	}
	
	// md command
	else if (lstrcmp(pszArg1, "MD") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter Directory : \n");
            gets(szFile1);
            if (!FtpCreateDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpCreateDirectory");
        }
        else
            printf("No active session\n");
	}

	// rd command
	else if (lstrcmp(pszArg1, "RD") == 0)
	{
	    if (bActiveSession)
        {
            printf("Enter Directory : \n");
            gets(szFile1);
            if (!FtpRemoveDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpRemoveDirectory");
        }
        else
            printf("No active session\n");
	}

	// type command
	else if (lstrcmp(pszArg1, "TYPE") == 0)
	{
        if (bActiveSession)
            type (pszArg2);
        else
		printf("No active session\n");
	}   
    
	// cd command
	else if (lstrcmp(pszArg1, "CD") == 0)
	{
        if (bActiveSession)
            rcd (pszArg2);
        else
		printf("No active session\n");
	}

	// lcd command
	else if (lstrcmp(pszArg1, "LCD") == 0)
	{
		lcd(pszArg2);
	}
	
	// open command
	else if (lstrcmp(pszArg1, "OPEN") == 0)
	{
        if (!bActiveSession)
        {
    	    if (Open (pszArg2))
                printf("Connected to %s\n",pszArg2);
            else
                printf("Session closed\n");    
        } 
        else
            printf("Already connected to %s\n",szServer);
           
	}

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

void DisplayHelp(void)
{
	printf( "Commands are:\n");
	printf( "   cd\n");
	printf( "   close\n");
	printf( "   del\n");
	printf( "   get\n");
	printf( "   help\n");
	printf( "   lcd\n");
	printf( "   ls\n");
	printf( "   md\n");
	printf( "   open\n");
	printf( "   put\n");
	printf( "   quit\n");
	printf( "   rd\n");
	printf( "   ren\n");
	printf( "   type\n");
	printf( "\n");
}

void lcd(char *pszDir)
{
	if ((*pszDir != 0) && (chdir(pszDir) == -1))
		printf( "?Invalid Local Directory\n");
	else
	{
		char dir[256];
		GetCurrentDirectory(256, dir);
		printf( "Local Directory is %s\n");
	}
}

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

void type (char *szType)
{
    
	strupr(szType);    
	if (*szType != 0)
    {
	    if (lstrcmp(szType, "BIN") == 0)
        {
		    dwType = FTP_TRANSFER_TYPE_BINARY;
            printf( "BIN mode selected");
        }
    	else if (lstrcmp(szType, "ASCII") == 0)
        {
		    dwType = FTP_TRANSFER_TYPE_ASCII;
            printf( "ASCII mode selected");
        }
        else
            printf( "Invalid mode");
        printf("\n");    
    }    
	else
	{
		if ( dwType == FTP_TRANSFER_TYPE_BINARY)
            printf( "BIN ");
        else    
    		printf( "ASCII ");
        printf( "mode selected \n");    
	}
}

BOOL Open (CHAR * szHost)
{
    CHAR szUser[256], szPass[256];
    if ( *szHost == 0)
    {
        printf( "Enter host: ");
		gets(szHost);
                printf("User: ");
		gets(szUser);
                printf("Password: ");
		gets(szPass);
    }
    else
    {
        printf( "Enter user: ");
        gets(szUser);
        printf("Enter password: ");
        gets( szPass);
    }
    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;
    strcpy (szServer, szHost);
    return TRUE;
}

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

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

	printf( "%s",pData.cFileName);
	if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
		// Entry is a directory, mark it as such
		printf( " <DIR> \n");
	else
		printf("\n");

	dError = NO_ERROR;
	do
	{
		 if (!InternetFindNextFile (hFind, &pData))
		 {
			 dError = GetLastError();
			 if ( dError == ERROR_NO_MORE_FILES ) 
				break;
			 else
			 {
				 ErrorOut (dError, "InternetFindNextFile");
				 return ;
			 }
		 }
		 else
		 {
				printf("%s", pData.cFileName);
				if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
					printf( " <DIR> \n");
				else
					printf("\n");
			 }
	}
	while ( TRUE);
	if (!InternetCloseHandle (hFind) )
	{
		ErrorOut (GetLastError(), "InternetCloseHandle error");
		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; 
    sprintf (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, "Unable to allocate memory to display Internet error code. Error code: " );
				sprintf(szTemp + strlen(szTemp),"%d",GetLastError());
				lstrcat (szTemp, "\n" );
				fprintf(stderr,"%s\n",szTemp );
				return FALSE;
			}
			if (!InternetGetLastResponseInfo (&dwIntError, (LPTSTR) szBuffer, &dwLength))
			{
				lstrcat (szTemp, TEXT ( "Unable to get Intrnet error. Error code: ") );
				sprintf(szTemp+strlen(szTemp),"%d",GetLastError());
				lstrcat (szTemp, TEXT ("\n") );
				fprintf(stderr,"%s\n",szTemp);
				return FALSE;
			}
			if ( !(szBufferFinal = (TCHAR *) LocalAlloc ( LPTR,  (strlen (szBuffer) +strlen (szTemp) + 1)  ) )  )
			{
				lstrcat (szTemp, TEXT ( "Unable to allocate memory. Error code: ") );
				sprintf(szTemp+strlen(szTemp),"%d",GetLastError());
				lstrcat (szTemp, TEXT ("\n") );
				fprintf(stderr,"%s\n",szTemp);
				return FALSE;
			}
			lstrcpy (szBufferFinal, szTemp);
			lstrcat (szBufferFinal, szBuffer);
			LocalFree (szBuffer);
			fprintf(stderr,"%s\n",szBufferFinal);
			LocalFree (szBufferFinal);
		}
	}
	else
        fprintf(stderr,"%s\n",szTemp );
    return TRUE;
}
 
