/*++ Copyright (c) 1994 Microsoft Corporation Module Name: ftpcat.c Abstract: Windows Internet API FTP test program Provides the same functionality as a cut-down version of the venerable (console-mode) ftp program Author: Richard L Firth (rfirth) 05-Jun-1995 Environment: Win32 user-mode console app Revision History: 05-Jun-1995 rfirth Created --*/ #include "ftpcatp.h" #undef tolower #ifndef _CRTAPI1 #define _CRTAPI1 #endif // // macros // #define IS_ARG(c) (((c) == '-') || ((c) == '/')) // // manifests // #define MAX_COMMAND_LENGTH 100 // // external data // extern BOOL fQuit; // // data // BOOL Verbose = FALSE; INTERNET_STATUS_CALLBACK PreviousCallback; HINTERNET hCancel = NULL; BOOL AsyncMode = FALSE; DWORD Context = 0; DWORD AsyncResult = 0; DWORD AsyncError = 0; HANDLE AsyncEvent = NULL; #if DBG BOOL CheckHandleLeak = FALSE; #endif // // external functions // extern BOOL DispatchCommand(LPCTSTR, HANDLE); // // prototypes // void _CRTAPI1 main(int, char**); void _CRTAPI1 control_c_handler(int); void usage(void); BOOL Prompt(LPCTSTR, LPTSTR*); // // functions // void _CRTAPI1 main(int argc, char** argv) { LPTSTR ptszSite = NULL; LPTSTR ptszUser = NULL; LPTSTR ptszPass = NULL; HINTERNET hInternet; HINTERNET hFtpSession; DWORD dwLocalAccessFlags; LPTSTR lpszCmd; DWORD lastError; DWORD bufLen; BOOL enableCallbacks = FALSE; DWORD flags = 0; DWORD accessMethod = PRE_CONFIG_INTERNET_ACCESS; BOOL expectingGatewayServer = FALSE; LPSTR gatewayServer = NULL; for (--argc, ++argv; argc; --argc, ++argv) { if (IS_ARG(**argv)) { switch (*++*argv) { case '?': usage(); break; case 'a': ++*argv; if (**argv == 'g') { accessMethod = GATEWAY_INTERNET_ACCESS; if (*++*argv) { gatewayServer = *argv; } else { expectingGatewayServer = TRUE; } } else if ((**argv == 'l') || (**argv == 'd')) { accessMethod = LOCAL_INTERNET_ACCESS; } else if (**argv == 'p') { accessMethod = CERN_PROXY_INTERNET_ACCESS; } else { printf("error: unrecognised access type: '%c'\n", **argv); usage(); } break; case 'c': enableCallbacks = TRUE; break; #if DBG case 'l': CheckHandleLeak = TRUE; break; #endif case 'p': flags |= INTERNET_FLAG_PASSIVE; break; case 'v': Verbose = TRUE; break; case 'x': Context = (DWORD)atoi(++*argv); break; case 'y': AsyncMode = TRUE; break; default: printf("error: unrecognized command line flag: '%c'\n", **argv); usage(); } } else if (expectingGatewayServer) { gatewayServer = *argv; expectingGatewayServer = FALSE; } else if (ptszSite == NULL) { ptszSite = *argv; } else if (ptszUser == NULL) { ptszUser = *argv; } else if (ptszPass == NULL) { ptszPass = *argv; } else { printf("error: unrecognized command line argument: \"%s\"\n", *argv); usage(); } } if (ptszSite == NULL) { printf("error: required server name argument missing\n"); exit(1); } if (AsyncMode) { // // create auto-reset, initially unsignalled event // AsyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (AsyncEvent == NULL) { print_error("ftpcat", "CreateEvent()"); exit(1); } } // // add a control-c handler // signal(SIGINT, control_c_handler); #if DBG if (CheckHandleLeak) { printf("initial handle count = %d\n", GetProcessHandleCount()); } #endif #if DBG if (CheckHandleLeak && Verbose) { printf("Initial handle count = %d\n", GetProcessHandleCount()); } #endif if (Verbose) { printf("calling InternetOpen()...\n"); } hInternet = InternetOpen("ftpcat", accessMethod, gatewayServer, 0, AsyncMode ? INTERNET_FLAG_ASYNC : 0 ); if (hInternet == NULL) { print_error("ftpcat", "InternetOpen()"); exit(1); } else if (Verbose) { printf("Internet handle = %x\n", hInternet); } #if DBG if (CheckHandleLeak) { printf("after InternetOpen(): handle count = %d\n", GetProcessHandleCount()); } #endif if (enableCallbacks) { // // let's have a status callback // // Note that call-backs can be set even before we have opened a handle // to the internet/gateway // PreviousCallback = InternetSetStatusCallback(hInternet, my_callback); if (PreviousCallback == INTERNET_INVALID_STATUS_CALLBACK) { print_error("ftpcat", "InternetSetStatusCallback()"); } else if (Verbose) { printf("previous Internet callback = %x\n", PreviousCallback); } } if (Verbose) { printf("calling InternetConnect()...\n"); } hFtpSession = InternetConnect(hInternet, ptszSite, 0, ptszUser, ptszPass, INTERNET_SERVICE_FTP, flags, FTPCAT_CONNECT_CONTEXT ); if ((hFtpSession == NULL) && AsyncMode) { if (Verbose) { printf("waiting for async InternetConnect()...\n"); } WaitForSingleObject(AsyncEvent, INFINITE); hFtpSession = (HINTERNET)AsyncResult; SetLastError(AsyncError); } if (hFtpSession == NULL) { if (AsyncMode) { SetLastError(AsyncError); } print_error("ftpcat", "%sInternetConnect()", AsyncMode ? "async " : "" ); get_response(); close_handle(hInternet); exit(1); } else if (Verbose) { printf("FTP session handle = %x\n", hFtpSession); } #if DBG if (CheckHandleLeak) { printf("after InternetConnect(): handle count = %d\n", GetProcessHandleCount()); } #endif printf("Connected to %s.\n", ptszSite); get_response(); #if DBG if (CheckHandleLeak) { printf("after InternetGetLastResponseInfo(): handle count = %d\n", GetProcessHandleCount()); } #endif // // set the (top level) cancellable handle // hCancel = hFtpSession; while (!fQuit) { if (Prompt(TEXT("ftp> "), &lpszCmd)) { DispatchCommand(lpszCmd, hFtpSession); } } printf("Closing %x\n", hFtpSession); close_handle(hFtpSession); #if DBG if (CheckHandleLeak) { printf("after InternetCloseHandle(): handle count = %d\n", GetProcessHandleCount()); } #endif get_response(); #if DBG if (CheckHandleLeak) { printf("after InternetGetLastResponseInfo(): handle count = %d\n", GetProcessHandleCount()); } #endif close_handle(hInternet); #if DBG if (CheckHandleLeak) { printf("after InternetCloseHandle(): handle count = %d\n", GetProcessHandleCount()); } if (CheckHandleLeak && Verbose) { printf("Final handle count = %d\n", GetProcessHandleCount()); } #endif exit(0); } void _CRTAPI1 control_c_handler(int sig) { // // disable signals // signal(SIGINT, SIG_IGN); // // cancel the current operation // printf("control-c handler\n"); if (hCancel == NULL) { char ch; printf("control-c handler: no Internet operation in progress\n"); } else { close_handle(hCancel); } // // re-enable this signal handler // signal(SIGINT, control_c_handler); } void usage() { printf("\n" "usage: ftpcat [-a{g{[ ]gateway server}|l|d|p}] [-c] [-d] [-p] [-v] [-x#] [-y]\n" " {servername} [username] [password]\n" "\n" "where: -a = access type. Default is pre-configured:\n" " g = gateway access via \n" " l = local internet access\n" " d = local internet access\n" " p = CERN proxy access\n" " -c = Enable call-backs\n" " -p = Use Passive transfer mode\n" " -v = Verbose mode. Default is off\n" " -x = Set context value\n" " -y = Asynchronous APIs\n" ); exit(1); } BOOL Prompt( IN LPCTSTR pszPrompt, OUT LPTSTR *ppszCommand ) { static CHAR Command[MAX_COMMAND_LENGTH + sizeof(TEXT('\0'))]; #ifdef UNICODE static WCHAR wchBuf[MAX_COMMAND_LENGTH + sizeof(L'\0')]; #endif HANDLE hStdin; DWORD dwBytesRead; PTCHAR pch; lprintf(TEXT("%s"), pszPrompt); hStdin = GetStdHandle(STD_INPUT_HANDLE); if (!ReadFile(hStdin, Command, MAX_COMMAND_LENGTH * sizeof(CHAR), &dwBytesRead, NULL)) { return FALSE; } Command[dwBytesRead] = '\0'; #ifdef UNICODE wsprintf(wchBuf, L"%S", Command); *ppszCommand = wchBuf; #else *ppszCommand = Command; #endif pch = lstrchr(*ppszCommand, TEXT('\r')); if (pch) { *pch = TEXT('\0'); } return TRUE; } VOID my_callback( HINTERNET Handle, DWORD Context, DWORD Status, LPVOID Info, DWORD Length ) { char* type$; switch (Status) { case INTERNET_STATUS_RESOLVING_NAME: type$ = "RESOLVING NAME"; break; case INTERNET_STATUS_NAME_RESOLVED: type$ = "NAME RESOLVED"; break; case INTERNET_STATUS_CONNECTING_TO_SERVER: type$ = "CONNECTING TO SERVER"; break; case INTERNET_STATUS_CONNECTED_TO_SERVER: type$ = "CONNECTED TO SERVER"; break; case INTERNET_STATUS_SENDING_REQUEST: type$ = "SENDING REQUEST"; break; case INTERNET_STATUS_REQUEST_SENT: type$ = "REQUEST SENT"; break; case INTERNET_STATUS_RECEIVING_RESPONSE: type$ = "RECEIVING RESPONSE"; break; case INTERNET_STATUS_RESPONSE_RECEIVED: type$ = "RESPONSE RECEIVED"; break; case INTERNET_STATUS_CLOSING_CONNECTION: type$ = "CLOSING CONNECTION"; break; case INTERNET_STATUS_CONNECTION_CLOSED: type$ = "CONNECTION CLOSED"; break; case INTERNET_STATUS_HANDLE_CREATED: type$ = "HANDLE CREATED"; hCancel = *(LPHINTERNET)Info; break; case INTERNET_STATUS_REQUEST_COMPLETE: type$ = "REQUEST COMPLETE"; AsyncResult = ((LPINTERNET_ASYNC_RESULT)Info)->dwResult; AsyncError = ((LPINTERNET_ASYNC_RESULT)Info)->dwError; break; default: type$ = "???"; break; } if (Verbose) { printf("callback: handle %x [context %x [%s]] %s ", Handle, Context, (Context == FTPCAT_CONNECT_CONTEXT) ? "Connect" : (Context == FTPCAT_FIND_CONTEXT) ? "Find" : (Context == FTPCAT_FILE_CONTEXT) ? "File" : (Context == FTPCAT_GET_CONTEXT) ? "Get" : (Context == FTPCAT_PUT_CONTEXT) ? "Put" : (Context == FTPCAT_COMMAND_CONTEXT) ? "Command" : "???", type$ ); } if (Info) { if (Status == INTERNET_STATUS_HANDLE_CREATED) { if (Verbose) { printf("%x", *(LPHINTERNET)Info); } } else if (Status == INTERNET_STATUS_REQUEST_COMPLETE) { // // nothing // } else if (Length == sizeof(DWORD)) { if (Verbose) { printf("%d", *(LPDWORD)Info); } } else if (Verbose) { printf(Info); } } if (Verbose) { putchar('\n'); } if (Status == INTERNET_STATUS_REQUEST_COMPLETE) { get_response(); if (AsyncMode) { SetEvent(AsyncEvent); } else { printf("error: INTERNET_STATUS_REQUEST_COMPLETE returned. Not async\n"); } } } void close_handle(HINTERNET handle) { if (!InternetCloseHandle(handle)) { print_error("close_handle", "InternetCloseHandle(%x)", handle); } } #if DBG DWORD GetProcessHandleCount() { DWORD error; DWORD count; DWORD countSize; countSize = sizeof(count); if (!InternetQueryOption(NULL, INTERNET_OPTION_GET_HANDLE_COUNT, &count, &countSize )) { print_error("GetProcessHandleCount", "InternetQueryOption()"); return 0; } return count; } #endif