RE: FTP Solution

From: Santhosh Kutty (sakutty_at_online.microsoft.com)
Date: 05/14/04


Date: Fri, 14 May 2004 04:06:07 GMT

Hi Kochumon,
   You've not detailed the exact error you're facing but I'd like to put in
some suggestions and sample code that I'm sure would help you out. The
important thing to note is that WinInet FTP APIs do not work if Internet
access is accomplished via CERN type proxy. So if you've enabled it on the
server, you want to down to the my VC++ part below and look at code in that
respect. This is a combination of several KB articles and the code is a sim
in VC++. You might just want to pick out th logic and apply it to you case.
 You can refer KB articles 168492, 216214, 195650, 166961, 195653, 259100.
Please look to the end of this message for a listing of references I've
compiled for you. These were earlier pieces of code but covers a lot of the
fundamentals. You might just want to do a quick look through and pick out
the necessary parts. I hope this helps.

Another cause is that a keep-alive connection to the proxy is required for
the request to succeed. This connection is identified by the
"Proxy-Connection: Keep-Alive" header. Absence of this header will cause
authentication to fail. To resolve this problem, set the
INTERNET_FLAG_KEEP_CONNECTION flag when you call the HttpOpenRequest
WinInet API or add the "Proxy-Connection: Keep-Alive" header in the Execute
method of Internet Transfer Control. This is a VC execution step though.

Using FTP WinInet APIs in Visual Basic with SimpleFtp and how to enumerate
a directory on the FTP server and return file information such as creation
date and size.
There are two ways of uploading a file to the FTP server without blocking
the entire application and with reporting transfer progress:
By using the FtpPutFile() API. However, this API blocks until the entire
file has been uploaded. Upon clicking the Put button, the sample will use
this method.
By using FtpOpenFile and InternetWriteFile. Once the file is open it can be
uploaded in chunks. This enables the application to report upload status
and avoid blocking. It does this by calling DoEvents() between calling
InternetWriteFile. Upon clicking the Put Large File button, the sample will
use this method.
How to get text information for WinInet errors and how to retrieve extended
error information. For simplicity, the sample does not implement
downloading of the large files. This functionality is similar to method b)
above; however, you should use the InternetReadFile API instead of
InternetWriteFile.

I enclose a sample from KB 195653 -
http://download.microsoft.com/download/ie4095/vbsmpftp/1/w9xnt4/en-us/VBSMPF
TP.exe
This sample uses pre-configured access to the Internet.
This sample was created with Visual Basic 6.0. There may be an error if the
project is opened in Visual Basic5.0.
Vbsmpftp.exe contains the following files:
   ErrorForm.frm 1,216
   ErrorForm.frx 6
   readme.txt 1,869
   SimpleFtp.bas 5,364
   SimpleFtp.exe 40,960
   SimpleFtp.frm 13,491
   SimpleFtp.vbp 646
   SimpleFtp.vbw 118

Now for some fundamentals to help you understand CERN proxy, Kochumon:
The WinInet control uses the HTTP protocol to talk to the proxy, and the
proxy connects to the FTP server via FTP protocol. This fact makes it
impossible to use specific WinInet FTP APIs such as FtpOpenFile and
FtpPutFile.

However, it is still possible to obtain a directory listing of the FTP
server and download files using InternetOpenUrl. This protocol-independent
API is capable of taking both an FTP URL, such as ftp://server, or an HTTP
URL, such as http://server.

Under certain conditions, in addition to the FTP server requiring a set of
credentials (that is, the user name and password), the proxy server may
require a separate set of credentials. With InternetOpenUrl the user name
and password required by the FTP server may be include in the URL, like
this:
ftp://User:Pass@ftp.server.com/file.txt
Note This syntax is invalid for HTTP and does not allow a password to be
included with the "@" sign.

If you do not specify the FTP user name and password in the URL, the proxy
accesses the server with user "Anonymous" and some sort of generic password
(for example, Microsoft Proxy uses "proxyuser@microsoft.com" as the
password).

The technique below outlines steps that can be used to handle proxy
authentication. In other words it explains how to submit a second set of
credentials for the proxy itself.
1. Call InternetOpenUrl. If the FTP server requires a name and password,
include it in the URL.
2. Check if the handle returned by InternetOpenUrl is of type HTTP. If the
handle type is HTTP, it indicates that WinInet is communicating with HTTP
type proxy.
3. Check for the HTTP status code. If status code indicates that proxy
authentication is required, acquire user credentials.
4. The user name and password may be acquired and set with a standard user
interface by calling InternetErrorDlg. If you do not want to use the
standard user interface, you can just use InternetSetOption to set the user
name and password.
5. Once credentials are acquired, the request must be resubmitted with the
HttpSendRequest API.

