Paiku Han
Paiku Han

Reputation: 583

libcURL buffered file upload not working

I am trying to make a file upload using libcurl. but all I get are php errors as if I called the script without any POST array.

code-listing 1: libcurl code

#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <curl/curl.h>

int main(int argc, char *argv[])
{
    CURL *curl;
    CURLcode res;


    std::streampos size;
    char * memblock;

    std::ifstream file ("unnamed.png", std::ios::in|std::ios::binary|std::ios::ate);
    if (file.is_open())
    {
        size = file.tellg();
        memblock = new char [size];
        file.seekg (0, std::ios::beg);
        file.read (memblock, size);
        file.close();

        struct curl_httppost *formpost=NULL;
        struct curl_httppost *lastptr=NULL;

        curl_formadd(&formpost,
                &lastptr,
                CURLFORM_COPYNAME, "file",
                CURLFORM_BUFFER, "unnamed.png",
                CURLFORM_BUFFERPTR, memblock,
                CURLFORM_BUFFERLENGTH, size,
                CURLFORM_END);


        curl = curl_easy_init();

        if(curl) {

            curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/upload_file.php");

            curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);


            res = curl_easy_perform(curl);
            if(res != CURLE_OK)
              fprintf(stderr, "curl_easy_perform() failed: %s\n",
                      curl_easy_strerror(res));

            curl_easy_cleanup(curl);
            curl_formfree(formpost);
        }

    }

    return 0;
}

Here are the php errors I get:

( ! ) Notice: Undefined index: file in F:\wamp\www\upload_file.php on line 4
Call Stack
#   Time    Memory  Function    Location
1   0.0003  240408  {main}( )   ..\upload_file.php:0

Notice: Undefined index: file in F:\wamp\www\upload.php on line 8
Call Stack
#   Time    Memory  Function    Location
1   0.0003  242624  {main}( )   ..\upload.php:0

Furthermore I wonder if I shouldn't add the content type in some way. I know the CURLFORM_FILE has CURLFORM_CONTENTTYPE for that but I didn't see anything for CURLFORM_BUFFER.

EDIT: here are the HTML upload form and the PHP file upload processing script.

code-listing 2: html form

<form enctype="multipart/form-data" action="upload.php" method="POST">
    Send this file: <input name="file" type="file" />
    <input type="submit" value="Send File" />
</form>

code listing 3: php fileupload processing script

$uploaddir = '/';
$uploadfile = $uploaddir . basename($_FILES['file']['name']);

echo "<p>";

if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
  echo "File is valid, and was successfully uploaded.\n";
    echo '<pre>';
      print_r($_FILES);
    print "</pre>";
} else {
   echo "Upload failed";
}

?> 

The HTML code and PHP script (i used for the test) will show when run on a web browser the message

File is valid, and was successfully uploaded

followed by a print of the array. which is not the case when I run my libcurl code. I get the php error messages and the text

Upload failed

Upvotes: 0

Views: 1932

Answers (1)

Serge Ballesta
Serge Ballesta

Reputation: 149025

It is a nasty bug, only evident when found. The problem is indeed in the call to curl_formadd. When there is an incorrect parameter (which is the case, see below), the function does nothing, and simply return a not null value. You did not notice it because you forgot to test the return code (and did not expect an error here ...)

So when you do you post, you do an empty post (no part) which fully explains the error in the php script.

The problem is that the parameter after CURLFORM_BUFFERLENGTH must be a long, and you pass a std::streampos.

The fix is trivial (I also add the content type) :

long lsize = size.seekpos();

CURLFORMcode code = curl_formadd(&formpost,
    &lastptr,
    CURLFORM_COPYNAME, "file",
    CURLFORM_BUFFER, "unnamed.png",
    CURLFORM_BUFFERPTR, memblock,
    CURLFORM_BUFFERLENGTH, lsize,
    CURLFORM_CONTENTTYPE, "image/png",
    CURLFORM_END);
if (code != 0) {
    // to avoid other problems ...
}

I could control with wireshark that the file bytes are effectively posted.

Upvotes: 4

Related Questions