Code archives/Networking/FTP Using WinInet Userlib Functions
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
This program uses the Windows Internet (WinInet) userlib to access FTP sites and transfer files. WinInet functions seem to be faster and more reliable than the TCP/IP alternatives using the native Blitz OpenTCPStream() command. | |||||
;===================== ;WinInet FTP Functions ;===================== ;By Patrick Lester (turtle1776) ;This program uses the Windows Internet (WinInet) userlib to access FTP sites and transfer files. ;WinInet functions seem to be faster and more reliable than the TCP/IP alternatives using the ;native Blitz OpenTCPStream() command. ;The list of functions can be found below in both the decls list and below that, where it says ;"Working WinInet Functions." These functions are used in the following, very simple FTP ;program. Obviously, a nicer Windows program, complete with bells and whistles, could be ;created with the basic elements included here. ;Reference ;----------- ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wininet/wininet/ftp_sessions.asp ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wininet/wininet/wininet_reference.asp ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wininet/wininet/wininet_errors.asp ;Add to decls file ;------------------ ;.lib "wininet.dll" ;FtpCreateDirectory%(hFTPSession%,lpszDirectory$):"FtpCreateDirectoryA" ;FtpDeleteFile%(hFTPSession%,lpszFileName$) : "FtpDeleteFileA" ;FtpFindFirstFile%(hFTPSession%,lpszFileName$,lpFindFileData*,dwFlags%,dwContext%) : "FtpFindFirstFileA" ;FtpGetCurrentDirectory%(hFTPSession%,lpszCurrentDirectory*,neededLength*):"FtpGetCurrentDirectoryA" ;FtpGetFile%(hFTPSession%,RemoteFile$,LocalFile$,fFailIfExists%,dwFlagsAndAttributes%,dwFlags%,dwContext%):"FtpGetFileA" ;FtpPutFile%(hFTPSession%,LocalFile$,NewRemoteFile$,dwFlags%,dwContext%):"FtpPutFileA" ;FtpRemoveDirectory%(hFTPSession%,lpszDirectory$) : "FtpRemoveDirectoryA" ;FtpRenameFile%(hFTPSession%,lpszExisting$,lpszNew$) : "FtpRenameFileA" ;FtpSetCurrentDirectory%(hFTPSession%,lpszDirectory$) : "FtpSetCurrentDirectoryA" ;InternetCloseHandle%(hInternet%):"InternetCloseHandle" ;InternetConnect%(hInternet%, ServerName$, ServerPort%, Username$, Password$, Service%, Flags%, dwContext%):"InternetConnectA" ;InternetFindNextFile%(hInternet%,lpvFindData*) : "InternetFindNextFileA" ;InternetOpen%(Agent$, AccessType%, ProxyName%, ProxyBypass%, Flags%):"InternetOpenA" ;InternetGetLastResponseInfo%(lpdwError*,lpszBuffer*,lpdwBufferLength*) : "InternetGetLastResponseInfoA" ;.lib "kernel32.dll" ;GetLastError%() : "GetLastError" ;======================================== ;Constants, globals, and types Const succeeded = 1, failed = -1 Const FTP_TRANSFER_TYPE_ASCII = 1 ;Used by FtpPutFile and FtpGetFile functions. Const FTP_TRANSFER_TYPE_BINARY = 2 ;Used by FtpPutFile and FtpGetFile functions. Const ERROR_NO_MORE_FILES = 18 ;Used by FtpFindFirstFile Const FILE_ATTRIBUTE_NORMAL = $80; = 128 -- Used by FtpGetFile function. Const INTERNET_FLAG_RELOAD = $80000000 ;Used by FtpFindFirstFile Global ghFTPSession, ghInternet, gCurrentDirectory$ Type FTPFile Field directory$ Field fileName$ Field typeOfFile Field sizeofFile ;in bytes End Type ;Full Program: Open, run and close FTP Session ;---------------------------------------------------- ;RunFTPSession("ftp.hq.nasa.gov","anonymous","anon@yahoo.com") RunFTPSession("ftp.gnu.org","anonymous","anon@yahoo.com") ;======================================== ;WORKING WININET FUNCTIONS: These functions are actually used elsewhere in the program. ;They are listed here so you can see what they look like all in one place. ;Functions that open and close FTP session (used by OpenFTPSession and CloseFTPSession) hInternet = InternetOpen("...", 0, 0, 0, 0) ghFTPSession = InternetConnect(hInternet ,"policyalmanac.org",21,login$,password$,1,$08000000,0) result = InternetCloseHandle(ghFTPSession) result = InternetCloseHandle(hInternet) ;File functions result = FtpDeleteFile(ghFTPSession,file$) result = FtpGetFile (ghFTPSession,remoteFile$,localFile$,0,FILE_ATTRIBUTE_NORMAL,FTP_TRANSFER_TYPE_BINARY,0) result = FTPGetFileList(directory$) result = FtpPutFile (ghFTPSession,localFile$,remoteFile$,FTP_TRANSFER_TYPE_BINARY,0) result = FtpRenameFile(ghFTPSession,existingFile$,newFileName$) ;also renames directories ;Directory functions result = FtpCreateDirectory%(ghFTPSession%,directory$) directory$ = FTPGetCurrDirectory$() result = FtpRemoveDirectory(ghFTPSession,directory$) result = FtpSetCurrentDirectory(ghFTPSession,directory$) error = GetLastError() ;======================================== ;BLITZ FUNCTIONS ;This function runs the FTP session until the user exits by typing 'x' Function RunFTPSession(host$,user$,password$) Print "Connecting to FTP server at "+ host$ + " ... (please wait)" If OpenFTPSession(host$,user$,password$) = failed error = GetLastError() Print "Error opening FTP session. Error = " + GetError(error) Print "Finished. Sorry." WaitKey() End End If gCurrentDirectory$ = FTPGetCurrDirectory$() If FTPGetFileList() = succeeded Then PrintRemoteFileList() Repeat myinput$ = Input$("Next Action (type h for help, x to exit program): ") Print " " ;Process user input If myInput$ = "x" Or myInput$ = "X" Then Exit ProcessUserInput(myInput$) ;Get and print current directory and current file list. gCurrentDirectory$ = FTPGetCurrDirectory$() If FTPGetFileList() = succeeded PrintRemoteFileList() Else error = GetLastError() Print "Error listing files. Error = " + GetError(error) End If If myinput$ = "h" Then PrintHelp() Forever Print "Closing FTP session ... (please wait)" CloseFTPSession() End Function ;This function conducts various actions based on what the user types in. ;This function illustrates how to access most of the WinInet functions. Function ProcessUserInput(myinput$) If myinput$ = ".." ;go up one level If gCurrentDirectory$ = "/" Then Return Repeat SecondToLastSlash = lastSlash lastSlash = x x = Instr (gCurrentDirectory$, "/",lastSlash+1) Until x = 0 newDirectory$ = Left (gCurrentDirectory$, SecondToLastSlash) If newDirectory$ = "" Then newDirectory$ = "/" result = FtpSetCurrentDirectory(ghFTPSession,newDirectory$) If result = 1 Return succeeded If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Get (download) a file Else If myinput$ = "g" remoteFile$ = Input$("Type a file in current directory listed above or x to cancel: ") If remoteFile$ = "" Or remoteFile$ = "x" Then Return transfer$ = Input$("Transfer in binary mode? If not sure, answer yes. If you answer no, it will be an ASCII transfer. (y/n): ") If Left (transfer$, 1) = "n" Or Left (transfer$, 1) = "N" transferType = FTP_TRANSFER_TYPE_ASCII Print "ASCII transfer" Else transferType = FTP_TRANSFER_TYPE_BINARY Print "Binary transfer." End If downloadToC$ = Input$("Download to C:\ drive? (y/n): ") If downloadToC$ = "" Or downloadToC$ = "y" Or downloadToC$ = "yes" localFile$ = "C:\"+remoteFile$ remoteFile$ = gCurrentDirectory$+remoteFile$ result = FtpGetFile (ghFTPSession,remoteFile$,localFile$,0,FILE_ATTRIBUTE_NORMAL,transferType,0) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If Else localDirectory$ = Input$("Please type the full path to the local directory: ") If Right$(localDirectory$,1) <> "\" And Right$(localDirectory$,1) <> "/" localDirectory$ = localDirectory$+"\" End If localFile$ = localDirectory$+remoteFile$ remoteFile$ = gCurrentDirectory$+remoteFile$ result = FtpGetFile (ghFTPSession,remoteFile$,localFile$,0,FILE_ATTRIBUTE_NORMAL,transferType,0) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If End If ;Put (upload) a file Else If myInput$ = "p" localFile$ = Input$("Type a local file starting with C:\ to upload it to this directory (or press x to cancel): ") If localFile$ = "" Or localFile$ = "x" Then Return remoteFile$ = gCurrentDirectory$+GetFileName$(localFile$) transfer$ = Input$("Transfer in binary mode? If not sure, answer yes. If you answer no, it will be an ASCII transfer. (y/n): ") If Left (transfer$, 1) = "n" Or Left (transfer$, 1) = "N" transferType = FTP_TRANSFER_TYPE_ASCII Print "ASCII transfer" Else transferType = FTP_TRANSFER_TYPE_BINARY Print "Binary transfer." End If result = FtpPutFile (ghFTPSession,localFile$,remoteFile$,transferType,0) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Delete a file Else If myInput$ = "d" remoteFile$ = Input$("Type a file in current directory listed above or x to cancel: ") If remoteFile$ = "" Or remoteFile$ = "x" Then Return remoteFile$ = gCurrentDirectory$+remoteFile$ result = FtpDeleteFile(ghFTPSession,remoteFile$) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Rename a file or directory Else If myInput$ = "r" existingFile$ = Input$("Type a file in current directory listed above or x to cancel: ") If existingFile$ = "" Or existingFile$ = "x" Then Return existingFile$ = gCurrentDirectory$+existingFile$ newFileName$ = Input$("Type a new name or x to cancel: ") If newFileName$ = "" Or newFileName$ = "x" Then Return newFileName$ = gCurrentDirectory$+newFileName$ result = FtpRenameFile(ghFTPSession,existingFile$,newFileName$) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Create a directory Else If myInput$ = "cd" newDirectory$ = Input$("Type a subdirectory name or x to cancel: ") If newDirectory$ = "" Or newDirectory$ = "x" Then Return newDirectory$ = gCurrentDirectory$+newDirectory+"/" result = FtpCreateDirectory%(ghFTPSession%,newDirectory$) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Delete a directory Else If myInput$ = "dd" directory$ = Input$("Type a subdirectory to delete or x to cancel: ") If directory$ = "" Or directory$ = "x" Then Return directory$ = gCurrentDirectory$+directory+"/" result = FtpRemoveDirectory(ghFTPSession,directory$) If result = 1 Then Print "Succeeded" If result <> 1 error = GetLastError() Print "Failed. Error = " + GetError(error) End If ;Stop in debug mode Else If myInput$ = "s" Stop ;Printing help. Else If myInput$ = "h" ;do nothing, handled in RunFTPSession() function ;Check to see if the user typed in a subdirectory name. If so, open it. Else For ftpfile.ftpfile = Each ftpfile If ftpfile\fileName$ = myinput$ If ftpfile\typeOfFile = 2 fullFile$ = gCurrentDirectory$ + myinput$ result = FtpSetCurrentDirectory(ghFTPSession,fullFile$) End If Return succeeded End If Next Print "No such subdirectory. Press h for help." Return failed End If End Function ;This function opens an FTP session Function OpenFTPSession(domain$,login$,password$) ghInternet = InternetOpen("...", 0, 0, 0, 0) If Not ghInternet Then Return failed ghFTPSession = InternetConnect(ghInternet,domain$,21,login$,password$,1,$08000000,0) If Not ghFTPSession Then Return failed Return succeeded End Function ;This function closes the current FTP session Function CloseFTPSession() InternetCloseHandle(ghFTPSession) InternetCloseHandle(ghInternet) End Function ;This function returns the current directory with a trailing slash / Function FTPGetCurrDirectory$() lpNeededLength = CreateBank(4) PokeInt lpNeededLength,0,200 lpCurrentDirectory = CreateBank(256) ;needs to be big enough to handle the string If FtpGetCurrentDirectory%(ghFTPSession,lpCurrentDirectory,lpNeededLength) = succeeded currentDirectory$=ReadAPIString$(lpCurrentDirectory) If Right$(currentDirectory$,1) <> "/" Then currentDirectory$ = currentDirectory$+"/" End If FreeBank lpNeededLength : FreeBank lpCurrentDirectory Return currentDirectory$ End Function ;This function iterates through the file in a given FTP directory ;and stores the file information in a type called ftpfile, which ;contains the file's directory, filename, type (1 = directory, 2 = file), ;and size in bytes. If no directory is specified, the function will use ;the current directory. Function FTPGetFileList(remoteDirectory$="") Delete Each ftpfile If remoteDirectory$ = "" Then remoteDirectory$ = gCurrentDirectory$ lpFindFileData = CreateBank (320) hInternet = FtpFindFirstFile(ghFTPSession,remoteDirectory$,lpFindFileData,INTERNET_FLAG_RELOAD,0) If hInternet = 0 FreeBank lpFindFileData If GetLastError() = ERROR_NO_MORE_FILES Then Return succeeded ;no files or subdirectories Return failed EndIf ;Iterate through the files in the directory and store each in a type. Repeat ftpfile.ftpfile = New ftpfile ftpfile\directory$ = remoteDirectory$ ftpfile\fileName$ = ReadAPIString$(lpFindFileData,44) If PeekInt(lpFindFileData,0) = 16 Then ftpfile\typeOfFile = 2 ;directory (FILE_ATTRIBUTE_DIRECTORY) If PeekInt(lpFindFileData,0) = 128 Then ftpfile\typeOfFile = 1 ;file (FILE_ATTRIBUTE_NORMAL) ftpfile\sizeofFile = PeekInt(lpFindFileData,32) ;nFileSizeLow is enough, accurate for files < 2.1 gigs (that's huge) Until InternetFindNextFile(hInternet,lpFindFileData) = 0 result = InternetCloseHandle(hInternet) FreeBank lpFindFileData Return succeeded End Function ;Technical Note: File information returned in the lpFindFileData bank is in the ;form of a WIN32_FIND_DATA structure. Data in this structure is in the following ;positions in the bank. See ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/win32_find_data_str.asp ;0-3 = dwFileAttributes ;4-11 = ftCreationTime; ;8-19 = ftLastAccessTime; ;20-27 = ftLastWriteTime;; ;28-31 = nFileSizeHigh ;32-35 = nFileSizeLow ;36-39 = dwReserved0 ;40-43 = dwReserved1 ;44+ = null terminated file name ;This function extracts the file name from an url or local file. Function GetFileName$(file$) Repeat x = Instr (file$, "\") file$ = Right (file$, Len (file$) - x) Until x = 0 Repeat x = Instr (file$, "/") file$ = Right (file$, Len (file$) - x) Until x = 0 Return file$ End Function ;This function retrieves and translates common error codes. A complete ;list can be found here: ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp ;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wininet/wininet/wininet_errors.asp Function GetError$(error) If error = 2 Then Return "ERROR_FILE_NOT_FOUND" If error = 3 Then Return "ERROR_PATH_NOT_FOUND" If error = 5 Then Return "ERROR_ACCESS_DENIED" If error = 18 Then Return "ERROR_NO_MORE_FILES" If error = 19 Then Return "ERROR_WRITE_PROTECT" If error = 87 Then Return "ERROR_INVALID_PARAMETER" If error = 12003 ; "ERROR_INTERNET_EXTENDED_ERROR" lpdwError = CreateBank (4) lpszBuffer = CreateBank (1000) lpdwBufferLength = CreateBank (4) PokeInt lpdwBufferLength,0,1001 result = InternetGetLastResponseInfo(lpdwError,lpszBuffer,lpdwBufferLength) If result = 1 Then errorString$="Extended error. " + ReadAPIString$(lpszBuffer) +" ("+PeekInt(lpdwError,0) + ")" If result = 0 Then errorString$="Extended error not returned. Error " + GetLastError() FreeBank lpdwError : FreeBank lpszBuffer : FreeBank lpdwBufferLength Return errorString$ End If If error = 12007 Then Return "ERROR_INTERNET_NAME_NOT_RESOLVED " If error = 12013 Then Return "ERROR_INTERNET_INCORRECT_USER_NAME" If error = 12014 Then Return "ERROR_INTERNET_INCORRECT_PASSWORD" If error = 12015 Then Return "ERROR_INTERNET_LOGIN_FAILURE" If error = 12030 Then Return "Internet connection has been terminated." ;ERROR_INTERNET_CONNECTION_ABORTED If error = 12031 Then Return "ERROR_INTERNET_CONNECTION_RESET" If error = 12110 Then Return "ERROR_FTP_TRANSFER_IN_PROGRESS" If error = 12111 Then Return "ERROR_FTP_DROPPED" If error = 12112 Then Return "ERROR_FTP_NO_PASSIVE_MODE" Return "Undefined error " + error ;if none of the above is true, return the number End Function ;This function reads a null-terminated string returned from API function ;to a given bank. The offset parameter is used if the string is part of a ;larger data structure, as is the case when this function is called from ;FTPGetFileList(). In that particular case, the string is stored in a larger ;data structure called WIN32_FIND_DATA. Function ReadAPIString$(bank,offset=0) size = BankSize(bank) For x = offset To (size-1) If PeekByte(bank,x) = 0 Then Exit ;null terminator found myString$ = myString$ + Chr$(PeekByte(bank,x)) Next Return myString$ End Function ;This function prints the file list obtained through FTPGetFileList. Function PrintRemoteFileList() Print "" Print "================================" Print "CURRENT DIRECTORY = " + gCurrentDirectory$ Print "" Print "Subdirectories"; Print "-----------------" For ftpfile.ftpfile = Each ftpfile If ftpfile\typeOfFile = 2 count = count + 1 Print ftpfile\fileName$ + " (" + ftpfile\sizeofFile + " bytes)" End If Next If count = 0 Then Print "None" Print "" Print "Files in directory" Print "------------------" count = 0 For ftpfile.ftpfile = Each ftpfile If ftpfile\typeOfFile = 1 count = count + 1 Print ftpfile\fileName$ + " (" + ftpfile\sizeofFile + " bytes)" End If Next If count = 0 Then Print "None" Print "" End Function ;This function prints out the commands. Function PrintHelp() Print "" Print "Commands" Print "-------------" Print "x = Exit program" Print "h = Help" Print "subdirectory name = Open subdirectory in current directory" Print ".. = Go up one directory level" Print "g = Get (download) a file." Print "p = Put (upload) a file" Print "d = Delete a file" Print "r = Rename a file or directory." Print "cd = Create a directory." Print "dd = Delete a directory (must be empty)." Print "" End Function |
Comments
| ||
There is no CHMOD possible?? Otherwise the library is great and fast ;) |
Code Archives Forum