Add support for multipart/form-data to HTTPFetch for server announcing
authorShadowNinja <shadowninja@minetest.net>
Thu, 19 Jun 2014 20:00:22 +0000 (16:00 -0400)
committerShadowNinja <shadowninja@minetest.net>
Mon, 30 Jun 2014 17:23:09 +0000 (13:23 -0400)
src/clientmedia.cpp
src/httpfetch.cpp
src/httpfetch.h
src/serverlist.cpp

index b3e7e8e0664b1a8768bf85bb5fe5d5733bd0dcea..c1043c76915533be036f119612a74bf5d5219866 100644 (file)
@@ -266,7 +266,7 @@ void ClientMediaDownloader::initialStep(Client *client)
                        fetchrequest.request_id = m_httpfetch_next_id; // == i
                        fetchrequest.timeout = m_httpfetch_timeout;
                        fetchrequest.connect_timeout = m_httpfetch_timeout;
-                       fetchrequest.post_fields = required_hash_set;
+                       fetchrequest.post_data = required_hash_set;
                        fetchrequest.extra_headers.push_back(
                                "Content-Type: application/octet-stream");
                        httpfetch_async(fetchrequest);
index 69c366ee00734f0a529ab7bfb32983f28075259c..c651055bca5364c969352f10ea139836c7f03c11 100644 (file)
@@ -46,6 +46,7 @@ HTTPFetchRequest::HTTPFetchRequest()
        request_id = 0;
        timeout = g_settings->getS32("curl_timeout");
        connect_timeout = timeout;
+       multipart = false;
 
        useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")";
 }
@@ -184,6 +185,7 @@ struct HTTPFetchOngoing
        std::ostringstream oss;
        char *post_fields;
        struct curl_slist *httpheader;
+       curl_httppost *post;
 
        HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_):
                pool(pool_),
@@ -192,7 +194,8 @@ struct HTTPFetchOngoing
                request(request_),
                result(request_),
                oss(std::ios::binary),
-               httpheader(NULL)
+               httpheader(NULL),
+               post(NULL)
        {
                curl = pool->alloc();
                if (curl != NULL) {
@@ -239,18 +242,52 @@ struct HTTPFetchOngoing
                                                httpfetch_writefunction);
                                curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
                        }
+
                        // Set POST (or GET) data
                        if (request.post_fields.empty()) {
                                curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
-                       }
-                       else {
-                               curl_easy_setopt(curl, CURLOPT_POST, 1);
-                               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
-                                               request.post_fields.size());
-                               curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
-                                               request.post_fields.c_str());
+                       } else if (request.multipart) {
+                               curl_httppost *last = NULL;
+                               for (std::map<std::string, std::string>::iterator it =
+                                                       request.post_fields.begin();
+                                               it != request.post_fields.end();
+                                               ++it) {
+                                       curl_formadd(&post, &last,
+                                                       CURLFORM_NAMELENGTH, it->first.size(),
+                                                       CURLFORM_PTRNAME, it->first.c_str(),
+                                                       CURLFORM_CONTENTSLENGTH, it->second.size(),
+                                                       CURLFORM_PTRCONTENTS, it->second.c_str(),
+                                                       CURLFORM_END);
+                               }
+                               curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
                                // request.post_fields must now *never* be
-                               // modified until CURLOPT_POSTFIELDS is cleared
+                               // modified until CURLOPT_HTTPPOST is cleared
+                       } else {
+                               curl_easy_setopt(curl, CURLOPT_POST, 1);
+                               if (request.post_data.empty()) {
+                                       std::string str;
+                                       for (std::map<std::string, std::string>::iterator it =
+                                                               request.post_fields.begin();
+                                                       it != request.post_fields.end();
+                                                       ++it) {
+                                               if (str != "")
+                                                       str += "&";
+                                               str += urlencode(it->first);
+                                               str += "=";
+                                               str += urlencode(it->second);
+                                       }
+                                       curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+                                                       str.size());
+                                       curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
+                                                       str.c_str());
+                               } else {
+                                       curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+                                                       request.post_data.size());
+                                       curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
+                                                       request.post_data.c_str());
+                                       // request.post_data must now *never* be
+                                       // modified until CURLOPT_POSTFIELDS is cleared
+                               }
                        }
                        // Set additional HTTP headers
                        for (size_t i = 0; i < request.extra_headers.size(); ++i) {
@@ -333,6 +370,10 @@ struct HTTPFetchOngoing
                        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
                        curl_slist_free_all(httpheader);
                }
+               if (post != NULL) {
+                       curl_easy_setopt(curl, CURLOPT_HTTPPOST, NULL);
+                       curl_formfree(post);
+               }
 
                // Store the cURL handle for reuse
                pool->free(curl);
index e02e92b4a90190489bb45abba336abb3befb46c3..aba7482c2686339fc523976ec085f7cabfd13bcd 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <string>
 #include <vector>
+#include <map>
 #include "config.h"
 
 // Can be used in place of "caller" in asynchronous transfers to discard result
@@ -47,10 +48,16 @@ struct HTTPFetchRequest
        // Timeout for the connection phase, in milliseconds
        long connect_timeout;
 
-       // POST data (should be application/x-www-form-urlencoded
-       // unless a Content-Type header is specified in extra_headers)
+       // Indicates if this is multipart/form-data or
+       // application/x-www-form-urlencoded.  POST-only.
+       bool multipart;
+
+       // POST fields.  Fields are escaped properly.
        // If this is empty a GET request is done instead.
-       std::string post_fields;
+       std::map<std::string, std::string> post_fields;
+
+       // Raw POST data, overrides post_fields.
+       std::string post_data;
 
        // If not empty, should contain entries such as "Accept: text/html"
        std::vector<std::string> extra_headers;
index 8a85b33b3d5511a83d755eaa3b6a48bdf83c14fa..315e23688afe36d699d1bc036123cd1e656f1df9 100644 (file)
@@ -230,11 +230,8 @@ void sendAnnounce(std::string action, const std::vector<std::string> & clients_n
        Json::FastWriter writer;
        HTTPFetchRequest fetchrequest;
        fetchrequest.url = g_settings->get("serverlist_url") + std::string("/announce");
-       std::string query = std::string("json=") + urlencode(writer.write(server));
-       if (query.size() < 1000)
-               fetchrequest.url += "?" + query;
-       else
-               fetchrequest.post_fields = query;
+       fetchrequest.post_fields["json"] = writer.write(server);
+       fetchrequest.multipart = true;
        httpfetch_async(fetchrequest);
 }
 #endif