Author Topic: Huge FTP Library for Windows (WinAPI)  (Read 8105 times)

0 Members and 1 Guest are viewing this topic.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Huge FTP Library for Windows (WinAPI)
« on: November 17, 2020, 06:50:05 pm »
Since this library is so huge it kind of deserves its own post (Literally used every FTP function available in Wininet except for FtpCommandA). Yes, I have been a little quieter on the forum recently but that's because I've been working behind the scenes trying to find my next big project. Well, I've found it. FTP using WinAPI. This took me several days of working night and day and staying up until 4, almost 5 AM. This library provides many possibilities. You can change FTP directory, create a directory, upload a file, download a file, delete a file, rename a file, and delete a directory. Also, a nice feature; you can generate a "CMD dir" style FTP listing that gives last modified date, file size, and file name (Now with extension filtering! Woot! Also, added a function that returns a file listing as a string using an extension filter). Uploads and downloads can be tracked with percentages. In the library, I've designed it so that the screen is cleared and the progress is printed as it does either of those commands. Obviously, one could comment those out if so desired. There is the option of uncommenting the FtpGetFile and FtpPutFile functions if you don't mind your program taking a pause to upload or download something large with no way of tracking the progress. It's up to you. Here are some screenshots showing a couple of FTP listings. Directories show as bright green.

ftp directory1.png
 
ftp directory2.png


