Reputation: 473
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
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
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