Libcurl error with FTP upload in ubuntu C++ - c++

In windows, this code works file, but now, I want to convert it into ubuntu:
// callback read function to upload file from local to ftp server
size_t read_callback (void* ptr, size_t size, size_t nmemb, FILE *stream){
//return fread(ptr,size,nmemb, (FILE*) stream);
return fread(ptr,size,nmemb,stream);
}
// get file name from a path
string FTPClientConnector::getFileName(string path){
int length = path.size();
for(int i = length - 1; i >= 0; i--){
if(path[i] == '/' || path[i] == '\\'){
return path.substr(i+1, length-i-1);
}
}
}
//function to upload a file to FTP server
int FTPClientConnector::uploadFile(string filePath, string serverPath ){
CURL *curl;
CURLcode res;
FILE *hd_src;
struct stat file_info;
curl_off_t fsize;
char* local_file = new char[filePath.size()+1];
std::copy(filePath.begin(), filePath.end(), local_file);
local_file[filePath.size()] = '\0';
// stat the local file
if(stat(local_file, &file_info)){
printf("couldn't open file\n");
delete local_file;
return -1;
}
// convert URL and username and password to connect to remote server
string urlPath = this->hostName + serverPath;
urlPath += getFileName(filePath);
char *url = new char[urlPath.size() + 1];
std::copy(urlPath.begin(), urlPath.end(), url);
url[urlPath.size()] = '\0';
string userAndPassString = this->userName + ":" + this->password;
char* usernameAndPassword = new char[userAndPassString.size() +1];
std::copy(userAndPassString.begin(), userAndPassString.end(), usernameAndPassword);
usernameAndPassword[userAndPassString.size()] = '\0';
// get the file to open
hd_src = fopen(local_file, "rb");
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl){
/* specify target */
curl_easy_setopt(curl,CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_PORT, this->port);
curl_easy_setopt(curl, CURLOPT_USERPWD, usernameAndPassword);
/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
/* enable uploading */
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* now specify which file to upload */
curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
/* Now run off and do what you've been told! */
res = curl_easy_perform(curl);
if(res != CURLE_OK){
printf("Upload file failed!\n");
delete local_file;
delete url;
delete usernameAndPassword;
return -1;
}
curl_easy_cleanup(curl);
}
fclose(hd_src);
delete local_file;
delete url;
delete usernameAndPassword;
return 0;
}
This is what I call in main.cpp:
FTPClientConnector connector(host,user,password,port);
connector.uploadFile("xml/kingfisher.xml", "/xml_test_upload");
The code above doesn't work in Ubuntu with errors:
220 ProFTPD 1.3.4a Server (Debian) [::ffff:10.244.31.244]
500 PUT not understood
500 AUTHORIZATION: not understood
500 HOST: not understood
550 */*: Forbidden command argument
500 TRANSFER-ENCODING: not understood
500 EXPECT: not understood
500 Invalid command: try being more creative
500 2A2 not understood
Edit: This is my Makefile:
uploader:
g++ -o uploader FTPClientConnector.cpp main.cpp -lcurl

The output seems to indicate that you speak HTTP to a FTP server. Make sure your URL properly uses a FTP:// prefix for FTP, as without a protocol prefix libcurl guesses which protocol you want and it defaults to HTTP...

It appears from your comments that you need to use IPv4. Add this to your list of setopt calls:
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);

Related

Can't login via FTP with cUrl

I'm implementing a function that should upload a file. I'm using cUrl and Qt/C++.
int FtpController::uploadFile()
{
#define UPLOAD_FILE_AS "temp.txt"
#define RENAME_FILE_TO "renamed-and-fine.txt"
CURL* curl;
curl = curl_easy_init();
CURLcode result;
FILE* hd_src;
const auto str = getFileName().toStdString(); // getFileName() provides the full path
const auto* const localFile = str.c_str();
struct stat file_info;
unsigned long fsize;
struct curl_slist* headerlist = nullptr;
static const char buf_1[] = "RNFR " UPLOAD_FILE_AS;
static const char buf_2[] = "RNTO " RENAME_FILE_TO;
/* get the file size of the local file */
if (stat(localFile, &file_info)) {
printf("Couldn't open '%s': %s\n", localFile, strerror(errno));
return 1;
}
fsize = (unsigned long)file_info.st_size;
printf("Local file size: %lu bytes.\n", fsize);
/* get a FILE * of the same file */
hd_src = fopen(localFile, "rb");
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl) {
/* build a list of commands to pass to libcurl */
headerlist = curl_slist_append(headerlist, buf_1);
headerlist = curl_slist_append(headerlist, buf_2);
{
curl_easy_setopt(curl, CURLOPT_PORT, _params.port);
curl_easy_setopt(curl, CURLOPT_USERNAME, _params.username.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, _settingsController->getSystemSettings()->getPass().toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, _params.timeout);
curl_easy_setopt(curl, CURLOPT_URL, _params.url.toString().toStdString().c_str());
/* If you intend to use this on windows with a libcurl DLL, you must use CURLOPT_WRITEFUNCTION as well */
}
/* Now run off and do what you have been told! */
result = curl_easy_perform(curl);
/* Check for errors */
if (result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
/* clean up the FTP commands list */
curl_slist_free_all(headerlist);
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(hd_src); /* close the local file */
curl_global_cleanup();
return 0;
}
It returns: curl_easy_perform() failed: Access denied to remote resource
However, my web-hosting returns:
What's wrong? I triple checked all input.
The file is definitely found, the curl writes in the terminal Local file size: 575131 bytes., though I don't ask it.
If I change the pass, it returns curl_easy_perform() failed: Login denied and if the URL is different or I try uploading anything it says curl_easy_perform() failed: URL using bad/illegal format or missing URL
This is the third approach to sending files via FTP, I need a help.

How to read the JSON Error response returned from Server using Libcurl

I have a requirement where I have to read the error response from backend server which returns 500 Internal Server error. The error response is in JSON Format.
Below is the code snippet used in our application
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
char errbuf[CURL_ERROR_SIZE];
curl = curl_easy_init();
get_request req;
req.buffer =0;
req.len =0;
req.buflen =0;
if(curl)
{
//add url, headers, and parameters to the request
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, endPointUrl);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
headers = curl_slist_append(headers, m_httpHeadAccept);
headers = curl_slist_append(headers, m_httpContentType);
//callback function used to save response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_String);
req.buffer = (unsigned char*) malloc(CHUNK_SIZE);
req.buflen = CHUNK_SIZE;
req.len = 0;
curl_easy_setopt(curl,CURLOPT_WRITEDATA, (void *)&req);
if (!cookie.IsEmpty())
{
headers = curl_slist_append(headers, m_DBAuthCertficate); //What is difference between this and line no 118?
CString pCookie = "DBAuthTicket=" + cookie;
curl_easy_setopt(curl,CURLOPT_COOKIE, pCookie);
}
else
{
headers = curl_slist_append(headers, m_OAuthToken);
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlparam);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
errbuf[0] = 0;
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 512000);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
{
/* if errors have occured, tell us wath's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
return res;
}
m_response = (char*)req.buffer;
m_errDescription = errbuf;
len = req.len;
buflen = req.buflen;
curl_easy_cleanup(curl);
free(req.buffer);
}
return res;
}
/****************************************************************************
Function: CurlWrite_CallbackFunc_String
Description: Read data from the connected URL
Return: String of data and size
****************************************************************************/
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size*nmemb;
get_request* req = (get_request*)userdata;
while(req->buflen < req->len + rLen + 1)
{
req->buffer = (unsigned char*)realloc(req->buffer,req->buflen + CHUNK_SIZE);
req->buflen += CHUNK_SIZE;
}
memcpy(&req->buffer[req->len], contents, rLen);
req->len += rLen;
req->buffer[req->len] = 0;
return rLen;
}
The above code works fine for the Success 200 OK Requests. It reads the JSON Response just fine. However, when I get a 500 Internal Server error, it does not read the JSON Error response that comes along with it. How do I read the JSON response in this scenario?
By setting the CURLOPT_FAILONERROR option to TRUE, you are telling curl_easy_perform() to fail immediately with CURLE_HTTP_RETURNED_ERROR on any HTTP response >= 400. It will not call the CURLOPT_WRITEFUNCTION callback, as it will simply close the connection and not even attempt to read the rest of the response.
To get the response data you want, simply remove the CURLOPT_FAILONERROR option. Curl's default behavior is to deliver the response data to you regardless of the HTTP response code. In which case, curl_easy_perform() will return CURLE_OK, and you can then retrieve the response code using curl_easy_getinfo(CURLINFO_RESPONSE_CODE) to check if the HTTP request was successful or not.
On a side note, since the code shown is written in C++, I would strongly advise you NOT to use a dynamic char[] buffer for get_request::buffer. Not only because you are not handling malloc()/realloc() failures at all, but also because manual memory management should be avoided in C++ in general. Use std::string or std::vector<char> instead, in which case you can eliminate get_request in this code altogether, eg:
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
...
std::string resp;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
...
//curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
/* if errors have occured, tell us what's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return res;
}
m_response = resp.c_str();
m_errDescription = errbuf;
len = resp.size();
buflen = resp.capacity();
curl_easy_cleanup(curl);
return res;
}
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size * nmemb;
static_cast<std::string*>(userdata)->append(static_cast<char*>(contents), rLen);
return rLen;
}

How to include an h file locally, without visual studio?

I have a simple cpp code that uses curl.h as it's header file.
I use vscode, and not interested on linking via gui or w/e
How can I make the include statement work properly with the single project?
I got the header file from https://github.com/curl/curl/blob/master/include/curl/curl.h
Folder structure is like this:
/Folder
+-> curl.h
+-> curli.cpp
the code:
#include "curl.h"
using namespace std;
#define YOUR_URL "https://www.insertyourURLhere.com/"
#define USER_AND_PWD "user:password"
static string gs_strLastResponse;
// Callback to gather the response from the server. Comes in chunks (typically 16384 characters at a time), so needs to be stitched together.
size_t function_pt(void *ptr, size_t size, size_t nmemb, void * /*stream*/)
{
gs_strLastResponse += (const char*)ptr;
return size * nmemb;
}
bool CallServerWithCurl(string strData1, strData2, string& strErrorDescription)
{
CURL* curl = curl_easy_init();
if (curl == NULL)
{
strErrorDescription = "Unable to initialise Curl";
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, (YOUR_URL + "get_result").c_str());
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_USERPWD, MY_USER_AND_PWD); // set user name and password for the authentication
char* data1 = curl_easy_escape(curl, strData1.c_str(), 0);
char* data2 = curl_easy_escape(curl, strData2.c_str(), 0);
string strArguments = "id=" + data1 + "&task=" + data2;
const char* my_data = strArguments.c_str();
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, my_data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(my_data)); // if we don't provide POSTFIELDSIZE, libcurl will strlen() by itself
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // enable verbose for easier tracing
gs_strLastResponse = "";
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_pt); // set a callback to capture the server's response
CURLcode res = curl_easy_perform(curl);
// we have to call twice, first call authenticates, second call does the work
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
strErrorDescription = "Curl call to server failed";
return false;
}
if (!DoSomethingWithServerResponse(gs_strLastResponse))
{
strErrorDescription = "Curl call to server returned an unexpected response";
return false;
}
// extract some transfer info
double speed_upload, total_time;
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
fprintf(stderr, "Speed: %.3f bytes/sec during %.3f seconds\n", speed_upload, total_time);
curl_easy_cleanup(curl);
return true;
}

