/*++ 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 // // macros // #define IS_ARG(c) (((c) == '-') || ((c) == '/')) // // manifests // #define MAX_COMMAND_LENGTH 100 // // external data // extern BOOL fQuit; extern DWORD CacheFlags; // // data // DWORD Verbose = 0; INTERNET_STATUS_CALLBACK PreviousCallback; HINTERNET hCancel = NULL; BOOL AsyncMode = FALSE; BOOL fOffline = FALSE; DWORD Context = 0; DWORD AsyncResult = 0; DWORD AsyncError = 0; HANDLE AsyncEvent = NULL; BOOL UseQueryData = FALSE; #if DBG BOOL CheckHandleLeak = FALSE; #endif // // external functions // extern BOOL DispatchCommand(LPCTSTR, HANDLE); // // prototypes // void __cdecl main(int, char**); void __cdecl control_c_handler(int); void usage(void); BOOL Prompt(LPCTSTR, LPTSTR*); // // functions // void __cdecl 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 = INTERNET_OPEN_TYPE_PRECONFIG; BOOL expectingProxy = FALSE; LPSTR proxyServer = NULL; for (--argc, ++argv; argc; --argc, ++argv) { if (IS_ARG(**argv)) { switch (*++*argv) { case '?': usage(); break; case 'a': ++*argv; if ((**argv == 'l') || (**argv == 'd')) { accessMethod = INTERNET_OPEN_TYPE_DIRECT; } else if (**argv == 'p') { accessMethod = INTERNET_OPEN_TYPE_PROXY; if (*++*argv) { proxyServer = *argv; } else { expectingProxy = TRUE; } } 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 'n': CacheFlags |= INTERNET_FLAG_DONT_CACHE; break; case 'p': flags |= INTERNET_FLAG_PASSIVE; break; case 'q': UseQueryData = TRUE; break; case 'v': if (*++*argv == '\0') { Verbose = 1; } else { Verbose = atoi(*argv); } break; case 'x': Context = (DWORD)atoi(++*argv); break; case 'y': AsyncMode = TRUE; break; case 'o': fOffline = TRUE; break; default: printf("error: unrecognized command line flag: '%c'\n", **argv); usage(); } } else if (expectingProxy) { proxyServer = *argv; expectingProxy = 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, proxyServer, NULL, AsyncMode ? INTERNET_FLAG_ASYNC : 0 | (fOffline ? INTERNET_FLAG_OFFLINE : 0) ); if (hInternet == NULL) { print_error("ftpcat", "InternetOpen()"); exit(1); } else if (Verbose) { printf("Internet handle = %x\n", hInternet); hCancel = hInternet; } #if DBG if (CheckHandleLeak) { printf("after InternetOpen(): handle count = %d\n", GetProcessHandleCount()); } #endif if (enableCallbacks) { // // let's have a status callback // // Note that callbacks 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(hFtpSession); 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(hFtpSession); #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); } } if (Verbose) { printf("Closing %x\n", hFtpSession); } close_handle(hFtpSession); #if DBG if (CheckHandleLeak) { printf("after InternetCloseHandle(): handle count = %d\n", GetProcessHandleCount()); } #endif get_response(hFtpSession); #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 __cdecl control_c_handler(int sig) { // // disable signals // signal(SIGINT, SIG_IGN); // // cancel the current operation // if (Verbose) { printf("control-c handler\n"); } if (hCancel == NULL) { if (Verbose) { 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{[ ]server}|l|d|p}] [-c] [-d] [-n] [-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 callbacks\n" " -n = Don't cache\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 DWORD dwBytesRead; PTCHAR pch; lprintf(TEXT("%s"), pszPrompt); if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), 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_HANDLE_CLOSING: type$ = "HANDLE CLOSING"; 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" : (Context == FTPCAT_OPEN_CONTEXT) ? "Open" : "???", type$ ); if (Info) { if ((Status == INTERNET_STATUS_HANDLE_CREATED) || (Status == INTERNET_STATUS_HANDLE_CLOSING)) { printf("%x", *(LPHINTERNET)Info); } else if (Length == sizeof(DWORD)) { printf("%d", *(LPDWORD)Info); } else { printf(Info); } } putchar('\n'); } if (Status == INTERNET_STATUS_REQUEST_COMPLETE) { get_response(Handle); if (AsyncMode) { SetEvent(AsyncEvent); } else { printf("error: INTERNET_STATUS_REQUEST_COMPLETE returned. Not async\n"); } } } void close_handle(HINTERNET handle) { if (Verbose) { printf("closing handle %#x\n", 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