From 1b3f00f7942a735e1b2b355e32cf5777576d8859 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Sat, 4 Jan 2025 17:10:25 +0100 Subject: [PATCH] mime: explicitly rewind subparts at attachment time. Subparts may have been previously used as a top-level mime structure and thus not rewound. New test 695 checks the proper functioning in these particular conditions. Reported-by: Qriist on github Fixes #15842 Closes #15911 --- lib/mime.c | 8 +++ tests/data/Makefile.am | 2 +- tests/data/test695 | 78 +++++++++++++++++++++++++ tests/libtest/Makefile.inc | 4 +- tests/libtest/lib695.c | 115 +++++++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 tests/data/test695 create mode 100644 tests/libtest/lib695.c diff --git a/lib/mime.c b/lib/mime.c index 21c40b06cb..cc93e07851 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1561,6 +1561,14 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, } } + /* If subparts have already been used as a top-level MIMEPOST, + they might not be positioned at start. Rewind them now, as + a future check while rewinding the parent may cause this + content to be skipped. */ + if(mime_subparts_seek(subparts, (curl_off_t) 0, SEEK_SET) != + CURL_SEEKFUNC_OK) + return CURLE_SEND_FAIL_REWIND; + subparts->parent = part; /* Subparts are processed internally: no read callback. */ part->seekfunc = mime_subparts_seek; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index fc5e4cef56..f915a3630c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -101,7 +101,7 @@ test652 test653 test654 test655 test656 test658 test659 test660 test661 \ test662 test663 test664 test665 test666 test667 test668 test669 test670 \ test671 test672 test673 test674 test675 test676 test677 test678 test679 \ test680 test681 test682 test683 test684 test685 test686 test687 test688 \ -test689 test690 test691 test692 test693 test694 \ +test689 test690 test691 test692 test693 test694 test695 \ \ test700 test701 test702 test703 test704 test705 test706 test707 test708 \ test709 test710 test711 test712 test713 test714 test715 test716 test717 \ diff --git a/tests/data/test695 b/tests/data/test695 new file mode 100644 index 0000000000..8910af0775 --- /dev/null +++ b/tests/data/test695 @@ -0,0 +1,78 @@ + + + +MIME + + +# Server-side + + +HTTP/1.0 200 OK swsclose +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake + +blablabla + + + + +# Client-side + + +Mime + + +http + + +lib%TESTNUMBER + + + +MIME parts reuse as a child part + + +http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +s/^--------------------------[A-Za-z0-9]*/------------------------------/ +s/boundary=------------------------[A-Za-z0-9]*/boundary=----------------------------/ + + +POST /we/want/%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 196 +Content-Type: multipart/form-data; boundary=---------------------------- + +------------------------------ +Content-Disposition: form-data; name="data" +Content-Type: text/html + +hello +-------------------------------- +POST /we/want/%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 423 +Content-Type: multipart/form-data; boundary=---------------------------- + +------------------------------ +Content-Disposition: form-data +Content-Type: multipart/mixed; boundary=---------------------------- + +------------------------------ +Content-Disposition: attachment; name="data" +Content-Type: text/html + +hello +-------------------------------- + +-------------------------------- + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 286f3d6ba3..b605d4f219 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -48,7 +48,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ lib599 \ lib643 lib645 lib650 lib651 lib652 lib653 lib654 lib655 lib658 \ lib659 lib661 lib666 lib667 lib668 \ - lib670 lib671 lib672 lib673 lib674 lib676 lib677 lib678 lib694 \ + lib670 lib671 lib672 lib673 lib674 lib676 lib677 lib678 lib694 lib695 \ lib1156 \ lib1301 \ lib1485 \ @@ -340,6 +340,8 @@ lib678_LDADD = $(TESTUTIL_LIBS) lib694_SOURCES = lib694.c $(SUPPORTFILES) +lib695_SOURCES = lib695.c $(SUPPORTFILES) + lib1301_SOURCES = lib1301.c $(SUPPORTFILES) $(TESTUTIL) lib1301_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib695.c b/tests/libtest/lib695.c new file mode 100644 index 0000000000..b4f662c5e4 --- /dev/null +++ b/tests/libtest/lib695.c @@ -0,0 +1,115 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "test.h" + +#include "memdebug.h" + + +/* write callback that does nothing */ +static size_t write_it(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + (void) ptr; + (void) userdata; + return size * nmemb; +} + +CURLcode test(char *URL) +{ + CURL *curl = NULL; + curl_mime *mime1 = NULL; + curl_mime *mime2 = NULL; + curl_mimepart *part; + CURLcode res = TEST_ERR_FAILURE; + + /* + * Check proper rewind when reusing a mime structure. + */ + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + + /* First set the URL that is about to receive our POST. */ + test_setopt(curl, CURLOPT_URL, URL); + + /* get verbose debug output please */ + test_setopt(curl, CURLOPT_VERBOSE, 1L); + + /* Do not write anything. */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_it); + + /* Build the first mime structure. */ + mime1 = curl_mime_init(curl); + part = curl_mime_addpart(mime1); + curl_mime_data(part, "hello", CURL_ZERO_TERMINATED); + curl_mime_type(part, "text/html"); + curl_mime_name(part, "data"); + + /* Use first mime structure as top level MIME POST. */ + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime1); + + /* Perform the request, res gets the return code */ + res = curl_easy_perform(curl); + + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() 1 failed: %s\n", + curl_easy_strerror(res)); + else { + /* phase two, create a mime struct using the mime1 handle */ + mime2 = curl_mime_init(curl); + part = curl_mime_addpart(mime2); + + /* use the new mime setup */ + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime2); + + /* Reuse previous mime structure as a child. */ + res = curl_mime_subparts(part, mime1); + + if(res != CURLE_OK) + fprintf(stderr, "curl_mime_subparts() failed: %sn", + curl_easy_strerror(res)); + else { + mime1 = NULL; + + /* Perform the request, res gets the return code */ + res = curl_easy_perform(curl); + + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() 2 failed: %s\n", + curl_easy_strerror(res)); + } + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_mime_free(mime1); + curl_mime_free(mime2); + curl_global_cleanup(); + return res; +}