C++ libcurl how to send FTP file usign STOR and REST

I have an XML that I want to incrementally upload to the ftp server.
The problem is that it does not work for me with an APPEND, because I have to eliminate the final labels. For example.
File on the server
< xml>< root>< a>ssss< /a>< b>kkkkk< /b>< /root>
File in local
< xml>< root>< a>ssss< /a>< b>kkkkk< /b>< c>hhhhh< /c>< /root>
I need to upload from before < /root>.
< xml>< root>< a>ssss< /a>< b>kkkkk< /b>< /root>
----------------------------------------^
My code is the following
size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
curl_off_t nread;
size_t retcode = fread(ptr, size, nmemb, (FILE*) stream);
nread = (curl_off_t) retcode;
fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T " bytes from file\n", nread);
return retcode;
}
bool uploadFile(std::string urlFtp, std::string rutaFtp, std::string archivoOrigen, std::string userPassword, long posicion) {
bool resultado = false;
try {
CURL *curl;
CURLcode res;
FILE *hd_src;
struct stat file_info;
curl_off_t fsize;
/* get the file size of the local file */
if (stat(archivoOrigen.c_str(), &file_info)) {
LOGF_ERROR("Couldn't open '%s': %s\n", archivoOrigen.c_str(), strerror(errno));
return false;
}
fsize = (curl_off_t) file_info.st_size;
LOGF_INFO("Local file size: %" CURL_FORMAT_CURL_OFF_T " bytes.\n", fsize);
hd_src = fopen(archivoOrigen.c_str(), "rb"); // get a FILE * of the same file
curl_global_init(CURL_GLOBAL_ALL); // In windows, this will init the winsock stuff
curl = curl_easy_init(); // get a curl handle
if (curl) {
// build a list of commands to pass to libcurl
struct curl_slist *headerlist = NULL;
std::string cadena(rutaFtp);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); // we want to use our own read function
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // enable uploading
cadena = urlFtp;
cadena.append(rutaFtp);
curl_easy_setopt(curl, CURLOPT_URL, cadena.c_str()); // specify target
curl_easy_setopt(curl, CURLOPT_USERPWD, userPassword.c_str());
if (posicion > 0) {
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, posicion);
}
curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); // now specify which file to upload
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) fsize);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl); // Now run off and do what you've been told!
if (res != CURLE_OK)
LOGF_ERROR("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
else
resultado = true;
curl_slist_free_all(headerlist); // clean up the FTP commands list
curl_easy_cleanup(curl);
}
fclose(hd_src); // close the local file
curl_global_cleanup();
} catch (std::exception &e) {
LOGF_ERROR(e.what());
}
return resultado;
}
I have not been able to solve the problem with CURL. In order to solve it I created a class in C ++ that makes the upload. I noticed the documentation of RFC rfc3659 page 13.
5.5. REST Example
Assume that the transfer of a largish file has previously been
interrupted after 802816 octets had been received, that the previous
transfer was with TYPE=I, and that it has been verified that the file
on the server has not since changed.
C> TYPE I
S> 200 Type set to I.
C> PORT 127,0,0,1,15,107
S> 200 PORT command successful.
C> REST 802816
S> 350 Restarting at 802816. Send STORE or RETRIEVE
C> RETR cap60.pl198.tar
S> 150 Opening BINARY mode data connection
[...]
S> 226 Transfer complete.
Instead of putting the RETR I have put a STOR and it works correctly.