Before the request is resubmitted, all outstanding data must be read with
the InternetReadFile function.
When InternetReadFile is used to obtain a directory listing (for a URL such
as ftp://server/MyDir), it may fail with error 122 "Insufficient buffer."
In this case the forth parameter to the function, lpNumberOfBytesRead, will
be set to 0 and will not indicate the size of the needed buffer. To
determine the size of the minimal buffer to allocate, call the
InternetQueryDataAvailable function.

Also WinInet applications attempting to access files through a proxy that
requires a login will fail unless the proxy is provided with a valid
username and password. Tthe different options available to handle this
situation are explained below.

For the sake of clarity, error handling has been removed from most of the
code in this article. If you use any of this code in your own program,
please implement error handling appropriately. As well, anywhere you find
"...", code has been removed. Please reference the HttpDump and Tear
samples for complete implementations of WinInet coding.

The following code snippet was taken from the HttpDump sample. It
illustrates how to capture a proxy authorization request
(HTTP_STATUS_PROXY_AUTH_REQ):

HttpSendRequest (hReq, NULL, 0, NULL, 0);
HttpQueryInfo (hReq, HTTP_QUERY_STATUS_CODE |
      HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwSize, NULL);

if (dwCode == HTTP_STATUS_PROXY_AUTH_REQ)
                                
A check for HTTP_STATUS_PROXY_AUTH_REQ can be added to the Tear MFC sample
to check for HTTP_STATUS_PROXY_AUTH_REQ:
pFile->SendRequest();
pFile->QueryInfoStatusCode(dwRet);

if (dwRet == HTTP_STATUS_PROXY_AUTH_REQ)
                                

Both samples can successfully handle HTTP_STATUS_PROXY_AUTH_REQ by
providing a user interface to collect the username and password for proxy
authorization. The HttpDump sample does so with the following code:
if ( InternetErrorDlg (GetDesktopWindow(),
      hReq, ERROR_INTERNET_INCORRECT_PASSWORD,
      FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
      FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
      FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
      NULL) == ERROR_INTERNET_FORCE_RETRY)
            goto again;

WinInet will query hReq to determine the type of error and in the case of
HTTP_STATUS_PROXY_AUTH_REQ, will present the user with the dialog box to
collect the username and password for proxy authorization.

MFC wraps the InternetErrorDlg call and will attempt the proxy
authorization with the following code:
dwPrompt = pFile->ErrorDlg(NULL, ERROR_INTERNET_INCORRECT_PASSWORD,
      FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
            FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, NULL);

Unfortunately, CHttpFile::ErrorDlg is broken in the MFC that ships with
Visual C++ versions prior to 6.0. For additional information on how to call
InternetErrorDlg from an MFC application, please see the following article
in the Microsoft Knowledge Base:
189094 Calling CHttpFile::ErrorDlg Function Causes Errors 127 & 2

The drawback of the using InternetErrorDlg is that the function call
displays a user interface. In some cases, this is not desirable.

There are several ways to handle HTTP_STATUS_PROXY_AUTH_REQ without
displaying a user interface. By far the easiest way to do this is by using
the InternetSetOption function with the flags
INTERNET_OPTION_PROXY_PASSWORD and INTERNET_OPTION_PROXY_USERNAME. This
option can be used only on clients that have Internet Explorer 4.0 or later
loaded as the WinInet that shipped before Internet Explorer 4.0 did not
implement this functionality. It will also be necessary to link with the
updated WinInet.h and WinInet.lib from the Internet Client SDK or Microsoft
Platform SDK. The following code illustrates how to implement this
functionality with straight WinInet:
if (dwCode == HTTP_STATUS_PROXY_AUTH_REQ)
   {
      // read the data off the request handle and setup buffer see
      // handler HttpDump handler for HTTP_STATUS_DENIED for details
      ...
 
      InternetSetOption (hConnect, INTERNET_OPTION_PROXY_USERNAME,
            (LPVOID) szUser, lstrlen (szUser);
      InternetSetOption (hConnect, INTERNET_OPTION_PROXY_PASSWORD,
            (LPVOID) szPass, lstrlen (szPass);
 
      // calls HttpSendRequest again - see HttpDump
      goto again;

A sample is available that implements a Base54 encryption algorithm. For
additional information, see the following article in the Microsoft
Knowledge Base:
KB191239 SAMPLE: Sample Base 64 Encoding and Decoding

The format of the username and password that needs to be encrypted is
"username:password" including the colon. Please reference RFC2068 -
"Hypertext Transfer Protocol -- HTTP/1.1" for further details on this.

MFC WinInet applications can implement the same functionality. To do so,
the application should call CHttpFile::AddRequestHeaders and add the Proxy-
Authorization header as above. The application should then resubmit the
request with CHttpFile::SendRequest.
REFERENCES
RFC 2068 / Hypertext Transfer Protocol -- HTTP/1.1

KB articles you want to refer at support.microsoft.com
KB166961 HOWTO: FTP with CERN-Based Proxy Using WinInet API
KB193625 WinInet Error Codes (12001 through 12156)
KB216214 SAMPLE: FTP with CERN-Based Password Protected Proxy
KB165298 HOWTO: Simulate a Form POST Request Using WinInet
KB195650 HOWTO: Handle Proxy Authorization with WinInet
KB254396 PRB: Cannot Connect Through Proxy Server that Requires NTLM
Authentication
KB168151 HOWTO: Make SSL Requests Using WinInet
KB182888 HOWTO: Handle Invalid Certificate Authority Error with WinInet
KB193625 WinInet Error Codes (12001 through 12156)
KB185519 FILE: Vbinet.exe WinInet API Declarations for Visual Basic
KB175179 FILE: VBFTP.EXE: Implementing FTP Using WinInet API from VB
KB195653 SAMPLTE: Using FTP WinInet APIs in Visual Basic with SimpleFtp

Santhosh James Kutty

Microsoft Partner Support Engineer

This posting is provided “AS IS” with no warranties, and confers no rights.
 

Get Secure! - www.microsoft.com/security