And the code:
Code: QB64: [Select]
  1.  
  2. 'Constants
  3. CONST INTERNET_OPEN_TYPE_DIRECT = 1
  4.  
  5. CONST INTERNET_DEFAULT_FTP_PORT = 21
  6.  
  7. CONST INTERNET_SERVICE_FTP = 1
  8.  
  9. CONST FILE_ATTRIBUTE_ARCHIVE = &H20
  10. CONST FILE_ATTRIBUTE_COMPRESSED = &H800
  11. CONST FILE_ATTRIBUTE_DEVICE = &H40
  12. CONST FILE_ATTRIBUTE_DIRECTORY = &H10
  13. CONST FILE_ATTRIBUTE_ENCRYPTED = &H4000
  14. CONST FILE_ATTRIBUTE_HIDDEN = &H2
  15. CONST FILE_ATTRIBUTE_INTEGRITY_STREAM = &H8000
  16. CONST FILE_ATTRIBUTE_NORMAL = &H80
  17. CONST FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = &H2000
  18. CONST FILE_ATTRIBUTE_NO_SCRUB_DATA = &H20000
  19. CONST FILE_ATTRIBUTE_OFFLINE = &H1000
  20. CONST FILE_ATTRIBUTE_READONLY = &H1
  21. CONST FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = &H400000
  22. CONST FILE_ATTRIBUTE_RECALL_ON_OPEN = &H40000
  23. CONST FILE_ATTRIBUTE_REPARSE_POINT = &H400
  24. CONST FILE_ATTRIBUTE_SPARSE_FILE = &H200
  25. CONST FILE_ATTRIBUTE_SYSTEM = &H4
  26. CONST FILE_ATTRIBUTE_TEMPORARY = &H100
  27. CONST FILE_ATTRIBUTE_VIRTUAL = &H10000
  28.  
  29. 'Flags
  30. CONST INTERNET_FLAG_RELOAD = &H80000000
  31. CONST INTERNET_FLAG_PASSIVE = &H08000000
  32.  
  33. CONST FTP_TRANSFER_TYPE_ASCII = &H00000001
  34. CONST FTP_TRANSFER_TYPE_BINARY = &H00000002
  35. CONST FTP_TRANSFER_TYPE_UNKNOWN = &H00000000
  36.  
  37. CONST MAX_PATH = 260
  38.  
  39. CONST MAXDWORD = &HFFFFFFFF
  40.  
  41. CONST GENERIC_READ = &H80000000
  42. CONST GENERIC_WRITE = &H40000000
  43.  
  44. CONST TRUE = 1
  45. CONST FALSE = 0
  46.  
  47. DIM SHARED hInternet AS LONG
  48. DIM SHARED hFtpSession AS LONG
  49.  
  50. TYPE SYSTEMTIME
  51.     wYear AS INTEGER
  52.     wMonth AS INTEGER
  53.     wDayOfWeek AS INTEGER
  54.     wDay AS INTEGER
  55.     wHour AS INTEGER
  56.     wMinute AS INTEGER
  57.     wSecond AS INTEGER
  58.     wMilliseconds AS INTEGER
  59.  
  60. TYPE FILETIME
  61.     dwLowDateTime AS LONG
  62.     dwHighDateTime AS LONG
  63.  
  64. TYPE WIN32_FIND_DATA
  65.     dwFileAttributes AS LONG
  66.     ftCreationTime AS FILETIME
  67.     ftLastAccessTime AS FILETIME
  68.     ftLastWriteTime AS FILETIME
  69.     nFileSizeHigh AS _UNSIGNED LONG
  70.     nFileSizeLow AS _UNSIGNED LONG
  71.     dwReserved0 AS LONG
  72.     dwReserved1 AS LONG
  73.     cFileName AS STRING * MAX_PATH
  74.     cAlternateFileName AS STRING * 14
  75.     dwFileType AS LONG
  76.     dwCreatorType AS LONG
  77.     wFinderFlags AS INTEGER
  78.  
  79. TYPE TIME_DYNAMIC_ZONE_INFORMATION
  80.     Bias AS LONG
  81.     StandardName AS STRING * 64
  82.     StandardDate AS SYSTEMTIME
  83.     StandardBias AS LONG
  84.     DaylightName AS STRING * 64
  85.     DaylightDate AS SYSTEMTIME
  86.     DaylightBias AS LONG
  87.     TimeZoneKeyName AS STRING * 256
  88.     DynamicDaylightTimeDisabled AS _BYTE
  89.  
  90.     FUNCTION InternetOpen& ALIAS InternetOpenA (BYVAL lpszAgent AS _OFFSET, BYVAL dwAccessType AS LONG, BYVAL lpszProxy AS _OFFSET, BYVAL lpszProxyBypass AS _OFFSET, BYVAL dwFlags AS LONG)
  91.     FUNCTION InternetConnect& ALIAS InternetConnectA (BYVAL hInternet AS LONG, BYVAL lpszServerName AS _OFFSET, BYVAL nServerPort AS INTEGER, BYVAL lpszUserName AS _OFFSET, BYVAL lpszPassword AS _OFFSET, BYVAL dwService AS LONG, BYVAL dwFlags AS LONG, BYVAL dwContext AS _OFFSET)
  92.     FUNCTION InternetGetlastResponseInfo%% ALIAS InternetGetLastResponseInfoA (BYVAL lpdwError AS _OFFSET, BYVAL lpszBuffer AS _OFFSET, BYVAL lpdwBufferLength AS _OFFSET)
  93.     FUNCTION FTPGetCurrentDirectory%% ALIAS FtpGetCurrentDirectoryA (BYVAL hConnect AS LONG, BYVAL lpszCurrentDirectory AS _OFFSET, BYVAL lpdwCurrentDirectory AS _OFFSET)
  94.     FUNCTION FTPSetCurrentDirectory%% ALIAS FtpSetCurrentDirectoryA (BYVAL hConnect AS LONG, BYVAL lpszDirectory AS _OFFSET)
  95.     FUNCTION FTPFindFirstFile& ALIAS FtpFindFirstFileA (BYVAL hConnect AS LONG, BYVAL lpszSearchFile AS _OFFSET, BYVAL lpFindFileData AS _OFFSET, BYVAL dwFlags AS LONG, BYVAL dwContext AS _OFFSET)
  96.     FUNCTION FTPFindNextFile%% ALIAS InternetFindNextFileA (BYVAL hFind AS LONG, BYVAL lpvFindData AS _OFFSET)
  97.     FUNCTION FTPOpenFile& ALIAS FtpOpenFileA (BYVAL hConnect AS LONG, BYVAL lpszFileName AS _OFFSET, BYVAL dwAccess AS LONG, BYVAL dwFlags AS LONG, BYVAL dwContext AS _OFFSET)
  98.     'FUNCTION FTPGetFile%% ALIAS FtpGetFileA (BYVAL hConnect AS LONG, BYVAL lpszRemoteFile AS _OFFSET, BYVAL lpszNewFile AS _OFFSET, BYVAL fFailIfExists AS _BYTE, BYVAL dwFlagsAndAttributes AS LONG, BYVAL dwFlags AS LONG, BYVAL dwContext AS _OFFSET)
  99.     'FUNCTION FTPPutFile%% ALIAS FtpPutFileA (BYVAL hConnect AS LONG, BYVAL lpszLocalFile AS _OFFSET, BYVAL lpszNewRemoteFile AS _OFFSET, BYVAL dwFlags AS LONG, BYVAL dwContext AS _OFFSET)
  100.     FUNCTION FTPGetFileSize~& ALIAS FtpGetFileSize (BYVAL hFile AS LONG, BYVAL lpdwFileSizeHigh AS _OFFSET)
  101.     FUNCTION FTPRenameFile%% ALIAS FtpRenameFileA (BYVAL hConnect AS LONG, BYVAL lpszExisting AS _OFFSET, BYVAL lpszNew AS _OFFSET)
  102.     FUNCTION FTPDeleteFile%% ALIAS FtpDeleteFileA (BYVAL hConnect AS LONG, BYVAL lpszFileName AS _OFFSET)
  103.     FUNCTION FTPCreateDirectory%% ALIAS FtpCreateDirectoryA (BYVAL hConnect AS LONG, BYVAL lpszDirectory AS _OFFSET)
  104.     FUNCTION FTPRemoveDirectory%% ALIAS FtpRemoveDirectoryA (BYVAL hConnect AS LONG, BYVAL lpszDirectory AS _OFFSET)
  105.     FUNCTION InternetReadFile%% (BYVAL hFile AS LONG, BYVAL lpBuffer AS _OFFSET, BYVAL dwNumberOfBytesToRead AS LONG, BYVAL lpdwNumberOfBytesRead AS _OFFSET)
  106.     FUNCTION InternetWriteFile%% (BYVAL hFile AS LONG, BYVAL lpBuffer AS _OFFSET, BYVAL dwNumberOfBytesToWrite AS LONG, BYVAL lpdwNumberOfBytesWritten AS _OFFSET)
  107.     FUNCTION InternetLockRequestFile%% (BYVAL hInternet AS LONG, BYVAL lphLockRequestInfo AS _OFFSET)
  108.     FUNCTION InternetUnlockRequestFile%% (BYVAL hLockRequestInfo AS LONG)
  109.     FUNCTION InternetCloseHandle%% (BYVAL hInternet AS LONG)
  110.  
  111.     FUNCTION GetLastError& ()
  112.     FUNCTION FileTimeToSystemTime%% (BYVAL lpFileTime AS _OFFSET, BYVAL lpSystemTime AS _OFFSET)
  113.     FUNCTION SystemTimeToLocalTime%% ALIAS SystemTimeToTzSpecificLocalTimeEx (BYVAL lpTimeZoneInformation AS _UNSIGNED _OFFSET, BYVAL lpUniversalTime AS _UNSIGNED _OFFSET, BYVAL lpLocalTime AS _UNSIGNED _OFFSET)
  114.     FUNCTION GetDynamicTimeZoneInformation~& (BYVAL lpTimeZoneInformation AS _UNSIGNED _OFFSET)
  115.  
  116.  
  117. CALL FTPConnect("hostname", "username", "password", INTERNET_DEFAULT_FTP_PORT)
  118.  
  119. 'a = FTPchdir("Server/Server1/Movies/Bob Hope and Bing Crosby")
  120.  
  121. a = FTPchdir("Server/Server1/Music")
  122.  
  123. a = FTPDownload("2Yoon/Harvest Moon/01 24_7.mp3", "C:\Users\Zachary\Music\24_7.mp3")
  124. IF a = TRUE THEN PRINT "Downloaded" ELSE PRINT "Download failed"
  125.  
  126.  
  127. 'a = FTPUpload("C:\Users\Zachary\Music\The Seventh Power\The Power\10 The Power.mp3", "The Seventh Power - The Power.mp3")
  128. 'IF a = TRUE THEN PRINT "Uploaded" ELSE PRINT "Upload failed"
  129. 'a = FTPmkdir("Test Directory")
  130. 'a = FTPdel("The Seventh Power - The Power.mp3")
  131. 'CALL FTPdir(".srt")
  132. 'CALL FTPdir("")
  133. 'PRINT FTPfilelist(".mkv")
  134.  
  135. CALL CloseFTP
  136.  
  137.  
  138. SUB FTPConnect (hostname AS STRING, username AS STRING, password AS STRING, port AS INTEGER)
  139.     IF port = 0 THEN port = INTERNET_DEFAULT_FTP_PORT
  140.     hInternet = InternetOpen(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0)
  141.     IF hInternet = FALSE THEN
  142.         PRINT "InternetOpen", GetLastError
  143.     ELSE
  144.         hostname = hostname + CHR$(0)
  145.         username = username + CHR$(0)
  146.         password = password + CHR$(0)
  147.         hFtpSession = InternetConnect(hInternet, _OFFSET(hostname), port, _OFFSET(username), _OFFSET(password), INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0)
  148.         IF hFtpSession = FALSE THEN
  149.             DIM a AS _BYTE
  150.             PRINT "InternetConnect", GetLastError
  151.             DIM dwError AS LONG
  152.             DIM errorbuffer AS STRING
  153.             DIM errorlen AS LONG
  154.             errorbuffer = SPACE$(1024)
  155.             errorlen = LEN(errorbuffer)
  156.             a = InternetGetlastResponseInfo(_OFFSET(dwError), _OFFSET(errorbuffer), _OFFSET(errorlen))
  157.             PRINT "Error Info:"; dwError, MID$(errorbuffer, 1, errorlen)
  158.         END IF
  159.     END IF
  160.  
  161. SUB CloseFTP
  162.     DIM a AS _BYTE
  163.     a = InternetCloseHandle(hFtpSession)
  164.     a = InternetCloseHandle(hInternet)
  165.  
  166. FUNCTION FTPren%% (oldname AS STRING, newname AS STRING)
  167.     IF oldname <> "" AND newname <> "" AND hFtpSession <> 0 THEN
  168.         oldname = oldname + CHR$(0)
  169.         newname = newname + CHR$(0)
  170.         DIM a AS _BYTE
  171.         a = FTPRenameFile(hFtpSession, _OFFSET(oldname), _OFFSET(newname))
  172.         FTPren = a
  173.     END IF
  174.  
  175. FUNCTION FTPrmdir%% (directory AS STRING)
  176.     IF directory <> "" AND hFtpSession <> 0 THEN
  177.         directory = directory + CHR$(0)
  178.         DIM a AS _BYTE
  179.         a = FTPRemoveDirectory(hFtpSession, _OFFSET(directory))
  180.         FTPrmdir = a
  181.     END IF
  182.  
  183. FUNCTION FTPdel%% (filename AS STRING)
  184.     IF filename <> "" AND hFtpSession <> 0 THEN
  185.         filename = filename + CHR$(0)
  186.         DIM a AS _BYTE
  187.         a = FTPDeleteFile(hFtpSession, _OFFSET(filename))
  188.         FTPdel = a
  189.     END IF
  190.  
  191. FUNCTION FTPmkdir%% (directory AS STRING)
  192.     IF directory <> "" AND hFtpSession <> 0 THEN
  193.         directory = directory + CHR$(0)
  194.         DIM a AS _BYTE
  195.         a = FTPCreateDirectory(hFtpSession, _OFFSET(directory))
  196.         FTPmkdir = a
  197.     END IF
  198.  
  199. FUNCTION FTPchdir%% (directory AS STRING)
  200.     IF hFtpSession = FALSE OR directory = "" THEN
  201.         FTPchdir = FALSE
  202.     ELSE
  203.         DIM a AS _BYTE
  204.         directory = directory + CHR$(0)
  205.         a = FTPSetCurrentDirectory(hFtpSession, _OFFSET(directory))
  206.         IF a = FALSE THEN
  207.             FTPchdir = FALSE
  208.             PRINT "FTPSetCurrentDirectory", GetLastError
  209.             DIM dwError AS LONG
  210.             DIM errorbuffer AS STRING
  211.             DIM errorlen AS LONG
  212.             errorbuffer = SPACE$(1024)
  213.             errorlen = LEN(errorbuffer)
  214.             a = InternetGetlastResponseInfo(_OFFSET(dwError), _OFFSET(errorbuffer), _OFFSET(errorlen))
  215.             PRINT "Error Info:"; dwError, MID$(errorbuffer, 1, errorlen)
  216.         ELSE
  217.             FTPchdir = a
  218.         END IF
  219.     END IF
  220.  
  221. FUNCTION FTPfilelist$ (filter AS STRING)
  222.     DIM find AS WIN32_FIND_DATA
  223.     DIM ftpfind AS LONG
  224.     DIM filelist AS STRING
  225.     DIM tempfile AS STRING
  226.     DIM a AS _BYTE
  227.     ftpfind = FTPFindFirstFile(hFtpSession, 0, _OFFSET(find), INTERNET_FLAG_RELOAD, 0)
  228.     IF ftpfind <> FALSE THEN
  229.         tempfile = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)) - 1)
  230.         PRINT RIGHT$(tempfile, LEN(filter))
  231.         IF RIGHT$(tempfile, LEN(filter)) = filter THEN
  232.             filelist = filelist + CHR$(10) + tempfile
  233.         END IF
  234.         a = TRUE
  235.         WHILE a = TRUE
  236.             a = FTPFindNextFile(ftpfind, _OFFSET(find))
  237.             IF a = TRUE THEN
  238.                 tempfile = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)) - 1)
  239.                 IF RIGHT$(tempfile, LEN(filter)) = filter THEN
  240.                     filelist = filelist + CHR$(10) + tempfile
  241.                 END IF
  242.             END IF
  243.         WEND
  244.         FTPfilelist = filelist
  245.     END IF
  246.  
  247. SUB FTPdir (filter AS STRING)
  248.     _CONSOLE ON
  249.     IF hFtpSession = FALSE THEN
  250.         PRINT "No session"
  251.     ELSE
  252.         DIM a AS _BYTE
  253.         DIM currentdirectory AS STRING
  254.         DIM lendir AS LONG
  255.         currentdirectory = SPACE$(MAX_PATH)
  256.         lendir = LEN(currentdirectory) + 1
  257.         DIM ftpfind AS LONG
  258.         DIM fileSize AS _UNSIGNED _INTEGER64
  259.         DIM datasize AS _UNSIGNED _INTEGER64
  260.         DIM filename AS STRING
  261.         DIM year AS INTEGER
  262.         DIM month AS INTEGER
  263.         DIM day AS INTEGER
  264.         DIM hour AS INTEGER
  265.         DIM minute AS INTEGER
  266.         DIM systemTime AS SYSTEMTIME
  267.         DIM convertTime AS _BYTE
  268.         DIM timestamp AS STRING
  269.         DIM filecount AS INTEGER
  270.         DIM dircount AS INTEGER
  271.         DIM totalbytes AS _UNSIGNED _OFFSET
  272.         DIM changecolor AS _BYTE
  273.         DIM tempfile AS STRING
  274.         a = FTPGetCurrentDirectory(hFtpSession, _OFFSET(currentdirectory), _OFFSET(lendir))
  275.         DIM find AS WIN32_FIND_DATA
  276.         ftpfind = FTPFindFirstFile(hFtpSession, 0, _OFFSET(find), INTERNET_FLAG_RELOAD, 0)
  277.         IF ftpfind <> FALSE THEN
  278.             COLOR LightCyan
  279.             PRINT STRING$(23 + lendir, "=")
  280.             PRINT "|Directory Listing of "; MID$(currentdirectory, 1, lendir); "|"
  281.             _CONSOLETITLE "Directory Listing of " + MID$(currentdirectory, 1, lendir)
  282.             PRINT STRING$(23 + lendir, "=")
  283.             COLOR LightMagenta
  284.             PRINT STRING$(80, "=")
  285.             PRINT "Last Modified", "    Size", , "Name"
  286.             PRINT STRING$(80, "=")
  287.             COLOR BrightWhite
  288.             convertTime = FileTimeToSystemTime(_OFFSET(find.ftLastWriteTime), _OFFSET(systemTime))
  289.             DIM zoneInfo AS TIME_DYNAMIC_ZONE_INFORMATION
  290.             DIM localtime AS SYSTEMTIME
  291.             DIM zone AS _OFFSET
  292.             zone = GetDynamicTimeZoneInformation(_OFFSET(zoneInfo))
  293.             convertTime = SystemTimeToLocalTime(_OFFSET(zoneInfo), _OFFSET(systemTime), _OFFSET(localtime))
  294.             year = localtime.wYear
  295.             month = localtime.wMonth
  296.             day = localtime.wDay
  297.             hour = localtime.wHour
  298.             minute = localtime.wMinute
  299.             SELECT CASE month
  300.                 CASE IS >= 10
  301.                     timestamp = LTRIM$(STR$(month))
  302.                 CASE ELSE
  303.                     timestamp = "0" + LTRIM$(STR$(month))
  304.             END SELECT
  305.             SELECT CASE day
  306.                 CASE IS >= 10
  307.                     timestamp = timestamp + "/" + LTRIM$(STR$(day))
  308.                 CASE ELSE
  309.                     timestamp = timestamp + "/0" + LTRIM$(STR$(day))
  310.             END SELECT
  311.             timestamp = timestamp + "/" + LTRIM$(STR$(year))
  312.             SELECT CASE hour
  313.                 CASE IS >= 10
  314.                     timestamp = timestamp + " " + LTRIM$(STR$(hour))
  315.                 CASE ELSE
  316.                     timestamp = timestamp + " 0" + LTRIM$(STR$(hour))
  317.             END SELECT
  318.             SELECT CASE minute
  319.                 CASE IS >= 10
  320.                     timestamp = timestamp + ":" + LTRIM$(STR$(minute))
  321.                 CASE ELSE
  322.                     timestamp = timestamp + ":0" + LTRIM$(STR$(minute))
  323.             END SELECT
  324.             changecolor = 1
  325.             IF find.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY AND filter = "" THEN
  326.                 PRINT timestamp, ;
  327.                 COLOR LightGreen
  328.                 PRINT , , "<DIR> "; MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)))
  329.                 dircount = dircount + 1
  330.             ELSE
  331.                 tempfile = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)) - 1)
  332.                 IF RIGHT$(tempfile, LEN(filter)) = filter THEN
  333.                     PRINT timestamp, ;
  334.                     filecount = filecount + 1
  335.                     datasize = (find.nFileSizeLow)
  336.                     IF datasize < 0 AND (find.nFileSizeHigh) > 0 THEN
  337.                         fileSize = datasize + MAXDWORD + (find.nFileSizeHigh * MAXDWORD)
  338.                     ELSE
  339.                         IF (find.nFileSizeHigh) > 0 THEN
  340.                             fileSize = datasize + (find.nFileSizeHigh * MAXDWORD)
  341.                         ELSEIF datasize < 0 THEN
  342.                             fileSize = datasize + MAXDWORD
  343.                         ELSE
  344.                             fileSize = datasize
  345.                         END IF
  346.                     END IF
  347.                     filename = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)))
  348.                     SELECT CASE fileSize
  349.                         CASE IS < 1024
  350.                             PRINT USING "   ####  B"; fileSize,
  351.                         CASE IS < (1024 ^ 2) AND fileSize >= 1024
  352.                             PRINT USING "####.## KB"; (fileSize / 1024),
  353.                         CASE IS < (1024 ^ 3) AND fileSize >= (1024 ^ 2)
  354.                             PRINT USING "####.## MB"; (fileSize / (1024 ^ 2)),
  355.                         CASE IS < (1024 ^ 4) AND fileSize >= (1024 ^ 3)
  356.                             PRINT USING "####.## GB"; (fileSize / (1024 ^ 3)),
  357.                     END SELECT
  358.                     PRINT , filename
  359.                     totalbytes = totalbytes + fileSize
  360.                 END IF
  361.             END IF
  362.             WHILE a = TRUE
  363.                 a = FTPFindNextFile(ftpfind, _OFFSET(find))
  364.                 IF a THEN
  365.                     convertTime = FileTimeToSystemTime(_OFFSET(find.ftLastWriteTime), _OFFSET(systemTime))
  366.                     zone = GetDynamicTimeZoneInformation(_OFFSET(zoneInfo))
  367.                     convertTime = SystemTimeToLocalTime(_OFFSET(zoneInfo), _OFFSET(systemTime), _OFFSET(localtime))
  368.                     year = localtime.wYear
  369.                     month = localtime.wMonth
  370.                     day = localtime.wDay
  371.                     hour = localtime.wHour
  372.                     minute = localtime.wMinute
  373.                     SELECT CASE month
  374.                         CASE IS >= 10
  375.                             timestamp = LTRIM$(STR$(month))
  376.                         CASE ELSE
  377.                             timestamp = "0" + LTRIM$(STR$(month))
  378.                     END SELECT
  379.                     SELECT CASE day
  380.                         CASE IS >= 10
  381.                             timestamp = timestamp + "/" + LTRIM$(STR$(day))
  382.                         CASE ELSE
  383.                             timestamp = timestamp + "/0" + LTRIM$(STR$(day))
  384.                     END SELECT
  385.                     timestamp = timestamp + "/" + LTRIM$(STR$(year))
  386.                     SELECT CASE hour
  387.                         CASE IS >= 10
  388.                             timestamp = timestamp + " " + LTRIM$(STR$(hour))
  389.                         CASE ELSE
  390.                             timestamp = timestamp + " 0" + LTRIM$(STR$(hour))
  391.                     END SELECT
  392.                     SELECT CASE minute
  393.                         CASE IS >= 10
  394.                             timestamp = timestamp + ":" + LTRIM$(STR$(minute))
  395.                         CASE ELSE
  396.                             timestamp = timestamp + ":0" + LTRIM$(STR$(minute))
  397.                     END SELECT
  398.  
  399.                     IF find.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY THEN
  400.                         IF changecolor THEN
  401.                             COLOR Brown
  402.                             changecolor = 0
  403.                         ELSE
  404.                             COLOR BrightWhite
  405.                             changecolor = 1
  406.                         END IF
  407.                         PRINT timestamp, ;
  408.                         COLOR LightGreen
  409.                         PRINT , , "<DIR> "; MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)))
  410.                         dircount = dircount + 1
  411.                     ELSE
  412.                         tempfile = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)) - 1)
  413.                         IF RIGHT$(tempfile, LEN(filter)) = filter THEN
  414.                             IF changecolor THEN
  415.                                 COLOR Brown
  416.                                 changecolor = 0
  417.                             ELSE
  418.                                 COLOR BrightWhite
  419.                                 changecolor = 1
  420.                             END IF
  421.                             PRINT timestamp, ;
  422.                             filecount = filecount + 1
  423.                             datasize = (find.nFileSizeLow)
  424.                             IF datasize < 0 AND (find.nFileSizeHigh) > 0 THEN
  425.                                 fileSize = datasize + MAXDWORD + (find.nFileSizeHigh * MAXDWORD)
  426.                             ELSE
  427.                                 IF (find.nFileSizeHigh) > 0 THEN
  428.                                     fileSize = datasize + (find.nFileSizeHigh * MAXDWORD) + 1
  429.                                 ELSEIF datasize < 0 THEN
  430.                                     fileSize = datasize + MAXDWORD
  431.                                 ELSE
  432.                                     fileSize = datasize
  433.                                 END IF
  434.                             END IF
  435.                             filename = MID$(find.cFileName, 1, INSTR(find.cFileName, CHR$(0)))
  436.                             SELECT CASE fileSize
  437.                                 CASE IS < 1024
  438.                                     PRINT USING "   ####  B"; fileSize,
  439.                                 CASE IS < (1024 ^ 2) AND fileSize >= 1024
  440.                                     PRINT USING "####.## KB"; (fileSize / 1024),
  441.                                 CASE IS < (1024 ^ 3) AND fileSize >= (1024 ^ 2)
  442.                                     PRINT USING "####.## MB"; (fileSize / (1024 ^ 2)),
  443.                                 CASE IS < (1024 ^ 4) AND fileSize >= (1024 ^ 3)
  444.                                     PRINT USING "####.## GB"; (fileSize / (1024 ^ 3)),
  445.                             END SELECT
  446.                             PRINT , filename
  447.                             totalbytes = totalbytes + fileSize
  448.                         END IF
  449.                     END IF
  450.                 END IF
  451.             WEND
  452.             COLOR LightCyan
  453.             PRINT
  454.             PRINT filecount, " file(s)", , ;
  455.             SELECT CASE totalbytes
  456.                 CASE IS < 1024
  457.                     PRINT USING "Approx_.   #### bytes"; totalbytes
  458.                 CASE IS < (1024 ^ 2) AND totalbytes >= 1024
  459.                     PRINT USING "Approx_. ####.## kilobytes"; (totalbytes / 1024)
  460.                 CASE IS < (1024 ^ 3) AND totalbytes >= (1024 ^ 2)
  461.                     PRINT USING "Approx_. ####.## megabytes"; (totalbytes / (1024 ^ 2))
  462.                 CASE IS < (1024 ^ 4) AND totalbytes >= (1024 ^ 3)
  463.                     PRINT USING "Approx_. ####.## gigabytes"; (totalbytes / (1024 ^ 3))
  464.                 CASE IS < (1024 ^ 5) AND totalbytes >= (1024 ^ 4)
  465.                     PRINT USING "Approx_. ####.## terabytes"; (totalbytes / (1024 ^ 4))
  466.             END SELECT
  467.             PRINT dircount, " folder(s)"
  468.             IF filecount = 0 AND filter <> "" THEN
  469.                 COLOR LightRed
  470.                 PRINT "No files could be found matching filter " + CHR$(34) + filter + CHR$(34)
  471.             END IF
  472.         ELSE
  473.             PRINT "FTPFindFirstFile", GetLastError
  474.             DIM dwError AS LONG
  475.             DIM errorbuffer AS STRING
  476.             DIM errorlen AS LONG
  477.             errorbuffer = SPACE$(1024)
  478.             errorlen = LEN(errorbuffer)
  479.             a = InternetGetlastResponseInfo(_OFFSET(dwError), _OFFSET(errorbuffer), _OFFSET(errorlen))
  480.             PRINT "Error Info:"; dwError, MID$(errorbuffer, 1, errorlen)
  481.         END IF
  482.     END IF
  483.     _DEST 0
  484.  
  485. FUNCTION FTPUpload%% (sourcefile AS STRING, destfile AS STRING)
  486.     IF sourcefile <> "" AND destfile <> "" AND hFtpSession <> 0 THEN
  487.         'sourcefile = sourcefile + CHR$(0)
  488.         destfile = destfile + CHR$(0)
  489.         DIM a AS _BYTE
  490.         'a = FTPPutFile(hFtpSession, _OFFSET(sourcefile), _OFFSET(destfile), FTP_TRANSFER_TYPE_BINARY OR INTERNET_FLAG_RELOAD, 0)
  491.         DIM hTransfer AS LONG
  492.         hTransfer = FTPOpenFile(hFtpSession, _OFFSET(destfile), GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0)
  493.         IF hTransfer THEN
  494.             DIM buffer AS STRING
  495.             buffer = SPACE$(1024)
  496.             DIM filesize AS _UNSIGNED _INTEGER64
  497.             OPEN sourcefile FOR BINARY AS #1
  498.             LOCK #1
  499.             filesize = LOF(1)
  500.             DIM bytesWritten AS _UNSIGNED _INTEGER64
  501.             DIM dwWrite AS LONG
  502.             DO
  503.                 GET #1, , buffer
  504.                 a = InternetWriteFile(hTransfer, _OFFSET(buffer), LEN(buffer), _OFFSET(dwWrite))
  505.                 bytesWritten = bytesWritten + dwWrite
  506.                 CLS
  507.                 PRINT bytesWritten; "bytes uploaded of"; filesize
  508.                 PRINT USING "###.##%"; (bytesWritten / filesize) * 100
  509.                 _DISPLAY
  510.             LOOP UNTIL bytesWritten >= filesize OR GetLastError <> 0 OR EOF(1)
  511.             IF bytesWritten >= filesize AND GetLastError = 0 AND EOF(1) THEN
  512.                 FTPUpload = TRUE
  513.                 bytesWritten = filesize
  514.                 CLS
  515.                 PRINT bytesWritten; "bytes uploaded of"; filesize
  516.                 PRINT USING "###.##%"; (bytesWritten / filesize) * 100
  517.                 _DISPLAY
  518.             ELSE FTPUpload = FALSE
  519.             END IF
  520.             UNLOCK #1
  521.             CLOSE #1
  522.             _AUTODISPLAY
  523.             a = InternetCloseHandle(hTransfer)
  524.         ELSE
  525.             FTPUpload = FALSE
  526.             PRINT "FTPOpenFile", GetLastError
  527.         END IF
  528.     ELSE
  529.         FTPUpload = FALSE
  530.     END IF
  531.  
  532. FUNCTION FTPDownload%% (sourcefile AS STRING, destfile AS STRING)
  533.     IF sourcefile <> "" AND destfile <> "" AND hFtpSession <> 0 THEN
  534.         sourcefile = sourcefile + CHR$(0)
  535.         'destfile = destfile + CHR$(0)
  536.         DIM a AS _BYTE
  537.         'a = FTPGetFile(hFtpSession, _OFFSET(sourcefile), _OFFSET(destfile), TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 0)
  538.         'FTPDownload = a
  539.         DIM hTransfer AS LONG
  540.         hTransfer = FTPOpenFile(hFtpSession, _OFFSET(sourcefile), GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0)
  541.         DIM lockhandle AS LONG
  542.         a = InternetLockRequestFile(hTransfer, _OFFSET(lockhandle))
  543.         IF hTransfer AND a = TRUE AND lockhandle <> 0 THEN
  544.             DIM buffer AS STRING
  545.             buffer = SPACE$(1024)
  546.             DIM datasize AS _UNSIGNED _INTEGER64
  547.             DIM filesize AS _UNSIGNED _INTEGER64
  548.             DIM FileSizeHigh AS _UNSIGNED LONG
  549.             DIM FileSizeLow AS _UNSIGNED LONG
  550.             DIM bytesRead AS _UNSIGNED _INTEGER64
  551.             DIM dwRead AS LONG
  552.             OPEN destfile FOR BINARY AS #1
  553.             DIM outputfile AS STRING
  554.  
  555.             FileSizeLow = FTPGetFileSize(hTransfer, _OFFSET(FileSizeHigh))
  556.             datasize = (FileSizeLow)
  557.             IF datasize < 0 AND (FileSizeHigh) > 0 THEN
  558.                 filesize = datasize + MAXDWORD + (FileSizeHigh * MAXDWORD)
  559.             ELSE
  560.                 IF (FileSizeHigh) > 0 THEN
  561.                     filesize = datasize + (FileSizeHigh * MAXDWORD)
  562.                 ELSEIF datasize < 0 THEN
  563.                     filesize = datasize + MAXDWORD
  564.                 ELSE
  565.                     filesize = datasize
  566.                 END IF
  567.             END IF
  568.             datasize = 0
  569.             DO
  570.                 a = InternetReadFile(hTransfer, _OFFSET(buffer), LEN(buffer) - 1, _OFFSET(dwRead))
  571.                 IF dwRead > 0 THEN
  572.                     outputfile = MID$(buffer, 1, dwRead)
  573.                     PUT #1, , outputfile
  574.                     bytesRead = bytesRead + dwRead
  575.                     CLS
  576.                     PRINT bytesRead; "bytes downloaded of"; filesize
  577.                     PRINT USING "###.##%"; (bytesRead / filesize) * 100
  578.                     _DISPLAY
  579.                 END IF
  580.             LOOP UNTIL bytesRead = filesize OR GetLastError <> 0
  581.             _AUTODISPLAY
  582.             CLOSE #1
  583.             IF bytesRead = filesize THEN FTPDownload = TRUE ELSE FTPDownload = FALSE
  584.             a = InternetUnlockRequestFile(lockhandle)
  585.             a = InternetCloseHandle(hTransfer)
  586.         ELSE
  587.             FTPDownload = FALSE
  588.             PRINT "FTPOpenFile", GetLastError
  589.         END IF
  590.     END IF