NaCl and curl - can't connect to server

I'm new to NaCl and now I'm starting to have some issues with this. I'm trying to load some data into C++ part of the application from C++ code and I know that I can't access files on HDD from NaCl. So, what I tried to do, was to use curl library to load this files via http from localhost.
So I set up NaCl environment, added curl - everything built fine, then I had an issue that curl couldn't resolve host name, but hopefully I overcame this issue, but then curls says that it can't connect to server (error code 7). I don't know what might be the issue.
I start chrome using this command:
google-chrome 127.0.0.1:5103/myapp --allow-nacl-socket-api=127.0.0.1
--no-sandbox --load-extension="/home/user/Desktop/nacl_sdk/pepper_49/getting_started/web/"
--enable-nacl --user-data-dir="/home/user/Desktop/chrome-dir/"
And code that is trying to load data from localhost is as follows:
static size_t data_write(char* buf, size_t size, size_t nmemb, void* userp) {
printf("start data_write\n");
if (userp) {
std::stringstream& data = *static_cast<std::stringstream*>(userp);
std::streamsize len = size * nmemb;
for (int i = 0; i < len; ++i) {
data << buf[i];
}
return len;
}
printf("end data_write\n");
return 0;
}
CURLcode curl_read(const std::string& url, std::stringstream& os, long timeout = 300) {
CURLcode code(CURLE_FAILED_INIT);
CURL* curl = curl_easy_init();
printf("CURL_READ\n");
if (curl) {
printf("curl is not null\n");
if (CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
&data_write)) &&
CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L)) &&
CURLE_OK ==
(code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)) &&
CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &os)) &&
CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout)) &&
CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()))) {
printf("START curl perform\n");
code = curl_easy_perform(curl);
printf("CURL ERROR (%d) = %s\n", code, curl_easy_strerror(code));
printf("END curl perform\n");
}
curl_easy_cleanup(curl);
}
return code;
}
And the output from console is:
Init curl
Loading data from: http://127.0.0.1/assets/model.obj
CURL_READ
curl is not null
START curl perform
CURL ERROR (7) = Couldn't connect to server
END curl perform
What might be the issue? Is it even possible to use curl in NaCl?