Advertisement
Guest User

CFTPclient.cls

a guest
Dec 28th, 2017
395
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Option Explicit
  2.  
  3. ' //********************************************\\
  4. ' |**********************************************|
  5. ' |* CFTPclient: wininet ftp functions wrapper  *|
  6. ' |*             needs IE version 4 or above    *|
  7. ' |**********************************************|
  8. ' \\********************************************//
  9.  
  10. ' FTP transfer modes
  11. Public Enum enFtpFileMode
  12.     ffm_Ascii = 1&
  13.     ffm_Binary = 2&
  14. End Enum
  15.  
  16. ' Public events
  17. Public Event Message(ByVal sMsg As String)
  18. Public Event DirLine(ByVal vAttr As VbFileAttribute, ByVal dtDateTime As Date, ByVal dSize As Double, ByVal sName As String)
  19. Public Event Progress(ByVal lCurrent As Long, ByVal lTotal As Long)
  20. Public Event Failure(ByVal lCode As Long, ByVal sMessage As String)
  21.  
  22. ' Private FTP session data
  23. Private Type tag_FtpSession
  24.     sServer         As String   ' server name/ip
  25.    nPort           As Long     ' server port
  26.    sUser           As String   ' username
  27.    sPassword       As String   ' password
  28.    bPassive        As Boolean  ' true=passive mode
  29.    hInternet       As Long     ' internet session handle
  30.    hConnect        As Long     ' ftp session handle
  31.    hCtx            As Long     ' context (not used)
  32.    dwLastError     As Long     ' last error #
  33.    szLastError     As String   ' last error msg
  34. End Type
  35.  
  36. ' Storage area
  37. Private mFTP        As tag_FtpSession     ' current session
  38. Private msAgent     As String             ' user agent string
  39. Private mbDebug     As Boolean            ' true=enable debug/trace
  40.  
  41. ' Type of internet connection
  42. Private Enum INTERNET_OPEN_TYPE
  43.     INTERNET_OPEN_TYPE_PRECONFIG = 0                     ' use system (IE) configuration
  44.    INTERNET_OPEN_TYPE_DIRECT = 1                        ' direct to net
  45.    INTERNET_OPEN_TYPE_PROXY = 3                         ' via named proxy
  46.    INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4   ' prevent using java/script/INS
  47. End Enum
  48.  
  49. ' Misc constants
  50. Private Const INTERNET_INVALID_PORT_NUMBER = 0           ' invalid port
  51. Private Const INTERNET_DEFAULT_FTP_PORT = 21             ' default for FTP servers
  52. Private Const INTERNET_SERVICE_FTP = 1                   ' FTP service type selector
  53. Private Const INTERNET_FLAG_PASSIVE = &H8000000          ' passive FTP connections
  54. Private Const INTERNET_FLAG_RELOAD = &H80000000          ' avoid data caching
  55.  
  56. Private Const MAX_PATH = 260                             ' max path/file size
  57. Private Const ERROR_NO_MORE_FILES = 18                   ' end of dir listing
  58.  
  59. ' FileTime (see below)
  60. Private Type FILETIME
  61.     dwLowDateTime       As Long
  62.     dwHighDateTime      As Long
  63. End Type
  64.  
  65. ' Struct used for dir enumeration
  66. Private Type WIN32_FIND_DATA
  67.     dwFileAttributes    As Long
  68.     ftCreationTime      As FILETIME
  69.     ftLastAccessTime    As FILETIME
  70.     ftLastWriteTime     As FILETIME
  71.     nFileSizeHigh       As Long
  72.     nFileSizeLow        As Long
  73.     dwReserved0         As Long
  74.     dwReserved1         As Long
  75.     cFileName           As String * MAX_PATH
  76.     cAlternate          As String * 14
  77. End Type
  78.  
  79. ' Used to convert filetime to vb time
  80. Private Type SYSTEMTIME
  81.     wYear               As Integer
  82.     wMonth              As Integer
  83.     wDayOfWeek          As Integer
  84.     wDay                As Integer
  85.     wHour               As Integer
  86.     wMinute             As Integer
  87.     wSecond             As Integer
  88.     wMilliseconds       As Integer
  89. End Type
  90.  
  91. ' Directory data in VB format
  92. Private Type VB_FIND_DATA
  93.     vFileAttributes     As VbFileAttribute
  94.     dtCreationTime      As Date
  95.     dtLastAccessTime    As Date
  96.     dtLastWriteTime     As Date
  97.     dFileSize           As Double
  98.     sFileName           As String
  99.     sAlternate          As String
  100. End Type
  101.  
  102. ' Date/Time and memory manipulation
  103. Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (lpFILETIME As FILETIME, lpLocalFileTime As FILETIME) As Long
  104. Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFILETIME As FILETIME, lpSystemTime As SYSTEMTIME) As Long
  105. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long)
  106.  
  107. Private Const FORMAT_MESSAGE_FROM_HMODULE = &H800
  108.  
  109. ' Error messages decoding
  110. Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, ByVal lpSource As Long, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, dwArguments As Long) As Long
  111. Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpLibFileName As String) As Long
  112.  
  113. ' FTP transfer modes (private)
  114. Private Enum FTP_FILE_OPEN_MODE
  115.     GENERIC_READ = &H80000000                           ' file read mode (download)
  116.    GENERIC_WRITE = &H40000000                          ' file write mode (upload)
  117. End Enum
  118.  
  119. ' FTP transfer types (private)
  120. Private Enum FTP_TRANSFER_TYPE
  121.     FTP_TRANSFER_TYPE_UNKNOWN = &H0&                    ' unknown (binary)
  122.    FTP_TRANSFER_TYPE_ASCII = &H1&                      ' ASCII
  123.    FTP_TRANSFER_TYPE_BINARY = &H2&                     ' binary
  124.    FTP_TRANSFER_NO_CACHE = &H80000000                  ' no caching
  125. End Enum
  126.  
  127.  
  128. ' open/close an internet session
  129. Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" (ByVal lpszAgent As String, ByVal dwAccessType As INTERNET_OPEN_TYPE, ByVal lpszProxyName As String, ByVal lpszProxyBypass As String, ByVal dwFlags As Long) As Long
  130. Private Declare Function InternetClose Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
  131.  
  132. ' last error code/message
  133. Private Declare Function InternetError Lib "wininet.dll" Alias "InternetGetLastResponseInfoA" (lpdwError As Long, ByVal lpszBuffer As String, lpdwBufferLength As Long) As Long
  134.  
  135. ' connect/disconnect an ftp server
  136. Private Declare Function FtpConnect Lib "wininet.dll" Alias "InternetConnectA" (ByVal hInternet As Long, ByVal lpszServerName As String, ByVal nServerPort As Long, ByVal lpszUserName As String, ByVal lpszPassword As String, ByVal dwService As Long, ByVal dwFlags As Long, dwContext As Long) As Long
  137. Private Declare Function FtpDisconnect Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
  138.  
  139. ' directory operations
  140. Private Declare Function FtpQueryDir Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" (ByVal hConnect As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Long
  141. Private Declare Function FtpChangeDir Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
  142. Private Declare Function FtpCreateDir Lib "wininet.dll" Alias "FtpCreateDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
  143. Private Declare Function FtpRemoveDir Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hConnect As Long, ByVal lpszDirectory As String) As Long
  144. Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" (ByVal hConnect As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, dwContext As Long) As Long
  145. Private Declare Function FtpFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpFindFileData As WIN32_FIND_DATA) As Long
  146. Private Declare Function FtpFindClose Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
  147.  
  148. ' file operations
  149. Private Declare Function FtpOpenFile Lib "wininet.dll" Alias "FtpOpenFileA" (ByVal hConnect As Long, ByVal lpszFileName As String, ByVal dwAccess As FTP_FILE_OPEN_MODE, ByVal dwFlags As FTP_TRANSFER_TYPE, dwContext As Long) As Long
  150. Private Declare Function FtpFileSize Lib "wininet.dll" Alias "FtpGetFileSize" (ByVal hFile As Long, lpdwFileSizeHigh As Long) As Long
  151. Private Declare Function FtpBuffSize Lib "wininet.dll" Alias "InternetQueryDataAvailable" (ByVal hFile As Long, lpdwNumberOfBytesAvailable As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Long
  152. Private Declare Function FtpReadFile Lib "wininet.dll" Alias "InternetReadFile" (ByVal hFile As Long, ByRef lpBuffer As Byte, ByVal dwNumberOfBytesToRead As Long, lpdwNumberOfBytesRead As Long) As Long
  153. Private Declare Function FtpWriteFile Lib "wininet.dll" Alias "InternetWriteFile" (ByVal hFile As Long, ByRef lpBuffer As Byte, ByVal dwNumberOfBytesToWrite As Long, lpdwNumberOfBytesWritten As Long) As Long
  154. Private Declare Function FtpCloseFile Lib "wininet.dll" Alias "InternetCloseHandle" (ByVal hInternet As Long) As Long
  155. Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" (ByVal hConnect As Long, ByVal lpszFileName As String) As Long
  156. Private Declare Function FtpRenameFile Lib "wininet.dll" Alias "FtpRenameFileA" (ByVal hConnect As Long, ByVal lpszExisting As String, ByVal lpszNew As String) As Long
  157.  
  158. ' debugging
  159. Private Declare Sub OutputDebugString Lib "kernel32" Alias "OutputDebugStringA" (ByVal lpOutputString As String)
  160.  
  161. ' .:::: Init instance
  162. Private Sub Class_Initialize()
  163.     ' setup some reasonable defaults
  164.    With mFTP
  165.         .sServer = "ftp.cisco.com"
  166.         .nPort = INTERNET_DEFAULT_FTP_PORT
  167.         .sUser = "anonymous"
  168.         .sPassword = "guest@example.com"
  169.         .bPassive = True
  170.         .hInternet = 0
  171.         .hConnect = 0
  172.         .hCtx = 0
  173.         .dwLastError = 0
  174.         .szLastError = ""
  175.     End With
  176.     msAgent = App.EXEName & "(" & App.Major & ":" & App.Minor & ":" & App.Revision & ")"
  177.     mbDebug = False
  178. End Sub
  179.  
  180. ' .:::: Release instance
  181. Private Sub Class_Terminate()
  182.     On Local Error Resume Next
  183.     Disconnect
  184. End Sub
  185.  
  186. ' .:::: Server name or IP address
  187. Public Property Let Server(ByVal sServer As String)
  188.     mFTP.sServer = sServer
  189. End Property
  190.  
  191. Public Property Get Server() As String
  192.     Server = mFTP.sServer
  193. End Property
  194.  
  195. ' .:::: server port #
  196. Public Property Let Port(ByVal nPort As Long)
  197.     mFTP.nPort = nPort
  198. End Property
  199.  
  200. Public Property Get Port() As Long
  201.     Port = mFTP.nPort
  202. End Property
  203.  
  204. ' .:::: logon user name
  205. Public Property Let User(ByVal sUser As String)
  206.     mFTP.sUser = sUser
  207. End Property
  208.  
  209. Public Property Get User() As String
  210.     User = mFTP.sUser
  211. End Property
  212.  
  213. ' .:::: logon password
  214. Public Property Let Password(ByVal sPassword As String)
  215.     mFTP.sPassword = sPassword
  216. End Property
  217.  
  218. Public Property Get Password() As String
  219.     Password = mFTP.sPassword
  220. End Property
  221.  
  222. ' .:::: passive mode
  223. Public Property Let Passive(ByVal bYesNo As Boolean)
  224.     mFTP.bPassive = bYesNo
  225. End Property
  226.  
  227. Public Property Get Passive() As Boolean
  228.     Passive = mFTP.bPassive
  229. End Property
  230.  
  231. ' .:::: user agent string
  232. Public Property Let UserAgent(ByVal sAgent As String)
  233.     msAgent = sAgent
  234. End Property
  235.  
  236. Public Property Get UserAgent() As String
  237.     UserAgent = msAgent
  238. End Property
  239.  
  240. ' .:::: debug/trace mode
  241. Public Property Let DebugMode(ByVal bYesNo As Boolean)
  242.     mbDebug = bYesNo
  243. End Property
  244.  
  245. Public Property Get DebugMode() As Boolean
  246.     DebugMode = mbDebug
  247. End Property
  248.  
  249. ' .:::: Connect and logon to ftp server
  250. Public Function Connect() As Boolean
  251.     Dim lErrNo As Long, lFlags As Long
  252.     Dim bRet As Boolean
  253.    
  254.     On Local Error Resume Next
  255.     bRet = False
  256.     Connect = bRet
  257.    
  258.     DbgTrace "Connect " & mFTP.sServer
  259.    
  260.     Err.Clear
  261.     mFTP.hInternet = InternetOpen(msAgent, INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0&)
  262.     If (mFTP.hInternet = 0) Then
  263.         DecodeError
  264.         Disconnect
  265.         Exit Function
  266.     End If
  267.    
  268.     lFlags = IIf(mFTP.bPassive, INTERNET_FLAG_PASSIVE, 0&)
  269.     Err.Clear
  270.     mFTP.hConnect = FtpConnect(mFTP.hInternet, mFTP.sServer, mFTP.nPort, mFTP.sUser, mFTP.sPassword, INTERNET_SERVICE_FTP, lFlags, mFTP.hCtx)
  271.     If (mFTP.hConnect = 0) Then
  272.         DecodeError
  273.         Disconnect
  274.         Exit Function
  275.     End If
  276.     FtpResponse
  277.     bRet = True
  278.    
  279.     Connect = bRet
  280. End Function
  281.  
  282. ' .:::: Disconnect
  283. Public Sub Disconnect()
  284.     On Local Error Resume Next
  285.     DbgTrace "Disconnect"
  286.     If mFTP.hConnect <> 0 Then
  287.         Call FtpDisconnect(mFTP.hConnect)
  288.         FtpResponse
  289.         mFTP.hConnect = 0
  290.     End If
  291.     If mFTP.hInternet <> 0 Then
  292.         Call InternetClose(mFTP.hInternet)
  293.         mFTP.hInternet = 0
  294.     End If
  295. End Sub
  296.  
  297. ' .:::: read current dir path
  298. Public Function GetCurrentDirectory() As String
  299.     Dim sDir As String, lSize As Long, bRet As Boolean
  300.    
  301.     On Local Error Resume Next
  302.     lSize = 2048
  303.     sDir = String(lSize, vbNullChar)
  304.     bRet = FtpQueryDir(mFTP.hConnect, sDir, lSize)
  305.     If bRet = False Then
  306.         DecodeError
  307.         sDir = vbNullString
  308.     Else
  309.         FtpResponse
  310.     End If
  311.     sDir = GetSZ(sDir)
  312.     DbgTrace "GetCurrentDirectory = " & sDir
  313.     GetCurrentDirectory = sDir
  314. End Function
  315.  
  316. ' .:::: set current dir path
  317. Public Function SetCurrentDirectory(ByVal sDirectory As String) As Boolean
  318.     Dim bRet As Boolean
  319.  
  320.     On Local Error Resume Next
  321.     DbgTrace "SetCurrentDirectory " & sDirectory
  322.     bRet = FtpChangeDir(mFTP.hConnect, sDirectory)
  323.     If bRet = False Then
  324.         DecodeError
  325.     Else
  326.         FtpResponse
  327.     End If
  328.     SetCurrentDirectory = bRet
  329. End Function
  330.  
  331. ' .:::: create a directory
  332. Public Function CreateDirectory(ByVal sDirectory As String) As Boolean
  333.     Dim bRet As Boolean
  334.    
  335.     On Local Error Resume Next
  336.     DbgTrace "CreateDirectory " & sDirectory
  337.     bRet = FtpCreateDir(mFTP.hConnect, sDirectory)
  338.     If bRet = False Then
  339.         DecodeError
  340.     Else
  341.         FtpResponse
  342.     End If
  343.     CreateDirectory = bRet
  344. End Function
  345.  
  346. ' .:::: remove a directory (directory must be empty)
  347. Public Function RemoveDirectory(ByVal sDirectory As String) As Boolean
  348.     Dim bRet As Boolean
  349.    
  350.     On Local Error Resume Next
  351.     DbgTrace "RemoveDirectory " & sDirectory
  352.     bRet = FtpRemoveDir(mFTP.hConnect, sDirectory)
  353.     If bRet = False Then
  354.         DecodeError
  355.     Else
  356.         FtpResponse
  357.     End If
  358.     RemoveDirectory = bRet
  359. End Function
  360.  
  361. ' .:::: read directory listing
  362. Public Function ReadDirList(Optional ByVal sFileMask As String = vbNullString) As Long
  363.     Dim hFind As Long, tFIND As WIN32_FIND_DATA
  364.     Dim bFind As Boolean, lFiles As Long
  365.    
  366.     On Local Error Resume Next
  367.     lFiles = -1
  368.     ReadDirList = lFiles
  369.     DbgTrace "ReadDirList " & sFileMask
  370.    
  371.     Err.Clear
  372.     hFind = FtpFindFirstFile(mFTP.hConnect, sFileMask, tFIND, INTERNET_FLAG_RELOAD, mFTP.hCtx)
  373.     If (hFind = 0) Then
  374.         DecodeError
  375.         Exit Function
  376.     End If
  377.     FtpResponse
  378.    
  379.     lFiles = 0
  380.     Do
  381.         lFiles = lFiles + 1
  382.         DecodeDirEntry tFIND
  383.         bFind = FtpFindNextFile(hFind, tFIND)
  384.         If bFind = False Then
  385.             If Err.LastDllError <> ERROR_NO_MORE_FILES Then
  386.                 DecodeError
  387.                 lFiles = -1
  388.             End If
  389.         End If
  390.     Loop While bFind <> False
  391.    
  392.     Call FtpFindClose(hFind)
  393.     If lFiles <> -1 Then
  394.         FtpResponse
  395.     End If
  396.     ReadDirList = lFiles
  397. End Function
  398.  
  399. ' .:::: delete a file
  400. Public Function DeleteFile(ByVal sFileName As String) As Boolean
  401.     Dim bRet As Boolean
  402.    
  403.     On Local Error Resume Next
  404.     DbgTrace "DeleteFile " & sFileName
  405.     bRet = FtpDeleteFile(mFTP.hConnect, sFileName)
  406.     If bRet = False Then
  407.         DecodeError
  408.     Else
  409.         FtpResponse
  410.     End If
  411.     DeleteFile = bRet
  412. End Function
  413.  
  414. ' .:::: rename a file
  415. Public Function RenameFile(ByVal sFileName As String, ByVal sNewName As String) As Boolean
  416.     Dim bRet As Boolean
  417.    
  418.     On Local Error Resume Next
  419.     DbgTrace "RenameFile " & sFileName & " -> " & sNewName
  420.     bRet = FtpRenameFile(mFTP.hConnect, sFileName, sNewName)
  421.     If bRet = False Then
  422.         DecodeError
  423.     Else
  424.         FtpResponse
  425.     End If
  426.     RenameFile = bRet
  427. End Function
  428.  
  429. ' .:::: download (receive) a file from ftp server
  430. Public Function DownloadFile(ByVal sRemoteFile As String, ByVal sLocalFile As String, Optional ByVal nMode As enFtpFileMode = ffm_Binary) As Boolean
  431.     Dim lMode As FTP_TRANSFER_TYPE
  432.     Dim hLocal As Long, hRemote As Long
  433.     Dim lSizeLow As Long, lSizeHigh As Long
  434.     Dim lSize As Long, lCurrent As Long
  435.     Dim bRet As Boolean, bRead As Boolean
  436.     Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
  437.    
  438.     On Local Error Resume Next
  439.     bRet = False
  440.     lCurrent = 0
  441.     DownloadFile = bRet
  442.     DbgTrace "DownloadFile " & sRemoteFile & " -> " & sLocalFile
  443.    
  444.     hLocal = OpenFile(sLocalFile, True)
  445.     If hLocal = 0 Then
  446.         Exit Function
  447.     End If
  448.    
  449.     If nMode = ffm_Ascii Then
  450.         lMode = FTP_TRANSFER_TYPE_ASCII + FTP_TRANSFER_NO_CACHE
  451.     Else
  452.         lMode = FTP_TRANSFER_TYPE_BINARY + FTP_TRANSFER_NO_CACHE
  453.     End If
  454.     Err.Clear
  455.     hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_READ, lMode, mFTP.hCtx)
  456.     If (hRemote = 0) Then
  457.         DecodeError
  458.         Close #hLocal
  459.         Exit Function
  460.     End If
  461.     FtpResponse
  462.    
  463.     lSizeLow = FtpFileSize(hRemote, lSizeHigh)
  464.     lSize = DecodeSize(lSizeLow, lSizeHigh)
  465.     RaiseEvent Progress(lCurrent, lSize)
  466.    
  467.     ' loop: read from ftp and write to file
  468.    bRet = False
  469.     Do
  470.         bRead = FtpBuffSize(hRemote, lBuff, 0&, mFTP.hCtx)
  471.         If lBuff < 1 Then
  472.             lBuff = 1024
  473.         End If
  474.         ReDim cbBuff(lBuff + 1)
  475.         lBytes = 0
  476.         bRead = FtpReadFile(hRemote, cbBuff(0), lBuff, lBytes)
  477.         If (bRead <> False) Then
  478.             If (lBytes > 0) Then
  479.                 ReDim Preserve cbBuff(lBytes - 1)
  480.                 Err.Clear
  481.                 Put #hLocal, , cbBuff
  482.                 If Err.Number <> 0 Then
  483.                     RaiseEvent Failure(Err.Number, Err.Description)
  484.                     DbgTrace "Error: " & Err.Number & " " & Err.Description
  485.                     bRead = False
  486.                     bRet = False
  487.                 Else
  488.                     lCurrent = lCurrent + lBytes
  489.                     RaiseEvent Progress(lCurrent, lSize)
  490.                 End If
  491.             Else
  492.                 bRead = False
  493.                 bRet = True
  494.             End If
  495.         Else
  496.             DecodeError
  497.             bRead = False
  498.             bRet = False
  499.         End If
  500.     Loop While (bRead <> False)
  501.    
  502.     If bRet = True Then
  503.         RaiseEvent Progress(lCurrent, lSize)
  504.     End If
  505.     Call FtpCloseFile(hRemote)
  506.     If bRet = True Then
  507.         FtpResponse
  508.     End If
  509.     Close #hLocal
  510.    
  511.     DownloadFile = bRet
  512. End Function
  513.  
  514. ' .:::: upload (send) a file to ftp server
  515. Public Function UploadFile(ByVal sLocalFile As String, ByVal sRemoteFile As String, Optional ByVal nMode As enFtpFileMode = ffm_Binary) As Boolean
  516.     Dim lMode As FTP_TRANSFER_TYPE
  517.     Dim hLocal As Long, hRemote As Long
  518.     Dim lSizeLow As Long, lSizeHigh As Long
  519.     Dim lSize As Long, lCurrent As Long, lRemainder As Long
  520.     Dim bRet As Boolean, bWrite As Boolean
  521.     Dim cbBuff() As Byte, lBuff As Long, lBytes As Long
  522.    
  523.     On Local Error Resume Next
  524.     bRet = False
  525.     lCurrent = 0
  526.     UploadFile = bRet
  527.     DbgTrace "UploadFile " & sLocalFile & " -> " & sRemoteFile
  528.    
  529.     hLocal = OpenFile(sLocalFile, False)
  530.     If hLocal = 0 Then
  531.         Exit Function
  532.     End If
  533.    
  534.     If nMode = ffm_Ascii Then
  535.         lMode = FTP_TRANSFER_TYPE_ASCII + FTP_TRANSFER_NO_CACHE
  536.     Else
  537.         lMode = FTP_TRANSFER_TYPE_BINARY + FTP_TRANSFER_NO_CACHE
  538.     End If
  539.     Err.Clear
  540.     hRemote = FtpOpenFile(mFTP.hConnect, sRemoteFile, GENERIC_WRITE, lMode, mFTP.hCtx)
  541.     If (hRemote = 0) Then
  542.         DecodeError
  543.         Close #hLocal
  544.         Exit Function
  545.     End If
  546.     FtpResponse
  547.    
  548.     lSize = FileLen(sLocalFile)
  549.     RaiseEvent Progress(lCurrent, lSize)
  550.    
  551.     ' loop: read from file and write to ftp
  552.    bRet = False
  553.     lRemainder = lSize
  554.     Do
  555.         If lRemainder < 1024 Then
  556.             lBuff = lRemainder
  557.         Else
  558.             lBuff = 1024
  559.         End If
  560.         ReDim cbBuff(lBuff - 1)
  561.         lRemainder = lRemainder - lBuff
  562.         Err.Clear
  563.         Get #hLocal, , cbBuff
  564.         If Err.Number <> 0 Then
  565.             RaiseEvent Failure(Err.Number, Err.Description)
  566.             DbgTrace "Error: " & Err.Number & " " & Err.Description
  567.             bWrite = False
  568.             bRet = False
  569.         Else
  570.             lBytes = 0
  571.             bWrite = FtpWriteFile(hRemote, cbBuff(0), lBuff, lBytes)
  572.             If (bWrite <> False) Then
  573.                 If (lBytes > 0) Then
  574.                     lCurrent = lCurrent + lBytes
  575.                     RaiseEvent Progress(lCurrent, lSize)
  576.                 Else
  577.                     bWrite = False
  578.                     bRet = True
  579.                 End If
  580.                 If lRemainder < 1 Then
  581.                     bWrite = False
  582.                     bRet = True
  583.                 End If
  584.             Else
  585.                 DecodeError
  586.                 bWrite = False
  587.                 bRet = False
  588.             End If
  589.         End If
  590.     Loop While (bWrite <> False)
  591.    
  592.     If bRet = True Then
  593.         RaiseEvent Progress(lCurrent, lSize)
  594.     End If
  595.     Call FtpCloseFile(hRemote)
  596.     If bRet = True Then
  597.         FtpResponse
  598.     End If
  599.     Close #hLocal
  600.    
  601.     UploadFile = bRet
  602. End Function
  603.  
  604. ' :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  605. ' :: Internal service functions - not visible from outer program
  606. ' :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  607.  
  608. ' ==== decode error and signal it
  609. Private Sub DecodeError()
  610.     Dim lSize As Long, lErrNo As Long, lRet As Long
  611.     Dim hMod As Long
  612.    
  613.     lErrNo = Err.LastDllError
  614.     lSize = 2048
  615.     mFTP.dwLastError = 0
  616.     mFTP.szLastError = String(lSize, vbNullChar)
  617.     lRet = InternetError(mFTP.dwLastError, mFTP.szLastError, lSize)
  618.     If (lRet <> 0) And (mFTP.dwLastError <> 0) Then
  619.         mFTP.szLastError = GetSZ(mFTP.szLastError)
  620.     Else
  621.         mFTP.dwLastError = lErrNo
  622.         mFTP.szLastError = ""
  623.         hMod = GetModuleHandle("wininet.dll")
  624.         If hMod <> 0 Then
  625.             lSize = 2048
  626.             mFTP.szLastError = String(lSize, vbNullChar)
  627.             lRet = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, hMod, lErrNo, 0, mFTP.szLastError, lSize, 0)
  628.             mFTP.dwLastError = lErrNo
  629.             mFTP.szLastError = GetSZ(mFTP.szLastError)
  630.         End If
  631.         If Len(mFTP.szLastError) < 1 Then
  632.             mFTP.szLastError = "Unexpected error"
  633.         End If
  634.     End If
  635.     RaiseEvent Failure(mFTP.dwLastError, mFTP.szLastError)
  636.     DbgTrace "Failure: " & mFTP.dwLastError & " " & mFTP.szLastError
  637. End Sub
  638.  
  639. ' ==== FTP responses
  640. Private Sub FtpResponse()
  641.     Dim lSize As Long, lErrNo As Long
  642.     Dim lCode As Long, sData As String
  643.     Dim vaData As Variant, iData As Long
  644.     Dim sMsg As String
  645.    
  646.     On Local Error Resume Next
  647.     lSize = 2048
  648.     lCode = 0
  649.     sData = String(lSize, vbNullChar)
  650.     If InternetError(lCode, sData, lSize) Then
  651.         sData = GetSZ(sData)
  652.         vaData = Split(sData, vbCrLf)
  653.         For iData = LBound(vaData) To UBound(vaData)
  654.             sMsg = Trim(Replace(vaData(iData), vbCr, " "))
  655.             sMsg = Trim(Replace(sMsg, vbLf, " "))
  656.             If Len(sMsg) > 0 Then
  657.                 RaiseEvent Message(sMsg)
  658.                 DbgTrace "Message " & sMsg
  659.             End If
  660.         Next iData
  661.     End If
  662. End Sub
  663.  
  664. ' ==== cut an AsciiZ to its length
  665. Private Function GetSZ(ByVal sBuff As String)
  666.     On Local Error Resume Next
  667.     GetSZ = Mid(sBuff, 1, InStr(sBuff & vbNullChar, vbNullChar) - 1)
  668. End Function
  669.  
  670. ' ==== decode a directory entry
  671. Private Sub DecodeDirEntry(tFIND As WIN32_FIND_DATA)
  672.     Dim tDATA As VB_FIND_DATA
  673.     Dim cTemp As Currency
  674.     Dim st As SYSTEMTIME
  675.    
  676.     On Local Error Resume Next
  677.     With tDATA
  678.         If (tFIND.dwFileAttributes And vbDirectory) = 0 Then
  679.             CopyMemory cTemp, tFIND.nFileSizeLow, 4
  680.             CopyMemory ByVal VarPtr(cTemp) + 4, tFIND.nFileSizeHigh, 4
  681.             .dFileSize = CDbl(cTemp) * 10000#
  682.         End If
  683.         FileTimeToSystemTime tFIND.ftLastWriteTime, st
  684.         .dtLastWriteTime = DateSerial(st.wYear, st.wMonth, st.wDay) + TimeSerial(st.wHour, st.wMinute, st.wSecond) + (st.wMilliseconds / 86400000)
  685.         .vFileAttributes = tFIND.dwFileAttributes
  686.         .sFileName = GetSZ(tFIND.cFileName)
  687.         .sAlternate = GetSZ(tFIND.cAlternate) ' <-- not used
  688.        RaiseEvent DirLine(.vFileAttributes, .dtLastWriteTime, .dFileSize, .sFileName)
  689.         DbgTrace "DirLine " & Format(.dtLastWriteTime, "YYYY-MM-DD HH:NN:SS") & " " & Format(.dFileSize, "#,###,###,###,##0") & " " & .sFileName
  690.     End With
  691. End Sub
  692.  
  693. ' ==== decode a qword quantity
  694. Private Function DecodeSize(ByVal lSizeLow As Long, ByVal lSizeHigh As Long) As Double
  695.     Dim cTemp As Currency
  696.    
  697.     On Local Error Resume Next
  698.     CopyMemory cTemp, lSizeLow, 4
  699.     CopyMemory ByVal VarPtr(cTemp) + 4, lSizeHigh, 4
  700.     DecodeSize = CDbl(cTemp) * 10000#
  701. End Function
  702.  
  703. ' ==== Open a file for read or write
  704. Private Function OpenFile(ByVal sFileName As String, Optional bWrite As Boolean = False) As Long
  705.     Dim hFile As Long, bRet As Boolean
  706.    
  707.     On Local Error GoTo Catch
  708.     bRet = False
  709.     hFile = FreeFile
  710.     If bWrite Then
  711.         On Local Error Resume Next
  712.         SetAttr sFileName, vbNormal
  713.         Kill sFileName
  714.         On Local Error GoTo Catch
  715.         Open sFileName For Binary Access Write Lock Read Write As #hFile
  716.     Else
  717.         Open sFileName For Binary Access Read Lock Read Write As #hFile
  718.     End If
  719.     bRet = True
  720.    
  721. BailOut:
  722.     On Local Error Resume Next
  723.     If bRet = False Then
  724.         Close #hFile
  725.         hFile = 0
  726.         If bWrite Then
  727.             Kill sFileName
  728.         End If
  729.     End If
  730.     OpenFile = hFile
  731.     Exit Function
  732.    
  733. Catch:
  734.     RaiseEvent Failure(Err.Number, Err.Description)
  735.     DbgTrace "Error: " & Err.Number & " " & Err.Description
  736.     bRet = False
  737.     Resume BailOut
  738. End Function
  739.  
  740. ' ==== writes a debug message
  741. Private Function DbgTrace(ByVal sMsg As String)
  742.     Dim sTxt As String
  743.    
  744.     On Local Error Resume Next
  745.     If mbDebug = True Then
  746.         sTxt = "[" & App.EXEName & "][FTP] " & sMsg & vbCrLf
  747.         Call OutputDebugString(sTxt)
  748.     End If
  749. End Function
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement