]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
file: add support for getting basic directory listings
authorColin Leroy-Mira <colin@colino.net>
Sat, 16 Mar 2024 11:39:01 +0000 (12:39 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 11 Apr 2024 10:37:12 +0000 (12:37 +0200)
Not supported on Windows (yet)

Closes #13137

13 files changed:
CMake/Platforms/WindowsCache.cmake
CMakeLists.txt
configure.ac
docs/cmdline-opts/list-only.md
docs/libcurl/opts/CURLOPT_DIRLISTONLY.md
lib/curl_config.h.cmake
lib/file.c
lib/urldata.h
src/tool_listhelp.c
tests/data/Makefile.inc
tests/data/test1463
tests/data/test1464
tests/data/test3203 [new file with mode: 0644]

index d3391d92f87f959aa196e72e12113b2da0621505..082154ff9f8ac71703ee12d4e6b4c6ec9fe82b80 100644 (file)
@@ -137,6 +137,9 @@ set(HAVE_TERMIOS_H 0)
 set(HAVE_TERMIO_H 0)
 set(HAVE_UTIME_H 0)  # mingw-w64 has it (wrapper to sys/utime.h)
 
+set(HAVE_DIRENT_H 0)
+set(HAVE_OPENDIR 0)
+
 set(HAVE_FSEEKO 0)
 set(HAVE__FSEEKI64 1)
 set(HAVE_SOCKET 1)
index bfc062759cbc6ab29f51e23736c2234f6575a9df..433dc51a840558e1a8d332b7179f80212103ed96 100644 (file)
@@ -1126,6 +1126,7 @@ check_include_file_concat("sys/un.h"         HAVE_SYS_UN_H)
 check_include_file_concat("sys/utime.h"      HAVE_SYS_UTIME_H)
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
+check_include_file_concat("dirent.h"         HAVE_DIRENT_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
@@ -1188,6 +1189,7 @@ endif()
 
 check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
 check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
+check_symbol_exists(opendir       "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
 check_symbol_exists(sched_yield   "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD)
 check_symbol_exists(socketpair    "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
index 0a356e9efdf621aa63559daa1f77ed115d1a1094..9bc7fabd823372387b617930c85d5d6b26dab372 100644 (file)
@@ -3984,6 +3984,12 @@ if test "$want_thres" = "yes" && test "x$USE_THREADS_POSIX" != "x1"; then
   fi
 fi
 
+AC_CHECK_HEADER(dirent.h,
+  [ AC_DEFINE(HAVE_DIRENT_H, 1, [if you have <dirent.h>])
+    AC_CHECK_FUNC(opendir, AC_DEFINE(HAVE_OPENDIR, 1, [if you have opendir]) )
+  ]
+)
+
 CURL_CONVERT_INCLUDE_TO_ISYSTEM
 
 dnl ************************************************************
index 5d8dde65c2dad26b3e519423b51054b787c7da0d..2800a8f79313601ed02ee416a0ad472cb661db24 100644 (file)
@@ -3,10 +3,10 @@ c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
 SPDX-License-Identifier: curl
 Long: list-only
 Short: l
-Protocols: FTP POP3 SFTP
+Protocols: FTP POP3 SFTP FILE
 Help: List only mode
 Added: 4.0
-Category: ftp pop3 sftp
+Category: ftp pop3 sftp file
 Multi: boolean
 See-also:
   - quote
@@ -35,6 +35,9 @@ When retrieving a specific email from POP3, this switch forces a LIST command
 to be performed instead of RETR. This is particularly useful if the user wants
 to see if a specific message-id exists on the server and what size it is.
 
+For FILE, this option has no effect yet as directories are always listed in
+this mode.
+
 Note: When combined with --request, this option can be used to send a UIDL
 command instead, so the user may use the email's unique identifier rather than
 its message-id to make the request.
index 8775d16f1018b42b75ff61a7bdbfc07b848f1e70..348faf386dfdcd347515c3aa956796ac2a915fa6 100644 (file)
@@ -36,6 +36,9 @@ messages on the POP3 server. This can be used to change the default behavior
 of libcurl, when combined with a URL that contains a message ID, to perform a
 "scan listing" which can then be used to determine the size of an email.
 
+For FILE, this option has no effect yet as directories are always listed in
+this mode.
+
 Note: For FTP this causes a NLST command to be sent to the FTP server. Beware
 that some FTP servers list only files in their response to NLST; they might
 not include subdirectories and symbolic links.
index 0f4db69820ed174424be4162d8018adfdb175bf7..dbdd04b5ad006c553a14fc727be20f3d947ab198 100644 (file)
 /* Define to 1 if you have the `closesocket' function. */
 #cmakedefine HAVE_CLOSESOCKET 1
 
+/* Define to 1 if you have the <dirent.h> header file. */
+#cmakedefine HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the `opendir' function. */
+#cmakedefine HAVE_OPENDIR 1
+
 /* Define to 1 if you have the fcntl function. */
 #cmakedefine HAVE_FCNTL 1
 
index c436aaaad47df9619db72c35d73cb0349f2c4d7e..70328fabd95a3e3a7c0922d97f8136cb4ccdbe79 100644 (file)
 #include <fcntl.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
 #include "strtoofft.h"
 #include "urldata.h"
 #include <curl/curl.h>
@@ -544,49 +552,85 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     Curl_pgrsSetDownloadSize(data, expected_size);
 
   if(data->state.resume_from) {
-    if(data->state.resume_from !=
-       lseek(fd, data->state.resume_from, SEEK_SET))
+    if(!S_ISDIR(statbuf.st_mode)) {
+      if(data->state.resume_from !=
+          lseek(fd, data->state.resume_from, SEEK_SET))
+        return CURLE_BAD_DOWNLOAD_RESUME;
+    }
+    else {
       return CURLE_BAD_DOWNLOAD_RESUME;
+    }
   }
 
   result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
   if(result)
     goto out;
 
-  while(!result) {
-    ssize_t nread;
-    /* Don't fill a whole buffer if we want less than all data */
-    size_t bytestoread;
+  if(!S_ISDIR(statbuf.st_mode)) {
+    while(!result) {
+      ssize_t nread;
+      /* Don't fill a whole buffer if we want less than all data */
+      size_t bytestoread;
 
-    if(size_known) {
-      bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ?
-        curlx_sotouz(expected_size) : (xfer_blen-1);
-    }
-    else
-      bytestoread = xfer_blen-1;
+      if(size_known) {
+        bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ?
+          curlx_sotouz(expected_size) : (xfer_blen-1);
+      }
+      else
+        bytestoread = xfer_blen-1;
 
-    nread = read(fd, xfer_buf, bytestoread);
+      nread = read(fd, xfer_buf, bytestoread);
 
-    if(nread > 0)
-      xfer_buf[nread] = 0;
+      if(nread > 0)
+        xfer_buf[nread] = 0;
 
-    if(nread <= 0 || (size_known && (expected_size == 0)))
-      break;
+      if(nread <= 0 || (size_known && (expected_size == 0)))
+        break;
 
-    if(size_known)
-      expected_size -= nread;
+      if(size_known)
+        expected_size -= nread;
 
-    result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
-    if(result)
-      goto out;
+      result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
+      if(result)
+        goto out;
 
-    if(Curl_pgrsUpdate(data))
-      result = CURLE_ABORTED_BY_CALLBACK;
-    else
-      result = Curl_speedcheck(data, Curl_now());
-    if(result)
+      if(Curl_pgrsUpdate(data))
+        result = CURLE_ABORTED_BY_CALLBACK;
+      else
+        result = Curl_speedcheck(data, Curl_now());
+      if(result)
+        goto out;
+    }
+  }
+  else {
+#ifdef HAVE_OPENDIR
+    DIR *dir = opendir(file->path);
+    struct dirent *entry;
+
+    if(!dir) {
+      result = CURLE_READ_ERROR;
       goto out;
+    }
+    else {
+      while((entry = readdir(dir))) {
+        if(entry->d_name[0] != '.') {
+          result = Curl_client_write(data, CLIENTWRITE_BODY,
+                   entry->d_name, strlen(entry->d_name));
+          if(result)
+            break;
+          result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
+          if(result)
+            break;
+        }
+      }
+      closedir(dir);
+    }
+#else
+    failf(data, "Directory listing not yet implemented on this platform.");
+    result = CURLE_READ_ERROR;
+#endif
   }
+
   if(Curl_pgrsUpdate(data))
     result = CURLE_ABORTED_BY_CALLBACK;
 
index 308c51c923796bf0f3521b907828bc65440a0454..03f883496d6d8c88f8843cec2342282bfc14987d 100644 (file)
@@ -104,7 +104,7 @@ typedef unsigned int curl_prot_t;
 #define PROTO_FAMILY_SSH  (CURLPROTO_SCP|CURLPROTO_SFTP)
 
 #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) ||   \
-  !defined(CURL_DISABLE_POP3)
+  !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_FILE)
 /* these protocols support CURLOPT_DIRLISTONLY */
 #define CURL_LIST_ONLY_PROTOCOL 1
 #endif
index 5d9364405448bf33854b47dcd9f3c0746d6ff991..adb01b3bed24bfe5c4193af26f4463ab8175ed85 100644 (file)
@@ -338,7 +338,7 @@ const struct helptxt helptext[] = {
    CURLHELP_CONNECTION},
   {"-l, --list-only",
    "List only mode",
-   CURLHELP_FTP | CURLHELP_POP3 | CURLHELP_SFTP},
+   CURLHELP_FTP | CURLHELP_POP3 | CURLHELP_SFTP | CURLHELP_FILE},
   {"    --local-port <range>",
    "Use a local port number within RANGE",
    CURLHELP_CONNECTION},
index cfbfb7860cf949d06c03eedbc11e9a480645c514..a91b4b7ce51d5518669728bd4e2961cb9180a9d0 100644 (file)
@@ -261,4 +261,4 @@ test3024 test3025 test3026 test3027 test3028 test3029 test3030 \
 \
 test3100 test3101 test3102 test3103 \
 test3200 \
-test3201 test3202
+test3201 test3202 test3203
index 254b51b2227da30ed07b7b54dba5a65b3fd421e7..68b3e4fb0d602452698ff6ab19f5d961a612990d 100644 (file)
@@ -39,6 +39,7 @@ Usage: curl [options...] <url>
 file: FILE protocol options
      --create-file-mode <mode>  File mode for created files
  -I, --head                     Show document info only
+ -l, --list-only                List only mode
  -r, --range <range>            Retrieve only the bytes within RANGE
 </stdout>
 </verify>
index 6a0af34c29ab91e31ab0497e8dbe3f5783aaa11f..186c6ee02456cb0871d402ed4044a30f8594eaa9 100644 (file)
@@ -39,6 +39,7 @@ Usage: curl [options...] <url>
 file: FILE protocol options
      --create-file-mode <mode>  File mode for created files
  -I, --head                     Show document info only
+ -l, --list-only                List only mode
  -r, --range <range>            Retrieve only the bytes within RANGE
 </stdout>
 </verify>
diff --git a/tests/data/test3203 b/tests/data/test3203
new file mode 100644 (file)
index 0000000..527e870
--- /dev/null
@@ -0,0 +1,41 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+FILE
+</keywords>
+</info>
+
+#
+# Client-side
+<client>
+<server>
+file
+</server>
+<name>
+GET a directory using file://
+</name>
+<!-- doesn't work on win32, see #6379 -->
+<features>
+!win32
+</features>
+<command option="no-include">
+file://localhost%FILE_PWD/%LOGDIR/test%TESTNUMBER.dir/
+</command>
+<file name="%LOGDIR/test%TESTNUMBER.dir/dir-listing-test.txt">
+Contents of file are irrelevant
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+<stdout>
+dir-listing-test.txt
+</stdout>
+</verify>
+</testcase>