when i use hmac and curl then the programe coredump. And the log run there
"Oct 17 17:58:37 info [6382]: 6385 httpsPost before apped headers"
I got the infomation in gdb
Program terminated with signal 11, Segmentation fault.
#0 0x0819a8e7 in EVP_MD_CTX_md ()
(gdb) where
#0 0x0819a8e7 in EVP_MD_CTX_md ()
Cannot access memory at address 0x4004
these information is no help to me,is anyone know about it?
but when I delete the code:
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, AUTH_KEY, strlen(AUTH_KEY), EVP_sha256(), NULL);
HMAC_Update(&ctx, (unsigned char*)&szJsonData, strlen(szJsonData));
HMAC_Final(&ctx, result, &len);
HMAC_CTX_cleanup(&ctx);
it run well,I don't know why
I got these information :
successfully set certificate verify locations:
CAfile: none
CApath: /etc/ssl/certs
void postHttps(uint32_t uid,std::map& headResult,std::map& bodyResult)
{
char szJsonData[1024];
memset(szJsonData, 0, sizeof(szJsonData));
std::ostringstream requetContent;
requetContent<<"{\"cmuid\":\""<<uid<<"\",\"s\":2}";
strcpy(szJsonData, requetContent.str().c_str());
char bodyBuff[1024];
memset(bodyBuff, 0, sizeof(bodyBuff));
char headBuff[1024];
memset(headBuff, 0, sizeof(headBuff));
uint32_t start = sox::env::now;
uint32_t ssend = sox::env::now;
unsigned char* result;
char sign[1024] = {0};
unsigned int len = 256;
result = (unsigned char*)malloc(sizeof(char) * len);
log(Info,"postHttps accpet");
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, AUTH_KEY, strlen(AUTH_KEY), EVP_sha256(), NULL);
HMAC_Update(&ctx, (unsigned char*)&szJsonData, strlen(szJsonData));
HMAC_Final(&ctx, result, &len);
HMAC_CTX_cleanup(&ctx);
for (unsigned int i = 0; i != len; i++){
int length = strlen(sign);
char *s = sign;
sprintf(s + length, "%02x", (unsigned int)result[i]);
}
free(result);
string auth_head = "Authorization:";
auth_head += UrlEncode(sign).c_str();
log(Info,"postHttps requetContent %s auth_head %s size %u len %u", requetContent.str().c_str(),auth_head.c_str(),requetContent.str().size(),strlen(szJsonData));
try
{
CURL *pCurl = NULL;
CURLcode res;
pCurl = curl_easy_init();
if (NULL != pCurl)
{
curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 2);
curl_easy_setopt(pCurl, CURLOPT_URL, "https://www.xxxx.com/id");
curl_easy_setopt(pCurl, CURLOPT_POST, 1L);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 2);
//curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1);
//curl_easy_setopt(pCurl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, 1);
struct curl_slist *headers = NULL;
log(Info,"httpsPost before apped headers");
headers = curl_slist_append(headers, auth_head.c_str());
headers = curl_slist_append(headers, "Accept: text/plain");
headers = curl_slist_append(headers, "charset:utf-8");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(pCurl, CURLOPT_FORBID_REUSE, 1);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, on_writebuff);
curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, bodyBuff);
curl_easy_setopt(pCurl, CURLOPT_HEADERFUNCTION, header_handler);
curl_easy_setopt(pCurl, CURLOPT_WRITEHEADER, headBuff);
curl_easy_setopt(pCurl,CURLOPT_POSTFIELDS, requetContent.str().c_str());
curl_easy_setopt(pCurl, CURLOPT_POSTFIELDSIZE,requetContent.str().size());
int HTTP_flag = 0;
curl_easy_getinfo(pCurl, CURLINFO_RESPONSE_CODE, &HTTP_flag);
res = curl_easy_perform(pCurl);
log(Info,"httpsPost after curl_easy_perform");
curl_slist_free_all(headers);
curl_easy_cleanup(pCurl);
if (res == CURLE_OK)
{
std::string httpRes = headBuff;
HttpParser::ParseResponseHead(httpRes,headResult);
std::map<std::string,std::string>::iterator it = headResult.find("HTTP");
if(it != headResult.end())
{
std::string& value = it->second;
if(value != "200 OK")
{
log(Error,"postHttps HTTP %s", value.c_str());
}
else
{
json_char* json = (json_char*)bodyBuff;
json_value* value = json_parse(json,strlen(bodyBuff));
process_value(value, 0,bodyResult);
json_value_free(value);
log(Info,"httpsPost headMap %d bodyMap %d", headResult.size(),bodyResult.size());
}
}
}
else
{
log(Info,"curl_easy_perform() failed:%s", curl_easy_strerror(res));
}
}
}
catch (std::exception &ex)
{
log(Info,"curl exception %s", ex.what());
}
log(Info,"httpPost uid %u elapse %u", uid,ssend-start);
}
use ssl,you should init some thinds. like below
void Callbackfee::init_locks(void)
{
int i;
if(m_lockarray == NULL)
{
m_lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
sizeof(pthread_mutex_t));
}
if(m_lockarray != NULL)
{
for (i = 0; i < CRYPTO_num_locks(); ++i) {
pthread_mutex_init(&(m_lockarray[i]), NULL);
}
CRYPTO_set_id_callback(&thread_id);
CRYPTO_set_locking_callback(&lock_callback);
}
}
Related
I written a simple little code based around libcurl that sends a email to myself:
#include <cstdio>
#include <cstring>
#include <curl/curl.h>
static const char* payload_text[] = {
"To: " "<myemail#web.de>" "\r\n",
"From: " "<myemail#web.de>" " (Example User)\r\n",
"Cc: " "<myemail#web.de>" " (Another example User)\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#"
"rfcpedant.example.org>\r\n",
"Subject: Hello\r\n",
"\r\n",
"The body of the message starts here.\r\n",
"\r\n",
"Test.\r\n",
nullptr
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void* ptr, const size_t size, const size_t nmemb, void* userp)
{
auto* const upload_ctx = static_cast<struct upload_status*>(userp);
if (size == 0 || nmemb == 0 || size * nmemb < 1) {
return 0;
}
const auto* const data = payload_text[upload_ctx->lines_read];
if (data) {
const auto len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int _stdcall WinMain(struct HINSTANCE__* hinstance, struct HINSTANCE__* hprevinstance, char* cmdline, int cmdshow)
{
auto res = CURLE_OK;
struct curl_slist* recipients = nullptr;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
auto* const curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "myemail#web.de");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.web.de:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, static_cast<long>(CURLUSESSL_ALL));
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return static_cast<int>(res);
}
NOTE: thats not my password there in curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
The above code works. but i needs libcurl.dll and zlib1.dll
I would like it in a single .exe file
My question is.. Is that possible in c++, i know its possible in c# with using System.Net.Mail or something, or is there are alternative to my code that doesn't require external .dlls
I have an application which is using LibCurl to upload files. I'm showing the upload progress with the percentage with the underneath function which works great. However, I can't get the average upload speed (CURLINFO_SPEED_UPLOAD) nor the time elapsed (CURLINFO_TOTAL_TIME) with the curl_easy_getinfo function. What am I doing wrong here? I can't see much differences with the original example from Libcurl?
static int ReceiveFileProgress(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
if (ultotal > 0) {
string Output;
double curtime = 0;
char ulspeed[10000];
double ulspeed_curl = 0;
double ulnow_db = (double)ulnow;
double ultotal_db = (double)ultotal;
double percentage = ceil((ulnow_db / ultotal_db) * 100);
struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl;
if (curl) {
cout << "CURL OK" << endl;
}
else {
cout << "CURL FAILED" << endl;
}
if (percentage != 100) {
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &ulspeed_curl);
}
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
readable_fs(ulspeed_curl, ulspeed);
stringstream OutputStream;
OutputStream << percentage;
Output = PadString(OutputStream.str(), 3, " ");
OutputStream.str("");
OutputStream << Output << "%";
if (percentage != 100) {
OutputStream << " (" << ulspeed << "/s)";
}
Output = OutputStream.str();
if (Output.length() > SendOffFileMaxProgressLength) {
Output.substr(0, SendOffFileMaxProgressLength - 3);
Output += "...";
}
else {
while (Output.length() < SendOffFileMaxProgressLength) {
Output += ' ';
}
}
cout << Output << '\r';
}
return 0;
}
The curl request:
string PerformCurlRequest(string RequestType, struct curl_httppost &formpost, struct curl_httppost &lastptr) {
CURL *curl;
CURLcode res;
bool UsedProxy;
string CurlResponse;
int LibCurlError = 0;
int ProxyRetryCount = 1;
bool TryWithProxy = true;
struct curl_slist *LibcurlHeaders = NULL;
curl = curl_easy_init();
if (curl) {
host = "url/";
host += RequestType;
host += "/";
LibcurlHeaders = curl_slist_append(LibcurlHeaders, "Expect:");
curl_easy_setopt(curl, CURLOPT_URL, (host).c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, LibcurlHeaders);
curl_easy_setopt(curl, CURLOPT_VERBOSE, CurlVerbose);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, LibcurlResponse);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &CurlResponse);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, GetCurlVerboseData);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ReceiveFileProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
res = curl_easy_perform(curl);
curl_slist_free_all(LibcurlHeaders);
curl_easy_cleanup(curl);
}
return CurlResponse;
}
Send off File:
PerformCurlRequest("SendFile", *formpostReceiveFile, *lastptrReceiveFile)
I don't know what libcurl version you're using or if you perhaps mess up your code somewhere else, but I can certainly make CURLINFO_SPEED_UPLOAD from within a libcurl progress callback in this example code doing HTTP "upload" (using a PUT here for simplicity):
#include <stdio.h>
#include <curl/curl.h>
#include <sys/stat.h>
struct myprogress {
CURL *curl;
};
static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl;
double ulspeed_curl = 0;
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &ulspeed_curl);
fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
" DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
" AVRUP: %.1f"
"\r\n",
ulnow, ultotal, dlnow, dltotal, ulspeed_curl);
return 0;
}
/* send off a random test file lying around */
#define FILENAME "file-to-send"
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct myprogress prog;
curl = curl_easy_init();
if(curl) {
FILE *strace;
struct stat file_info;
prog.curl = curl;
strace = fopen(FILENAME, "rb");
if(!strace)
return 1;
/* get the file size of the local file */
stat(FILENAME, &file_info);
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/");
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
(curl_off_t)file_info.st_size);
curl_easy_setopt(curl, CURLOPT_READDATA, strace);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "%s\n", curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return (int)res;
}
I have this piece of code which sends email via libcurl
#define FROM "<my#gmail.com>"
#define TO "<your#gmail.com>"
static const char *payload_text[] = {
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: Shift\r\n",
"text\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "my#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "pass");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_CAINFO, "google.pem");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
//curl_easy_setopt(curl, CURLOPT_FILE, "edgE0DF.tmp");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return 0;
}
My question is: how can I make a function which takes as arguments from and to and change my global FROM and TO? Is this possible? Because, I know that global variables are initialized before other parts of code and this means that my static const char *payload_text[] that uses FROM and TO will be initialized with wrong addresses.
I have the following code:
#define FROM "<example#gmail.com>"
#define TO "<example2#gmail.com>"
const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO "\r\n",
"From: " FROM "(Example User)\r\n",
"Subject: SMTP TLS example message\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed; boundary=\"KkK170891tpbkKk__FV_KKKkkkjjwq\"\r\n",
"\r\n",
"This is a multipart message in MIME format.",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: text/plain",
"\r\n",
"here goes the text message\r\n",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: image/jpeg; name=\"test.jpg\"\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=\"test.jpg\"\r\n",
"\r\n",
NULL, /*19*/
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq--\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) {
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
std::string readFileToBase64(const char* filename) {
/* converts binary file to base64 */
}
std::string split76(std::string in) {
int lines = in.length() / 76;
for(int i=0;i<lines;i++) {
in.insert((i+1)*76+i*2, "\r\n");
}
return in;
}
int main(void) {
payload_text[19] = split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")).c_str();
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "example2#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "legitpassword");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return (int)res;
}
However, when I compile and run, payload_source() stops reading payload_text after the 18th indice. This completely cuts off my attachment, which I assign to payload_text[19]. Any ideas on why it does this?
The problem is the value returned from split76 is temporary and is destroyed after the assignment has completed. Any attempt to access it after that results in undefined behavior. If you need to store a pointer to the string buffer you can create local std::string to hold it an ensure it lives long enough to be used.
std::string encodedFile(split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")));
payload_text[19] = encodedFile.c_str();
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...