I am writing a program that pulls an image from a restful server that is delivered in JSON, parsed, processed and sent back to the server. I am having issues on sending the image back. Currently I have it stored in a string and am trying to use CURLFORM_BUFFER to send it back. I have confirmed that the image is in the string by writing it to file. No problems there. My current code is below. I am currently experiencing a seg fault 11 on the post. My code is below.
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "dist/jsoncpp.cpp"
#include "dist/json/json.h"
using namespace std;
static size_t write_data(void *contents, size_t size, size_t nmemb,
void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
void uploadImage(std::string readBuffer){
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);
printf("Image length: %d\n\n", readBuffer.length());
CURLFORMcode code = curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "processedImage",
CURLFORM_BUFFER, "image.jpg",
CURLFORM_BUFFERPTR, readBuffer,
//CURLFORM_BUFFERLENGTH, readBuffer.length(),
CURLFORM_END);
if(code != 0){
printf("Something went wrong in formadd.\n");
}
curl = curl_easy_init();
/* initialize 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, "uploadProcessedImageURL");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
//curl_easy_setopt(curl, CURLOPT_POSTFIELDS, formpost);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "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);
}
}
int main(void)
{
CURL *curl;
CURLcode res;
const cv::_InputArray data;
std::string readBuffer;
char *url = "requestImageFileURL";
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
readBuffer.clear();
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
//printf("%s\n\n", readBuffer.c_str());
printf("Image retrieved.\n");
Json::Value values;
Json::Reader reader;
reader.parse(readBuffer, values);
Json::Value imageArray = values.get("userUploadedImage","default
value");
Json::Value idNumber = values.get("id","default value");
Json::FastWriter fastWriter;
std::string output = fastWriter.write(imageArray);
//cout << output << endl;
std::vector<char> vectordata(output.begin(), output.end());
//for (auto i = vectordata.begin(); i != vectordata.end(); ++i)
//std::cout << *i;
cv::Mat data_mat(vectordata,true);
cv::Mat image(cv::imdecode(data_mat, 1));
std::cout<<"Height: " << image.rows <<" Width: "<<image.cols<<endl;
//cv::namedWindow( "Display Image", CV_WINDOW_AUTOSIZE );
//cv::imshow( "Display Image",image);
//cv::waitKey(0);
uploadImage(readBuffer);
return 0;
}
Related
Im trying to send an email using curl c++, i managed to log in well and when i run the program it works fine, does not throw any error, but the email never comes.
This is my code:
#include <iostream>
#include <curl/curl.h>
static const char *payload_text =
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
"To: " "mailto" "\r\n"
"From: " "mymail" "\r\n"
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#"
"rfcpedant.example.org>\r\n"
"Subject: SMTP example message\r\n"
"\r\n" /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\r\n"
"\r\n"
"It could be a lot of lines, could be MIME encoded, whatever.\r\n"
"Check RFC5322.\r\n";
size_t read_function(char *buffer, size_t size, size_t nmemb,char *data)
{
size_t len;
if(size == 0 or nmemb == 0)
{
return 0;
}
if(data)
{
len = strlen(data);
memcpy(buffer, data, len);
return len;
}
return 0;
}
int main()
{
CURL *curl;
CURLcode res = CURLE_OK;
const char *data = payload_text;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_USERNAME, "mymail");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "my mail");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, "mailto");
curl_easy_setopt(curl, CURLOPT_READDATA,payload_text);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
}
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);
}
return 0;
}
I think the problem is in the curl options READDATA and READUNCTION.
In the documentation says that you have to pass as an argument to READDATA a data pointer.
const char *data = payload_text; is the data pointer, right?
then READFUNCTION takes as an argument a function which return the size of the data and i think that is what size_t read_function(char *buffer, size_t size, size_t nmemb,char *data) is doing.
I am new in this so any advice would be good for me.
I found this to be a helpful starting point:
https://curl.se/libcurl/c/smtp-mail.html
There are two main problems with your code:
Your read_function didn't keep track of how much of the payload has been read so it would keep giving the same content to libcurl over and over and never signal the end of the message.
You were setting CURLOPT_MAIL_RCPT to a string when in fact it should be a struct curl_slist * because there can be multiple recipients.
Here is a fixed example that I tested on my computer and it worked. Private data at the top of the file was modified before posting.
#define USERNAME "david"
#define PASSWORD "xxxxx"
#define MAILTO "david#example.com"
#define MAILFROM "you#example.com"
#define SMTP "smtp://your.smtp.server.example.com:25"
#include <stdio.h>
#include <curl/curl.h>
const char * payload_text =
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
"To: " MAILTO "\r\n"
"From: " MAILFROM "\r\n"
"Subject: SMTP example message with libcurl 6\r\n"
"\r\n"
"Hello world!\r\n";
struct ReadData
{
explicit ReadData(const char * str)
{
source = str;
size = strlen(str);
}
const char * source;
size_t size;
};
size_t read_function(char * buffer, size_t size, size_t nitems, ReadData * data)
{
size_t len = size * nitems;
if (len > data->size) { len = data->size; }
memcpy(buffer, data->source, len);
data->source += len;
data->size -= len;
return len;
}
int main()
{
CURL * curl = curl_easy_init();
if (!curl)
{
fprintf(stderr, "curl_easy_init failed\n");
return 1;
}
curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
curl_easy_setopt(curl, CURLOPT_URL, SMTP);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);
struct curl_slist * rcpt = NULL;
rcpt = curl_slist_append(rcpt, MAILTO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt);
ReadData data(payload_text);
curl_easy_setopt(curl, CURLOPT_READDATA, &data);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
// If your server doesn't have a proper SSL certificate:
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
CURLcode 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);
}
return 0;
}
read_function: you are changing the local pointer buffer that does not affect the byte buffer of a caller. You should copy data to the pointed buffer
memcpy(buffer, data, len);
FYI sizeof(char) is guaranteed to be 1, thus is unneeded.
Another issue - the function never returns 0 that signals all data is sent, it sends the same data again and again. You should return 0 on a second call. Or set the length to the option CURLOPT_POSTFIELDSIZE_LARGE.
See the example smtp-mail.c
I have POST request on python with a lot of settings, and I don't uderstand how their look like in curl.
data_str = '{' + '"username": "{}", "domain_id": {}, "password": {}'.format(login, domain_id, password) + '}'
try:
data = requests.post("https://example.com/v/session",
proxies=proxy,
verify=False,
data=data_str,
headers={"Content-Type": "application/json;charset=UTF-8",
"Accept": "application/json"})
if is_json(data.text):
print(data)
I find that url set parament CURLOPT_URL, headers - CURLOPT_HTTPHEADER. But how set proxy, verify, data ? How get json as in python ?
how to complete the code that it have the same result as in python:
CURL *curl = curl_easy_init();
struct curl_slist *list = NULL;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
list = curl_slist_append(list, "Shoesize: 10");
list = curl_slist_append(list, "Accept:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */
}
In order to get the return data from the curl request, we need a callback function for the CURLOPT_WRITEFUNCTION option.
The proxy, data, verify parameters should be set as following :
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t curlWriter(void *contents, size_t size, size_t nmemb, std::string *s)
{
size_t newLength = size*nmemb;
try
{
s->append((char*)contents, newLength);
}
catch(std::bad_alloc &e)
{
//memory problem
return 0;
}
return newLength;
}
int main()
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
std::string strResponse;
std::string strPostData = "my data post";
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/v/session");
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
//set the proxy
curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.net");
curl_easy_setopt(curl, CURLOPT_PROXYPORT, 8080L);
//verify=False. SSL checking disabled
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
//set the callback function
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strResponse);
/* size of the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strPostData.length() );
/* pass in a pointer to the data - libcurl will not copy */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPostData.c_str() );
/* Execute the request */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
{
std::cerr << "CURL error : " << curl_easy_strerror(res) << std::endl;
}else {
std::cout << "CURL result : " << strResponse << std::endl;
}
curl_easy_cleanup(curl);
}
}
I am trying to make a HTTP Request to retrieve some JSON data; I get the error that curl variable is not initialized though I easy_init() it. Any help on how to go around this error would be very kind!!
Below is my code:
#pragma once
#include "stdafx.h"
#include "RequestJson.h"
#include <string.h>
#include <include/curl/curl.h>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
class RequestJson
{
public:
static std::string RequestJsonString(std::string URL)
{
//set to get the JSON Response on listed loans; open a CSV file and read unemployment and other indices.
::CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
std::ostringstream oss;
//curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_slist_append(headers, "Accept: application/json");
curl_slist_append(headers, "Content-Type: application/json");
curl_easy_cleanup(curl);
if (curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer); //define a write-function below.
res = curl_easy_perform(curl);
if (CURLE_OK == res)
{
char *ct;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
if ((CURLE_OK == res) && ct)
{
return *DownloadedResponse;
}
}
}
}
//parse the JSON String and return the downloaded string.
static std::string *DownloadedResponse;
static int writer(char *data, size_t size, size_t nmemb, std::string *buffer_in)
{
if (buffer_in != NULL)
{
buffer_in->append(data, size * nmemb);
DownloadedResponse = buffer_in;
return size * nmemb;
}
return 0;
}
};
From the curl_easy_cleanup reference:
This function must be the last function to call for an easy session. It is the opposite of the curl_easy_init function and must be called with the same handle as input that a curl_easy_init call returned.
[Emphasis mine]
When you call curl_easy_cleanup it cleans up all resources allocated by curl_easy_init. After that you can't use the CURL pointer any more.
As the reference says: Put it last, when you're done.
I'm trying to upload a file to an http server. I'm getting the 200 OK from the server, but the code below is only transmitting 4 bytes.
size_t myclass::read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
handler->read(buffer, buffer_size); // buffer_size is 100000
size_t res = handler->gcount();
if( res == 0 )
return 0;
ptr = buffer; // buffer is array of char, defined in myclass
size = res;
nmemb = sizeof(char);
return 1;
}
void myclass::upload_function(const std::string& url)
{
CURL *curl;
CURLcode res;
std::ifstream if_file;
if_file.open("/path_to_file", std::ios::binary);
handler = &if_file; // handler is defined in myclass
/* In windows, this will init the winsock stuff */
res = curl_global_init(CURL_GLOBAL_DEFAULT);
/* Check for errors */
if(res != CURLE_OK) {
// failure
return;
}
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "hostname");
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, myclass::read_callback);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked");
chunk = curl_slist_append(chunk, "Content-Type: application/x-mpegURL");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK) {
// failed
}
else
{
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: %0.3f b/sec during %.3f seconds\n",
speed_upload, total_time);
}
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
if_file.close();
}
The callback doesn't seem to copy data to the buffer. It just assigns the local pointer, quite without any effect.
The callback looks like to be a C++ method that can't be used like that as a callback in a C API that doesn't know about C++ objects...
I have used the following code to get to download all the files from the FTP Server
Steps followed are:
1. Creating a FTP list of File
getFTPList(string sHost, string sUser, string sPass, string sUri)
{
CURL *curl;
CURLcode res;
FILE *ftplister;
string host = "ftp://";
host += sHost;
host += "/sample/";
string furl = host + sUri;
string usrpwd = sUser;
usrpwd += ":";
usrpwd += sPass;
/* local file name to store the file as */
ftplister = fopen("ftp-list", "wb"); /* b is binary, needed on win32 */
curl = curl_easy_init();
if(curl) {
/* Get a file listing from sunet */
curl_easy_setopt(curl, CURLOPT_URL, furl.c_str() );
curl_easy_setopt(curl, CURLOPT_USERPWD, usrpwd.c_str());
curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, TRUE);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_list);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ftplister);
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(ftplister); /* close the local file */
}
Use this list to download the files calling the download functions recursively
int main(){
FILE *ftpfile;
string line;
ftpfile = fopen("ftp-list", "r");
ifstream infile("ftp-list");
while ( getline(infile, line) )
{
string url, ofname, surl = "ftp://myhost/uploader/", sfname = "C:\\CNAP\\";
url = surl + line;
ofname = sfname +line;
cout<<url<<" "<<ofname<<endl;
char* theVal ;
char* theStr ;
theVal = new char [url.size()+1];
theStr = new char [ofname.size()+1];
strcpy(theVal, url.c_str());
strcpy(theStr, ofname.c_str());
downloadFile(theVal, theStr);
}
return 0;
}
Now the download function:
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
void downloadFile(const char* url, const char* ofname)
{
CURL *curl;
FILE *fp;
CURLcode res;
curl = curl_easy_init();
if (curl){
fp = fopen(ofname,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
}
It works well when implemented but only to download text files or some files with texts, if I download an image or a docx or a a zip or rar or for that matter any file which is not text it fails, it fails to open after downloading (says invalid file).
I am not sure what am I missing, any help would be appreciated.
I know this is an inefficient way of coding, but I just need the downloads to be right (any file). Working on the efficiency is my next agenda.
PS: Used this method used here
Downloading multiple files with libcurl in C++
Thank you
The file when downloading needs to be opened as a binary file
fp = fopen(ofname,"wb");
Thank you for the help Rob
This is a guess.:
Try clearing the options CURLOPT_TRANSFERTEXT and CURLOPT_CRLF.
Refernce:
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
#include <stdio.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream);
void getFileList(
const string &strHost,
const string &strUri,
const string &strUser,
const string &strPassWord,
const string &strTargetFile
);
void downloadAllFiles(
const string &strFtpListFile,
const string &strHost,
const string &strUri,
const string &strUser,
const string &strPassWord,
const string &strSaveTargetFolder
);
int main(void)
{
string strHost = "ftp://192.168.0.1:22";
string strUri = "/cobus/test/";
string strUser = "cobus";
string strPassWord = "password";
string strTargetFile = "c:\\cobus\\ftpList.txt";
string strSaveDestFolder = "c:\\cobus\\";
getFileList(strHost, strUri, strUser, strPassWord, strTargetFile);
downloadAllFiles(
strTargetFile,
strHost,
strUri,
strUser,
strPassWord,
strSaveDestFolder
);
}
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
void getFileList(const string &strHost, const string &strUri,
const string &strUser, const string &strPassWord, const string &strTargetFile)
{
CURL *curl;
CURLcode res;
FILE *ftplister;
string strSourceFullUri = strHost + strUri;
string strUserPwInfo = strUser + ":" + strPassWord;
/* local file name to store the file as */
ftplister = fopen(strTargetFile.c_str(), "wb"); /* b is binary, needed on win32 */
curl = curl_easy_init();
if(curl)
{
/* Get a file listing from sunet */
curl_easy_setopt(curl, CURLOPT_URL, strSourceFullUri.c_str() );
curl_easy_setopt(curl, CURLOPT_USERPWD, strUserPwInfo.c_str());
curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, TRUE);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ftplister);
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(ftplister); /* close the local file */
}
void downloadAllFiles(const string &strFtpListFile, const string &strHost,
const string &strUri, const string &strUser, const string &strPassWord,
const string &strSaveDestFolder)
{
FILE *ftpFile = NULL;
string strFileName = "";
ifstream infile(strFtpListFile.c_str());
if(!infile.is_open())
{
cerr << "can not open ftpList.txt" << endl;
return ;
}
/* ftpList.txt get data, line by line, processing */
while(getline(infile, strFileName))
{
CURL *curl;
FILE *destFilePath;
CURLcode res;
curl = curl_easy_init();
if (curl)
{
destFilePath = fopen((strSaveDestFolder+strFileName).c_str(),"wb");
curl_easy_setopt(curl, CURLOPT_URL, (strHost+strUri+strFileName).c_str());
curl_easy_setopt(curl, CURLOPT_USERPWD, (strUser+":"+strPassWord).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, destFilePath);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(destFilePath);
}
}// end while
}
I expressed as much as possible to see at once the contents of the Vivian Lobo.
I tried to test to make statements Vivian Lobo. It is good working.
stackOverFlow is hard to write code.
And how can I comment to Vivian lobo article?