willy
willy

Reputation: 473

error: Invalid Signature when uploading files on dropbox

I am trying to develop a desktop application that uses dropbox. I am using C++, libcurl (for the requests) liboauthcpp (for the authentication) and the Rest API (from dropbox).

Since I have successed the authentication and some more operations (list and download file), I cannot manage to upload a file. I am receiving the following error: {"error": "Invalid signature."}

Here is my code:

m_upload_url = "https://api-content.dropbox.com/1/files/sandbox/";

        OAuth::Consumer consumer(m_consumer_key, m_consumer_secret);
        OAuth::KeyValuePairs access_token_resp_data = OAuth::ParseKeyValuePairs(m_access_token_Response);
        OAuth::Token access_token = OAuth::Token::extract( access_token_resp_data );
        OAuth::Client  oauth = OAuth::Client(&consumer, &access_token);
        string oAuthQueryString = oauth.getURLQueryString( OAuth::Http::Post, m_upload_url);

        string upload_Request = (string(m_upload_url) + string("?") + oAuthQueryString);

       CURL *curl;
         CURLcode res;

         struct curl_httppost *formpost=NULL;
         struct curl_httppost *lastptr=NULL;
         struct curl_slist *headerlist=NULL;
         static const char buf[] = "Expect:";

         curl_global_init(CURL_GLOBAL_ALL);

         /* Fill in the file upload field */
         curl_formadd(&formpost,
                      &lastptr,
                      CURLFORM_COPYNAME, "file",
                      CURLFORM_FILE, "C:/Users/Desktop/textfile.txt",
                      CURLFORM_END);

         /* Fill in the filename field */
         curl_formadd(&formpost,
                      &lastptr,
                      CURLFORM_COPYNAME, "name",
                      CURLFORM_COPYCONTENTS, "textfile",
                      CURLFORM_END);


         /* Fill in the submit field too, even if this is rarely needed */
         curl_formadd(&formpost,
                      &lastptr,
                      CURLFORM_COPYNAME, "submit",
                      CURLFORM_COPYCONTENTS, "send",
                      CURLFORM_END);

         curl = curl_easy_init();
         /* initalize custom header list (stating that Expect: 100-continue is not
            wanted */
         headerlist = curl_slist_append(headerlist, buf);
         if(curl) {
           /* what URL that receives this POST */
           curl_easy_setopt(curl, CURLOPT_URL, upload_Request.c_str());
             /* only disable 100-continue header if explicitly requested */
           curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1L);
           curl_easy_setopt(curl, CURLOPT_POST, 1L);
           curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
           curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
           curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
           curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
           curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

           /* Perform the request, res will get the return code */
           res = curl_easy_perform(curl);
           string response = string(gData);

           qDebug() << QString::fromStdString(response);

           /* Check for errors */
           if(res != CURLE_OK)
           qDebug() << "curl_easy_perform() failed: %s\n" << curl_easy_strerror(res);

           /* always cleanup */
           curl_easy_cleanup(curl);

           /* then cleanup the formpost chain */
           curl_formfree(formpost);
           /* free slist */
           curl_slist_free_all (headerlist);
         }

and here is the writeFunction:

char gData[1024*1024];
unsigned int gDataLen = 0;

size_t writeFunction( char *ptr, size_t size, size_t nmemb)
{
    memcpy( &( gData[gDataLen] ), ptr, (size * nmemb) );
    gDataLen += (size * nmemb);
    gData[ gDataLen ] = '\0';
    return (size * nmemb);
}

Any ideas, please?

EDIT:

Here is the request that the present code produces: https://api-content.dropbox.com/1/files/sandbox/?oauth_consumer_key=xxxxxxxxxxxxxxx&oauth_nonce=13xxxxxx83cf&oauth_signature=xxxxxx%xxxxxxxxxxxxxxxxxx%xxxxx%xx&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1371107283&oauth_token=xxxxxxxxxxxxxxx&oauth_version=1.0

and the request that I produse with the plaintext method: https://api-content.dropbox.com/1/files/sandbox/?oauth_version=1.0&oauth_signature_method=PLAINTEXT&oauth_consumer_key=xxxxxxxxxxxxxxx&oauth_token=xxxxxxxxxxxxxxx&oauth_signature=xxxxxxxxxxxxxxx&xxxxxxxxxxxxxxx

Response that O get in both cases: {"error": "Invalid signature."}

Upvotes: 0

Views: 1169

Answers (2)

user94559
user94559

Reputation: 60153

What OAuth signature method are you using? If it's anything but plaintext, then I believe the problem is that you're not passing the body of the HTTP request to getURLQueryString. The body needs to be part of the string that's signed. This explains why you've been able to get a few GET operations to work; those requests don't have a body.

Side note: it looks like you're using the POST form of file upload, which, as the docs say is considerably more complex than PUT. I think your life will be better if you switch. :-) For example, I think the submit field that you're adding will probably cause the call to not work.

EDIT: Here's a full working example using a manually-constructed plaintext signature. Fill in the app key, app secret, token, and token secret, and be sure to have a file called "hello.txt" for this to work:

#include <stdio.h>
#include <curl/curl.h>
#include <iostream>
#include <fstream>

using namespace std;

size_t read_data(void *ptr, size_t size, size_t nmeb, void *stream)
{
    return fread(ptr,size,nmeb,(FILE*)stream);
}

int main(void)
{
  CURL *curl;
  CURLcode res;

  FILE * rfp = fopen("hello.txt", "r");

  string appkey = "<APP KEY>";
  string appsecret = "<APP SECRET>";
  string token = "<ACCESS TOKEN>";
  string token_secret = "<ACCESS TOKEN SECRET>";

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://api-content.dropbox.com/1/files_put/sandbox/hello.txt");

    struct curl_slist *headers = NULL;
    string header = "Authorization: OAuth oauth_version=\"1.0\", oauth_signature_method=\"PLAINTEXT\", oauth_consumer_key=\"" + appkey + "\", oauth_token=\"" + token + "\", oauth_signature=\"" + appsecret + "&" + token_secret + "\"";
    headers = curl_slist_append(headers, header.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    curl_easy_setopt(curl, CURLOPT_PUT, 1L);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_data);
    curl_easy_setopt(curl, CURLOPT_READDATA, rfp);

    res = curl_easy_perform(curl);

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

    curl_easy_cleanup(curl);
  }

  fclose(rfp);

  return 0;
}

Upvotes: 1

willy
willy

Reputation: 473

Has anyone used liboauthcpp? I suspect that my mistake is in the way I use getURLQueryString.. I am passing the url into this function, but I am not sure if I have to pass the data too. And unfortunately, there is no documentation for this library.

Upvotes: 0

Related Questions