From 6026d76fdf4922cf62e3c29e547c53c6043e416f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 21 Mar 2026 18:18:53 +0100 Subject: [PATCH] test1668: unit test the FTP 213 time response parser Should make it harder to introduce regressions. Closes #21051 --- lib/ftp.c | 10 ++- tests/data/Makefile.am | 2 +- tests/data/test1668 | 21 +++++ tests/unit/Makefile.inc | 2 +- tests/unit/unit1668.c | 177 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tests/data/test1668 create mode 100644 tests/unit/unit1668.c diff --git a/lib/ftp.c b/lib/ftp.c index d93d2d9ff4..5677aa2313 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -2499,8 +2499,14 @@ static bool twodigit(const char *p, int *val) return FALSE; } -static bool ftp_213_date(const char *p, int *year, int *month, int *day, - int *hour, int *minute, int *second) +/* + * Unittest @1668 + */ + +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second); +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second) { int century; if((strlen(p) < 14) || twodigit(&p[0], ¢ury) || twodigit(&p[2], year) || diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index f8d8548265..eab4f8c99f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -222,7 +222,7 @@ test1640 test1641 test1642 test1643 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ -test1666 test1667 \ +test1666 test1667 test1668 \ \ test1670 test1671 \ \ diff --git a/tests/data/test1668 b/tests/data/test1668 new file mode 100644 index 0000000000..59d7108498 --- /dev/null +++ b/tests/data/test1668 @@ -0,0 +1,21 @@ + + + + +unittest +FTP +ftp_213_date + + + +# Client-side + + +unittest +ftp + + +ftp_213_date unit tests + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index 72d4dd2126..01934d5fc7 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -41,7 +41,7 @@ TESTS_C = \ unit1636.c \ unit1650.c unit1651.c unit1652.c unit1653.c unit1654.c unit1655.c unit1656.c \ unit1657.c unit1658.c unit1660.c unit1661.c unit1663.c unit1664.c \ - unit1666.c unit1667.c \ + unit1666.c unit1667.c unit1668.c \ unit1979.c unit1980.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ diff --git a/tests/unit/unit1668.c b/tests/unit/unit1668.c new file mode 100644 index 0000000000..2c467b3c89 --- /dev/null +++ b/tests/unit/unit1668.c @@ -0,0 +1,177 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" + +#ifndef CURL_DISABLE_FTP + +struct test_1668 { + const char *in; + const char *out; + bool rc; +}; + +static bool test1668(const struct test_1668 *spec, size_t i) +{ + bool rc; + bool ok = TRUE; + int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; + + rc = ftp_213_date(spec->in, + &year, &month, &day, &hour, &minute, &second); + + if(rc != spec->rc) { + curl_mfprintf(stderr, "test %zu: expected result %d, got %d for %s\n", + i, spec->rc, rc, spec->in); + ok = FALSE; + } + else if(rc) { + char buffer[80]; + curl_msnprintf(buffer, sizeof(buffer), + "%04d-%02d-%02d %02d:%02d:%02d", + year, month, day, hour, minute, second); + if(strcmp(buffer, spec->out)) { + curl_mfprintf(stderr, + "test %zu: (from input %s) got '%s' expected '%s'\n", i, + spec->in, buffer, spec->out); + ok = FALSE; + } + } + + return ok; +} + +static CURLcode test_unit1668(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + + static const struct test_1668 test_specs[] = { + /* fractions are ignored, even when invalid */ + { "19980320114234.123", "1998-03-20 11:42:34", TRUE }, + { "19980320114234.000", "1998-03-20 11:42:34", TRUE }, + { "19980320114234.aaa", "1998-03-20 11:42:34", TRUE }, + { "19980320114234.999", "1998-03-20 11:42:34", TRUE }, + { "19980320114234", "1998-03-20 11:42:34", TRUE }, + { "09980320114234", "0998-03-20 11:42:34", TRUE }, + { "10980320114234", "1098-03-20 11:42:34", TRUE }, + { "19080320114234", "1908-03-20 11:42:34", TRUE }, + { "19900320114234", "1990-03-20 11:42:34", TRUE }, + /* invalid month is accepted */ + { "19980020114234", "1998-00-20 11:42:34", TRUE }, + /* invalid day is accepted */ + { "19980300114234", "1998-03-00 11:42:34", TRUE }, + { "19980320014234", "1998-03-20 01:42:34", TRUE }, + { "19980320110234", "1998-03-20 11:02:34", TRUE }, + { "19980320114204", "1998-03-20 11:42:04", TRUE }, + { "19980320114230", "1998-03-20 11:42:30", TRUE }, + /* not all invalid times are accepted */ + { "19980320256565", "", FALSE }, + { "19980320236565", "", FALSE }, + { "19980320235865", "", FALSE }, + /* invalid February is okay */ + { "19980231114234", "1998-02-31 11:42:34", TRUE }, + { ":9980231114234", "", FALSE }, + { "a9980231114234", "", FALSE }, + { "1:980231114234", "", FALSE }, + { "1a980231114234", "", FALSE }, + { "19:80231114234", "", FALSE }, + { "19b80231114234", "", FALSE }, + { "199:0231114234", "", FALSE }, + { "199c0231114234", "", FALSE }, + { "1998:231114234", "", FALSE }, + { "1998d231114234", "", FALSE }, + { "19980:31114234", "", FALSE }, + { "19980e31114234", "", FALSE }, + { "199802:1114234", "", FALSE }, + { "199802f1114234", "", FALSE }, + { "1998023:114234", "", FALSE }, + { "1998023/114234", "", FALSE }, + { "19980231:14234", "", FALSE }, + { "19980231x14234", "", FALSE }, + { "199802311:4234", "", FALSE }, + { "199802311z4234", "", FALSE }, + { "1998023111:234", "", FALSE }, + { "1998023111&234", "", FALSE }, + { "19980231114:34", "", FALSE }, + { "19980231114#34", "", FALSE }, + { "199802311142:4", "", FALSE }, + { "199802311142!4", "", FALSE }, + { "1998023111423:", "", FALSE }, + { "1998023111423@", "", FALSE }, + + { "19980320114234--", "1998-03-20 11:42:34", TRUE }, + { " 19980320114234", "", FALSE }, + { "1998032011423", "", FALSE }, + { "199803201142", "", FALSE }, + { "19980320114", "", FALSE }, + { "199803201", "", FALSE }, + { "19980320", "", FALSE }, + { "1998032", "", FALSE }, + { "199803", "", FALSE }, + { "19980", "", FALSE }, + { "1998", "", FALSE }, + { "199", "", FALSE }, + { "19", "", FALSE }, + { "1", "", FALSE }, + { "", "", FALSE }, + { "20260320114234.123", "2026-03-20 11:42:34", TRUE }, + { "30260320114234.123", "3026-03-20 11:42:34", TRUE }, + { "40260320114234.123", "4026-03-20 11:42:34", TRUE }, + { "19980320114234\xff", "1998-03-20 11:42:34", TRUE }, + { "1998\x7e""320114234\xff", "", FALSE }, + { "1998\x7f""320114234\xff", "", FALSE }, + { "1998\x80""320114234\xff", "", FALSE }, + { "1998\x81""320114234\xff", "", FALSE }, + { "1998\x82""320114234\xff", "", FALSE }, + { "\xcc", "", FALSE }, + { "\x55", "", FALSE }, + }; + + size_t i; + bool all_ok = TRUE; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + for(i = 0; i < CURL_ARRAYSIZE(test_specs); ++i) { + if(!test1668(&test_specs[i], i)) + all_ok = FALSE; + } + fail_unless(all_ok, "some tests of ftp_213_date() failed"); + curl_global_cleanup(); + + UNITTEST_END_SIMPLE +} + +#else /* without FTP */ + +static CURLcode test_unit1668(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + puts("not tested since FTP is disabled"); + UNITTEST_END_SIMPLE +} + +#endif