Enjoy! Let me know how you all like it and hit me with any questions you have... provided that you have actually read the post first before asking the question.
This should go without saying but in order to test this you will need access to an FTP server and use valid credentials.
« Last Edit: December 07, 2020, 02:31:46 pm by SpriggsySpriggs »
Shuwatch!

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #1 on: November 18, 2020, 02:24:15 am »
Very interesting library Spriggsy, I'll dive into it deeper when back home from work.

And whoever is the owner of the FTP listed in your 1st screenshot seems to be a big K-Pop lover, just as I'am :)
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #2 on: November 18, 2020, 02:24:45 am »
And whoever is the owner of the FTP listed in your 1st screenshot seems to be a big K-Pop lover, just as I'am :)
@RhoSigma
Indeed :) (it's me)
« Last Edit: November 18, 2020, 03:35:09 am by SpriggsySpriggs »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #3 on: November 18, 2020, 02:47:27 am »
Added the ability to filter the "dir" style directory listing by extension as well as a function that returns a directory listing as a line-feed delimited string that can also be filtered.

Screenshot showing the filtered list:

 
ftp directory 3.png
« Last Edit: November 19, 2020, 09:39:42 pm by SpriggsySpriggs »
Shuwatch!

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #4 on: November 18, 2020, 02:29:08 pm »
 Nice! Im gonna dive into this when i get back home too.  K-pop is so popular nowdays, many of my piano students ask to learn kpop tunes on piano.

Thanks for continuing the API work!

- Dav

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #5 on: November 18, 2020, 08:09:58 pm »
Nice! Im gonna dive into this when i get back home too.  K-pop is so popular nowdays, many of my piano students ask to learn kpop tunes on piano.

Thanks for continuing the API work!

Thanks, Dav! Always glad to see you here on the forum!
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #6 on: November 19, 2020, 08:33:22 pm »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #7 on: November 19, 2020, 09:25:04 pm »
Another question from NOVARSEG:
https://www.qb64.org/forum/index.php?topic=3269.msg125485#msg125485

@NOVARSEG

InternetReadFile and InternetWriteFile are two entirely different functions. One is sending data to the FTP server and one is reading from the FTP server. Are you wanting to know about InternetReadFile or InternetWriteFile and what is it you are wanting to know? I want to help you but I have to know how.
« Last Edit: November 19, 2020, 09:27:59 pm by SpriggsySpriggs »
Shuwatch!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #8 on: November 19, 2020, 10:41:58 pm »
In the line
****
 PUT #1, , outputfile
****
 how does PUT know how many bytes to write to the string?

the string was DIMed like
****
DIM outputfile AS STRING
****



« Last Edit: November 19, 2020, 10:50:20 pm by NOVARSEG »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #9 on: November 19, 2020, 10:49:15 pm »
@NOVARSEG
how does PUT know how many bytes to write to the string?

PUT doesn't write to the string. It writes the string to the file. PUT is a file operation. It simply takes however many bytes the string is and writes it. It doesn't care how large or small it is.

https://www.qb64.org/wiki/PUT
Shuwatch!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #10 on: November 19, 2020, 10:51:39 pm »
whoops again     did not see the

****
OPEN destfile FOR BINARY AS #1
****

sorry

what does this do?
****
 outputfile = MID$(buffer, 1, dwRead)
****

why a mid$ and not a left$

I thought  ****  outputfile = LEFT$(buffer, dwRead)   ****

in case dwread < LEN(buffer)
« Last Edit: November 19, 2020, 11:00:45 pm by NOVARSEG »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #11 on: November 19, 2020, 11:04:02 pm »
@NOVARSEG

outputfile = MID$(buffer, 1, dwRead)
I thought  ****  outputfile = LEFT$(buffer, dwRead)   ****
in case dwread < LEN(buffer)

I simply am grabbing the number of bytes (dwRead) from outputfile because the string is actually much longer than the actual data. If I don't grab the string up until dwRead then the string contains junk data from elsewhere on the computer. Your graphics card info, a random word, etc. I prefer MID$ (I detest LEFT$ and RIGHT$) and use it often. I use it in many places in my WinAPI projects and so I keep using it for consistency. dwRead is the return from the FTP server. It will most likely always be less than LEN(buffer). The buffer is supposed to be larger than what I expect. Also, NOVARSEG, when you keep editing your posts when I am replying it makes it difficult to keep up with you (except now I realize you don't know that I'm replying when I am which is why it would be great if you join the Discord server hint hint).
« Last Edit: November 19, 2020, 11:13:35 pm by SpriggsySpriggs »
Shuwatch!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #12 on: November 19, 2020, 11:19:47 pm »
That is interesting about LEFT$ and MID$

MID$(buffer, 1, dwRead)

and

LEFT$(buffer, dwRead)

should compile the same way. 

sorry for the edits.  email is worse though

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Huge FTP Library for Windows (WinAPI)
« Reply #13 on: November 19, 2020, 11:23:13 pm »
@NOVARSEG Well the Discord server is an instant messaging platform.

I know LEFT$ would give me the same results as MID$ but like I said I just prefer it, use it more often, and want to keep up consistency. I'm going to sleep soon so if you have any more questions then I probably won't answer them until tomorrow afternoon.
Shuwatch!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Huge FTP Library for Windows (WinAPI)
« Reply #14 on: November 20, 2020, 12:48:00 am »
SpriggsySpriggs

I would like to try your program. Looks pretty solid.   A lot can be done with FTP library.