You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
602 lines
14 KiB
602 lines
14 KiB
/*++
|
|
|
|
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 <gateway server>\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
|