Adding to char array isn't working - c++

I'm trying to read a text file line by line, and add each line to a char array. But the lines aren't added, at all.
//This is the default char array that comes with the cURL code.
char *text[]={
"one\n",
"two\n",
"three\n",
" Hello, this is CURL email SMTP\n",
NULL
};
/*Now we're going to replace that char array, with an array that holds the contents of a textfile.
We'll read a textfile out line by line, and add each line to the char array.
*/
void makemailmessage()
{
text[0] = '\0'; //Clear text
text[0] = "testy\n"; //First line in new char array
//Read the text file, add each line to the char array.
string line;
ifstream myfile ("C:\\Users\\admin\\Downloads\\bbb.txt");
int counter;
counter = 1;
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
//Convert the string variable "line" to a char (a)
char *a=new char[line.size()+1];
a[line.size()]=0;
memcpy(a,line.c_str(),line.size());
//Add \n to the end of "a" (new char will be "str")
char str[80];
strcpy (str,a);
strcat (str,"\n");
//Add "str" to the char array "text"
text[counter] = str;
text[counter+1] = "test\n"; //Also added this for testing purposes
write_data("C:\\Users\\admin\\Downloads\\checkit.txt", str); //Also for testing purposes
//Increase counter by 2 because we added two new items to the char array "text"
counter++;
counter++;
}
myfile.close();
text[counter-1] = "testy2\n"; //Ad another text line
text[counter] = NULL; //End char array
}
Each str is written correctly to checkit.txt but for some reason it is not added to the char array because I end up with the char array looking like this:
testy
test
test
testy2
What am I doing wrong?
UPDATE2:
The reason I am trying to make a char array is because the cURL function I am using needs a char array to form the email body. This is the important part of the cURL code.
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct WriteThis *pooh = (struct WriteThis *)userp;
const char *data;
if(size*nmemb < 1)
return 0;
data = text[pooh->counter]; //This part is using the char array.
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
pooh->counter++;
return len;
}
return 0;
}
Here's the full code

Okay, after chatting on this a bit more, here is a fix:
C++ version
Full code file here: https://gist.github.com/1342118#file_test.cpp
Replace the relevant code with:
#include <vector>
#include <fstream>
// ...
std::vector<std::string> text;
static int read_text(char* fname)
{
//Read the text file, add each line to the char array.
std::ifstream myfile (fname);
std::string line;
while (std::getline(myfile, line))
text.push_back(line + '\n');
return 0;
}
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
/* This was already in. */
struct WriteThis *pooh = (struct WriteThis *)userp;
if(size*nmemb < 1)
return 0;
if (pooh->counter < text.size())
{
const std::string& data = text[pooh->counter];
memcpy(ptr, data.data(), data.length());
pooh->counter++; /* advance pointer */
return data.length();
}
return 0; /* no more data left to deliver */
}
Pure C version
Full code file here: https://gist.github.com/1342118#file_test.c
Replace
//This is the default char array that comes with the cURL code.
char *text[]={
"one\n",
"two\n",
"three\n",
" Hello, this is CURL email SMTP\n",
NULL
};
With
char **text = 0;
static int read_text(char* fname)
{
unsigned capacity = 10;
int linecount = 0;
// free_text(); see below
text = realloc(text, capacity*sizeof(*text));
FILE* file = fopen(fname, "r");
if (!file)
{ perror("Opening file"); return 1; }
char buf[2048];
char* line = 0;
while (line = fgets(buf, sizeof(buf), file))
{
if (linecount>=capacity)
{
capacity *= 2;
text = realloc(text, capacity*sizeof(*text));
}
text[linecount++] = strdup(line);
}
fclose(file);
return 0;
}
Hook it up in you main function, e.g. like so
if (argc<2)
{
printf("Usage: %s <email.eml>\n", argv[0]);
exit(255);
} else
{
printf("Reading email body from %s\n", argv[1]);
if (0 != read_text(argv[1]))
exit(254);
}
Or, if you so prefer, just calling read_text("C:\\Users\\admin\\Downloads\\bbb.txt") :)
To really top things off, don't forget to reclaim memory when you're done - properly:
#include "curl/curl.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <string.h>
#define GetCurrentDir getcwd
#define USERNAME "obscured#gmail.com"
#define PASSWORD "obscured"
#define SMTPSERVER "smtp.gmail.com"
#define SMTPPORT ":587"
#define RECIPIENT "<obscured#gmail.com>"
#define MAILFROM "<obscured#gmail.com>"
#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000
/* Note that you should include the actual meta data headers here as well if
you want the mail to have a Subject, another From:, show a To: or whatever
you think your mail should feature! */
char **text = 0;
void free_text()
{
if (text)
{
char** it;
for (it = text; *it; ++it)
free(*it);
free(text);
text = 0;
}
}
static int read_text(char* fname)
{
unsigned capacity = 10;
int linecount = 0;
free_text();
text = realloc(text, capacity*sizeof(*text));
FILE* file = fopen(fname, "r");
if (!file)
{ perror("Opening file"); return 1; }
char buf[2048];
char* line = 0;
while (line = fgets(buf, sizeof(buf), file))
{
if (linecount>=capacity)
{
capacity *= 2;
text = realloc(text, capacity*sizeof(*text));
}
text[linecount++] = strdup(line);
}
if (linecount>=capacity)
text = realloc(text, (++capacity)*sizeof(*text));
text[linecount] = 0; // terminate
fclose(file);
return 0;
}
struct WriteThis {
int counter;
};
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
/* This was already in. */
struct WriteThis *pooh = (struct WriteThis *)userp;
const char *data;
if(size*nmemb < 1)
return 0;
data = text[pooh->counter];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
pooh->counter++; /* advance pointer */
return len;
}
return 0; /* no more data left to deliver */
}
static struct timeval tvnow(void)
{
/*
** time() returns the value of time in seconds since the Epoch.
*/
struct timeval now;
now.tv_sec = (long)time(NULL);
now.tv_usec = 0;
return now;
}
static long tvdiff(struct timeval newer, struct timeval older)
{
return (newer.tv_sec-older.tv_sec)*1000+
(newer.tv_usec-older.tv_usec)/1000;
}
int main(int argc, char** argv)
{
if (argc<2)
{
printf("Usage: %s <email.eml>\n", argv[0]);
exit(255);
} else
{
printf("Reading email body from %s\n", argv[1]);
if (0 != read_text(argv[1]))
exit(254);
}
CURL *curl;
CURLM *mcurl;
int still_running = 1;
struct timeval mp_start;
char mp_timedout = 0;
struct WriteThis pooh;
struct curl_slist* rcpt_list = NULL;
pooh.counter = 0;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(!curl)
return 1;
mcurl = curl_multi_init();
if(!mcurl)
return 2;
rcpt_list = curl_slist_append(rcpt_list, RECIPIENT);
/* more addresses can be added here
rcpt_list = curl_slist_append(rcpt_list, "<others#example.com>");
*/
curl_easy_setopt(curl, CURLOPT_URL, "smtp://" SMTPSERVER SMTPPORT);
curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_SSLVERSION, 0);
curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0);
curl_multi_add_handle(mcurl, curl);
mp_timedout = 0;
mp_start = tvnow();
/* we start some action by calling perform right away */
curl_multi_perform(mcurl, &still_running);
while(still_running) {
struct timeval timeout;
int rc; /* select() return code */
fd_set fdread;
fd_set fdwrite;
fd_set fdexcep;
int maxfd = -1;
long curl_timeo = -1;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);
/* set a suitable timeout to play around with */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
curl_multi_timeout(mcurl, &curl_timeo);
if(curl_timeo >= 0) {
timeout.tv_sec = curl_timeo / 1000;
if(timeout.tv_sec > 1)
timeout.tv_sec = 1;
else
timeout.tv_usec = (curl_timeo % 1000) * 1000;
}
/* get file descriptors from the transfers */
curl_multi_fdset(mcurl, &fdread, &fdwrite, &fdexcep, &maxfd);
/* In a real-world program you OF COURSE check the return code of the
function calls. On success, the value of maxfd is guaranteed to be
greater or equal than -1. We call select(maxfd + 1, ...), specially in
case of (maxfd == -1), we call select(0, ...), which is basically equal
to sleep. */
//rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
if (tvdiff(tvnow(), mp_start) > MULTI_PERFORM_HANG_TIMEOUT) {
fprintf(stderr, "ABORTING TEST, since it seems "
"that it would have run forever.\n");
break;
}
switch(rc) {
case -1:
/* select error */
break;
case 0: /* timeout */
default: /* action */
curl_multi_perform(mcurl, &still_running);
break;
}
}
curl_slist_free_all(rcpt_list);
curl_multi_remove_handle(mcurl, curl);
curl_multi_cleanup(mcurl);
curl_easy_cleanup(curl);
curl_global_cleanup();
free_text();
return 0;
}

I'm trying to read a text file line by line, and add each line to a
char array.
Since this is C++, why not use an std::vector<string> and use the std::string version of getline?
The std::string class will look after the memory needed to hold a string of any sort of length, and the std::vector class will worry about the memory needed to hold an "array", so to speak, of strings.
EDIT: Actually looking at your code again, you do use an std::string and then allocate memory to store it as an array of chars, and then store pointers to those strings in some fixed sized array, test. Why go to all that trouble when, as I mentioned above, you can use an std::vector<string> to hold all your std::string objects? Mind = boggled.
EDIT2: Couldn't you also use cURLpp as a C++ wrapper for cURL? I haven't used either so I can't comment on the effectiveness of it.

What am I doing wrong?
For one, this:
char str[80];
strcpy (str,a);
strcat (str,"\n");
//Add "str" to the char array "text"
text[counter] = str;
str is allocated on the stack, with block-wide scope. Then you enter that pointer in an array with a greater scope. This is usually a recipe for disaster - a rather impressive segmentation fault or whatever the equivalent is on your platform.
In this case, due to its use in a loop, your program will either crash, or - if the stars have the proper alignment - you will end up with all pointers in your array pointing to the same out-of-scope string, namely the one that was last read.
Why do you even go into that trouble, when you have already dynamically allocated a in the heap?
By the way, mixing char[] arrays (and the associated standard C library functions) with C++ strings is NOT a good idea. Not even an acceptable one. OK, it a bad idea. Just stick to C++ strings...

Related

Same code works/gives error in different projects

I am using visual studio 2019.
When I try to run this from a new project it works totally fine. Sends the email and doesn't give any error.
But when I try to run this from my server code it gives this error:
To be more precise:
Project1/DEBUG works fine.
Project1/Release works fine.
Project2/DEBUG works fine.
Project2/Release gives runtime error.
"Exception thrown: read access violation.
this was 0xF. occurred"
And compiler is redirected to "xstring" code page, line 2873.
part from xstring
basic_string& assign(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) {
// assign [_Ptr, _Ptr + _Count)
if (_Count <= _Mypair._Myval2._Myres) { // LINE 2873
_Elem* const _Old_ptr = _Mypair._Myval2._Myptr();
_Mypair._Myval2._Mysize = _Count;
_Traits::move(_Old_ptr, _Ptr, _Count);
_Traits::assign(_Old_ptr[_Count], _Elem());
return *this;
}
I've tried with totally same code (with copy paste) but still no use.
Project properties are the same. I've checked it several times for any change in properties but there is not even a single difference.
email.h
#pragma warning(disable : 4996) //_CRT_SECURE_NO_WARNINGS
#ifndef EMAIL_H
#define EMAIL_H
#include <vector>
#include <string>
#include "curl/curl.h"
// Enable one of the below depending on your operating system
#define WINDOWS_H
//#define LINUX_H
class Email
{
public:
// default constructor
Email();
// sets who the email is going to
void setTo(const std::string to);
// sets who the email came from
void setFrom(const std::string from);
// sets the cc
void setCc(const std::string to);
// sets the subject of the email
void setSubject(const std::string subject);
// set the body of the email
void setBody(const std::string body);
// sets the smtp username
void setSMTP_username(const std::string username);
// sets the smtp password
void setSMTP_password(const std::string password);
// sets the SMTP HOST
void setSMTP_host(const std::string host);
// adds a binary attachment to the email
void addAttachment(const std::string filepath);
// removes an attachment from the email (Not implemented yet)
void removeAttachment(const std::string filepath);
// removes all attachments
void removeAllAttachments();
// contructs the final email
void constructEmail();
// clears the contents of the email
void clearEmailContents();
// void send email
int send() const; // returns a CURL error code
// dumps the email contents for debugging
void dump() const;
private:
// smtp information
std::string smtp_user;
std::string smtp_password;
std::string smtp_host;
// email data
std::string to;
std::string from;
std::string cc;
std::string subject;
std::string body;
// vector which stores the email data
std::vector<std::string> email_contents;
std::vector<std::string> attachments;
// length of the above vector
int numberOfLines;
};
#endif
email.cpp
#include <iostream>
#include <ctime>
#include <string.h>
// #include "stdafx.h"
#include "email.h"
#define MAX_LEN 255 // this must be divisible by 3 otherwise the SMTP server won't be able to decode the attachment properly
#define ENCODED_LEN 341
// offsets into the email template
#define BOUNDARY 5
#define END_LINE 8
#define ATTACH_TYPE 10
#define ATTACH_TRANSFER 11
#define ATTACH_DEPOSITION 12
#define END_OF_TRANSMISSION_BOUNDARY 15
#define END_OF_TRANSMISSION 16
#ifdef WINDOWS_H
#define DIR_CHAR '\\'
#else
#define DIR_CHAR '/'
#endif
using namespace std;
// base64 encoding functions
void base64_encode(char* input_buf, char* output_buf, size_t input_size);
void encodeblock(char* in, char* out, int len);
// callback function
static size_t payload_source(void* ptr, size_t size, size_t nmemb, void* userp);
// base64 encoding table
const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// global vector this is TEMP
vector<string> global_vec;
// struct used by the CURL callback
struct upload_status {
int lines_read;
};
static const char* email_template[] = {
/* i = 0 */
"User-Agent: DXF Viewer agent\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed;\r\n",
" boundary=\"------------030203080101020302070708\"\r\n",
"\r\nThis is a multi-part message in MIME format.\r\n",
"--------------030203080101020302070708\r\n", // use this type of boundary for subsequent attachments
"Content-Type: text/plain; charset=utf-8; format=flowed\r\n",
"Content-Transfer-Encoding: 7bit\r\n",
/* i = 8 (BODY) */
"\r\n", /* empty line to divide headers from body, see RFC5322 */
"--------------030203080101020302070708\r\n", /* i = 9 */
/* Template for binary attachments i = 10 */
"Content-Type: application/octet-stream\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment;\r\n",
/* Filename here */
"\r\n",
/* Encoded base64 contents here */
"\r\n",
"--------------030203080101020302070708--\r\n", // this type of boundary indicates that there are no more parts i = 15
"\r\n.\r\n",
NULL
};
Email::Email()
{
// empty constructor
}
void Email::setTo(const string to)
{
this->to = to;
}
void Email::setFrom(const string from)
{
this->from = from;
}
void Email::setCc(const string cc)
{
this->cc = cc;
}
void Email::setSubject(const string subject)
{
this->subject = subject;
}
void Email::setBody(const string body)
{
this->body = body;
}
void Email::setSMTP_username(const string user)
{
this->smtp_user = user;
}
void Email::setSMTP_password(const string pass)
{
this->smtp_password = pass;
}
void Email::setSMTP_host(const string hostname)
{
this->smtp_host = hostname;
}
void Email::addAttachment(const string file_path)
{
/*FILE* fp = NULL;
char buffer[MAX_LEN + 1] = { 0 };
char encodedBuffer[ENCODED_LEN] = { 0 };
char tempBuffer[MAX_LEN] = { 0 };
char* filename = NULL;
unsigned int fileSize = 0;
unsigned int bytesCopied = 0;
int bytesToCopy = 0;
int bytesRead = 0;
fp = fopen(file_path.c_str(), "rb");
if (fp) {
// get the file size
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
// copy the attachment header
string type(email_template[ATTACH_TYPE]);
this->attachments.push_back(type);
string encodingType(email_template[ATTACH_TRANSFER]);
this->attachments.push_back(encodingType);
string deposition(email_template[ATTACH_DEPOSITION]);
this->attachments.push_back(deposition);
// extract the filename from the path
filename = (char*)strrchr(file_path.c_str(), DIR_CHAR);
if (filename != NULL) {
filename += 1;
// push the filename
snprintf(tempBuffer, MAX_LEN, " filename=\"%s\"\r\n", filename);
string filename(tempBuffer);
this->attachments.push_back(filename);
string endLine(email_template[END_LINE]);
this->attachments.push_back(endLine);
// copy the file MAX_LEN bytes at a time into the attachments vector
while (bytesCopied < fileSize) {
// determine how many bytes to read
if (bytesCopied + MAX_LEN > fileSize) {
bytesToCopy = fileSize - bytesCopied;
}
else {
bytesToCopy = MAX_LEN;
}
// read from the file
memset(buffer, 0, MAX_LEN + 1);
bytesRead = fread(buffer, sizeof(char), bytesToCopy, fp);
// encoded the data read
memset(encodedBuffer, 0, ENCODED_LEN);
base64_encode(buffer, encodedBuffer, bytesRead);
// setup the encoded string so that we can push it to attachments
string line(encodedBuffer);
line += endLine;
this->attachments.push_back(line);
// update the number of bytes we have copied
bytesCopied += bytesToCopy;
}
this->attachments.push_back(endLine);
string boundary(email_template[BOUNDARY]);
this->attachments.push_back(boundary);
}
else {
removeAllAttachments();
cout << "Failed to extract filename!" << endl;
}
// close the file
fclose(fp);
}
else {
cout << "Unable to open file." << endl;
}
*/
}
void Email::constructEmail(void)
{
int i = 0;
char buffer[MAX_LEN];
string boundary(email_template[BOUNDARY]);
// time stuff
time_t rawtime;
struct tm* timeinfo;
// store all the email contents in a vector
// TO:
snprintf(buffer, MAX_LEN, "To: %s\r\n", this->to.c_str());
string line1(buffer);
this->email_contents.push_back(line1);
// FROM:
memset(buffer, 0, MAX_LEN);
snprintf(buffer, MAX_LEN, "From: %s\r\n", this->from.c_str());
string line2(buffer);
this->email_contents.push_back(line2);
// CC:
memset(buffer, 0, MAX_LEN);
snprintf(buffer, MAX_LEN, "Cc: %s\r\n", this->cc.c_str());
string line3(buffer);
if (this->cc.length() > 0) {
this->email_contents.push_back(line3);
}
// Subject:
memset(buffer, 0, MAX_LEN);
snprintf(buffer, MAX_LEN, "Subject: %s\r\n", this->subject.c_str());
string line4(buffer);
this->email_contents.push_back(line4);
// time:
time(&rawtime);
timeinfo = localtime(&rawtime);
memset(buffer, 0, MAX_LEN);
strftime(buffer, sizeof(buffer), "%d/%m/%Y %I:%M:%S +1100\r\n", timeinfo);
string time_str(buffer);
this->email_contents.push_back(time_str);
cout << time_str << endl;
for (i = 0; i < END_LINE; i++) { // other stuff e.g user-agent etc
string line(email_template[i]);
this->email_contents.push_back(line);
}
// add in the body
string endLine(email_template[END_LINE]);
this->email_contents.push_back(endLine);
memset(buffer, 0, MAX_LEN);
snprintf(buffer, MAX_LEN, "%s", this->body.c_str()); // Body:
string line5(buffer);
this->email_contents.push_back(line5); // body
this->email_contents.push_back(endLine); // \r\n
this->email_contents.push_back(boundary); // boundary
// add in the attachments
for (i = 0; i < attachments.size(); i++) {
this->email_contents.push_back(this->attachments[i]);
}
// add the last boundary with the two hyphens
string lastBoundary(email_template[END_OF_TRANSMISSION_BOUNDARY]);
this->email_contents.push_back(lastBoundary);
// specify that we don't want to send any more data
string endTransmission(email_template[END_OF_TRANSMISSION]);
this->email_contents.push_back(endTransmission);
}
/*
This function was taken and modified from:
https://curl.haxx.se/libcurl/c/smtp-ssl.html
*/
int Email::send(void) const
{
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();
global_vec = this->email_contents;
if (curl) {
/* Set username and password */
curl_easy_setopt(curl, CURLOPT_USERNAME, this->smtp_user.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, this->smtp_password.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); // allows emails to be sent
/* This is the URL for your mailserver. Note the use of smtps:// rather
* than smtp:// to request a SSL based connection. */
curl_easy_setopt(curl, CURLOPT_URL, this->smtp_host.c_str());
/* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you. */
#ifdef SKIP_PEER_VERIFICATION
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif
/* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure. */
#ifdef SKIP_HOSTNAME_VERIFICATION
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
/* Note that this option isn't strictly required, omitting it will result
* in libcurl sending the MAIL FROM command with empty sender data. All
* autoresponses should have an empty reverse-path, and should be directed
* to the address in the reverse-path which triggered them. Otherwise,
* they could cause an endless loop. See RFC 5321 Section 4.5.5 for more
* details.
*/
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, this->from.c_str());
/* Add two recipients, in this particular case they correspond to the
* To: and Cc: addressees in the header, but they could be any kind of
* recipient. */
recipients = curl_slist_append(recipients, this->to.c_str());
if (this->cc.length() > 0) {
recipients = curl_slist_append(recipients, this->cc.c_str());
}
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* We're using a callback function to specify the payload (the headers and
* body of the message). You could just use the CURLOPT_READDATA option to
* specify a FILE pointer to read from. */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* Since the traffic will be encrypted, it is very useful to turn on debug
* information within libcurl to see what is happening during the
* transfer */
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* Send the message */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* Free the list of recipients */
curl_slist_free_all(recipients);
/* Always cleanup */
curl_easy_cleanup(curl);
}
return (int)res;
}
void Email::removeAllAttachments()
{
this->attachments.clear();
}
void Email::clearEmailContents()
{
this->email_contents.clear();
this->attachments.clear();
}
void Email::dump(void) const
{
int i = 0;
cout << "Email contents: " << endl;
for (i = 0; i < this->email_contents.size(); i++) {
cout << this->email_contents[i];
if (i == 20) {
break;
}
}
cout << "\n\nEmail attachments: " << endl;
for (i = 0; i < this->attachments.size(); i++) {
cout << this->attachments[i];
if (i == 5) {
break;
}
}
}
void base64_encode(char* input_buf, char* output_buf, size_t input_size)
{
char in[3];
char out[4];
size_t len = input_size;
*output_buf = 0;
for (size_t i = 0; i < len; )
{
int buf3_len = 0;
for (int j = 0; j < 3; ++j)
{
in[j] = input_buf[i++];
if (i > len)
in[j] = 0;
else
buf3_len++;
}
if (len > 0)
{
encodeblock(in, out, buf3_len);
strncat(output_buf, out, 4);
}
}
}
void encodeblock(char* in, char* out, int len)
{
out[0] = (char)b64_table[(int)(in[0] >> 2)];
out[1] = (char)b64_table[(int)(((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4))];
out[2] = (char)(len > 1 ? b64_table[(int)(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6))] : '=');
out[3] = (char)(len > 2 ? b64_table[(int)(in[2] & 0x3f)] : '=');
}
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;
}
if (upload_ctx->lines_read >= 0 && upload_ctx->lines_read < global_vec.size())
{
data = global_vec[upload_ctx->lines_read].c_str();
if (data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
}
return 0;
}
main.cpp
#include <iostream>
#include "email.h"
using namespace std;
int main(void)
{
Email e;
int curlError = 0;
// e.dump();
e.setTo("receiver#example.com");
e.setFrom("sender#example.com");
e.setSubject("Test Email");
e.setCc("");
e.setBody("Do not reply to this email");
e.setSMTP_host("smtp://smtp.mail.com:587");
e.setSMTP_username("sender#example.com");
e.setSMTP_password("sender_password");
e.addAttachment("");
// e.addAttachment("email.h");
// e.addAttachment("main.cpp");
e.constructEmail();
e.dump();
curlError = e.send();
if (curlError) {
cout << "Error sending email!" << endl;
}
else {
cout << "Email sent successfully!" << endl;
}
return 0;
}
What is the reason this same code runs perfectly on one project but doesn't work on another?
EDIT:
error happens in Email::send(void) on the line :
global_vec2 = this->email_contents;
but I cannot try/catch it. it doesn't throws exception.
EDIT2 :
Problem is caused by static vector. It doesn't exist, doesn't initialize. I don't understand what's causing this project to prevent static std::vectors from being created.

Dynamic Char Allocation [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I have a loop that brings me a buffer each time, I want to concatenate the buffers in a char array named data and then I want write this data into a file, but the problem is that I can't concatenate the char without seg-fault/core-dumped.
Here's the code (I also tried new char[sizeof] instead of malloc):
int size = getFrames() * 4;
char * buffer = (char *) malloc(size);
char * data;
char * tmp;
for (loops = 100; loops > 0; loops--) {
buffer = getBuffer();
tmp = (char *) malloc(sizeof(data));
tmp = data;
data = (char *) malloc(sizeof(tmp) + sizeof(buffer));
data = strcat(tmp, buffer);
}
free(tmp);
free(data);
free(buffer);
You are not managing your buffers correctly, not even close. In fact, there are a LOT of mistakes in your code. Every one of your sizeof() calls is wrong. You are leaking buffer, tmp, and data on every loop iteration. You are using strcat() incorrectly. And worse, you are processing binary audio data using string functions? No wonder why your code is failing.
Try something more like this instead:
char* getBuffer(int *bufsize)
{
...
*bufsize = ...; // <-- number of bytes being returned
return ... ; // <-- malloc()'ed pointer to actual bytes
}
...
char *data = NULL;
int datasize = 0;
int allocsize = 0;
char *buffer;
int bufsize;
for (loops = 100; loops > 0; loops--) {
buffer = getBuffer(&bufsize);
if ((datasize + bufsize) > allocsize)
{
// round new size up to next 1K boundary
int tmpsize = (datasize + bufsize + 1023) & ~1023;
char *tmp = (char*) realloc(data, tmpsize);
if (!tmp) {
free(buffer);
break;
}
data = tmp;
allocsize = tmpsize;
}
memcpy(data + datasize, buffer, bufsize);
datasize += bufsize;
free(buffer);
}
FILE *f = fopen(..., "wb");
fwrite(data, datasize, 1, f);
fclose(f);
free(data);
Or simpler:
char *buffer;
int bufsize;
FILE *f = fopen(..., "wb");
for (loops = 100; loops > 0; loops--) {
buffer = getBuffer(&bufsize);
if (fwrite(buffer, bufsize, 1, f) != 1) {
free(buffer);
break;
}
free(buffer);
}
fclose(f);
However, you tagged your question as C++, even though you are not actually using C++ code. The C++ way to handle this would look more like this instead:
#include <vector>
#include <string>
#include <fstream>
void getBuffer(std::vector<char> &buffer)
{
...
buffer.resize(...); // <-- number of bytes
// fill buffer with bytes as needed...
}
...
std::string data;
for (loops = 100; loops > 0; loops--) {
std::vector<char> buffer;
getBuffer(buffer);
data.append(buffer.data(), buffer.size());
}
std::ofstream f(..., std::ios_base::binary);
f.write(data.c_str(), data.size());
f.close();
Or simpler:
#include <string>
#include <fstream>
void appendBuffer(std::string &buffer)
{
...
buffer.append(...); // <-- append bytes
}
...
std::string data;
for (loops = 100; loops > 0; loops--) {
appendBuffer(data);
}
std::ofstream f(..., std::ios_base::binary);
f.write(data.c_str(), data.size());
f.close();
Or simpler:
#include <fstream>
bool outputBuffer(std::ostream &out)
{
...
return out.write(...); // <-- write bytes
}
...
std::ofstream f(..., std::ios_base::binary);
for (loops = 100; loops > 0; loops--) {
if (!outputBuffer(f)) break;
}
f.close();

stack check fail in sha-1 c++

I'm having a __stack_chk_fail in the main thread.
I have no idea why is this happening?
I got the codes from this website:
http://www.packetizer.com/security/sha1/
Im trying to add a function to compute the digest of a file using the example.
.h file
#include <stdio.h>
#include <string>
std::string digestFile( char *filename );
.cpp file
std::string SHA1::digestFile( char *filename )
{
Reset();
FILE *fp = NULL;
if (!(fp = fopen(filename, "rb")))
{
printf("sha: unable to open file %s\n", filename);
return NULL;
}
char c = fgetc(fp);
while(!feof(fp))
{
Input(c);
c = fgetc(fp);
}
fclose(fp);
unsigned message_digest[5];
if (!Result(message_digest))
{ printf("sha: could not compute message digest for %s\n", filename); }
std::string hash;
for (int i = 0; i < 5; i++)
{
char buffer[8];
int count = sprintf(buffer, "%08x", message_digest[i]);
if (count != 8)
{ printf("converting unsiged to char ERROR"); }
hash.append(buffer);
}
return hash;
}
__stack_chk_fail occurs when you write to invalid address.
It turns out you do:
char buffer[8];
int count = sprintf(buffer, "%08x", message_digest[i]);
C strings are NUL-terminated. That means that when sprintf writes 8 digits, it adds 9-th char, '\0'. But buffer only has space for 8 chars, so the 9-th goes past the end of the buffer.
You need char buffer[9]. Or do it the C++ way with std::stringstream, which does not involve any fixed sizes and thus no risk of buffer overrun.

Creating a C++ class to read data from a website to a string using libcurl

I am using http://ubuntuforums.org/showthread.php?t=781021 as a guide for how to write a c++ program that use libcurl to check a website and downloads the text to a c++ app. When I copy the code to my visual studio project, it works with only slight modification. However, I hit a snag trying to convert it to class based files.
I created a .h and .cpp files to run the query. What I didn't realize before was that handle_data was not a typical function, since it is not given any arguments or even () in the midway down Viewer.cpp. This is how the above site did it, and it worked, but I'm at a loss as to why or how to convert it now.
Does anyone know whats going on here or how to fix it?
Thanks!
Viewer.h:
#pragma once
#include <string>
#include "curl.h"
class Viewer
{
public:
Viewer(void);
~Viewer(void);
std::string view(std::string q);
private:
size_t handle_data(void *ptr, size_t size, size_t nmemb, void *stream);
std::string contents;
};
Viewer.cpp:
#include "stdafx.h"
#include "Viewer.h"
#include <iostream>
Viewer::Viewer(void)
{
std::cout << "ViewerCreated!\n";
}
Viewer::~Viewer(void)
{
}
size_t Viewer::handle_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
int numbytes = size*nmemb;
// The data is not null-terminated, so get the last character, and replace
// it with '\0'.
char lastchar = *((char *) ptr + numbytes - 1);
*((char *) ptr + numbytes - 1) = '\0';
contents.append((char *)ptr);
contents.append(1,lastchar);
*((char *) ptr + numbytes - 1) = lastchar; // Might not be necessary.
return size*nmemb;
}
std::string Viewer::view(std::string q)
{
char* url = "www.google.com";
CURL* curl = curl_easy_init();
if(curl)
{
// Tell libcurl the URL
curl_easy_setopt(curl,CURLOPT_URL, url);
// Tell libcurl what function to call when it has data
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,handle_data);
// Do it!
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == 0)
std::cout << contents << std::endl;
else
std::cerr << "Error: " << res << std::endl;
}
return contents;
}
You should not replace the last character of the chunk that you have received. Instead you should force that the buffer containing the appended blocks has a '\0' just after the end of the data. This way it can be printed, but if you are going to store that in a file, you should just write the bytes that you have received.
This has always worked for me:
struct MemoryStruct chunk;
chunk.memory=NULL;
chunk.size = 0;
...
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
And the callback:
struct MemoryStruct {
char *memory;
size_t size;
};
static void *myrealloc(void *ptr, size_t size)
{
if(ptr)
return realloc(ptr, size);
else
return malloc(size);
}
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)data;
mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
if (mem->memory) {
memcpy(&(mem->memory[mem->size]), ptr, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
}
return realsize;
}
handle_data is used as a parameter for curl_easy_setopt. What that means is that you have to program what handle_data does when it receives the next data chunk (providing the data chunk is what curl will do for you).
handle_data is being passed to curl_easy_setopt as a callback function, which it will call when there's data available. Because of that, handle_data must be called as a static function (that is, one not receiving an object pointer, because cURL doesn't have one). However, you're not declaring it as static. Even if you do, you are accessing contents inside it, which is invalid.

Multithreaded curl application has memory allocation problems

I'm working on an application in C++ that threads and hands a bunch of threads URLs for cURL to download in parallel.
I'm employing a method that should be safe to download images and videos, etc. I uses memcpy instead of assuming the data is a string or character array.
I pass each thread a structure, thread_status, for a number of things. The structure lets the parent process know the thread is done downloading. It also stores the data cURL is downloading and keeps track of it's size as cURL returns more buffers for writing.
I pass a (void *) pointer that points to each structure that's allocated at initialization to each thread that does the downloading. The first page is downloaded properly, after that I keep getting errors from realloc().
Here is the simplest example that illustrates my problem. This sample is not multi-threaded but uses a similar structure to keep track of itself.
#include <string>
#include <assert.h>
#include <iostream>
#include <curl/curl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define NOT_READY 1
#define READY 0
using namespace std;
struct thread_status {
int id;
pthread_t *pid;
int readyState;
char *url;
void *data;
size_t bufferlen;
size_t writepos;
int initialized;
} ;
size_t static
writefunction( void *ptr, size_t size,
size_t nmemb, void *userdata)
{
size_t nbytes = size*nmemb;
struct thread_status **this_status;
this_status = (struct thread_status **) userdata;
if (!(*this_status)->initialized){
(*this_status)->data = (void *)malloc(1024);
(*this_status)->bufferlen = 1024;
(*this_status)->writepos = 0;
(*this_status)->initialized = true;
}
if ((*this_status)->bufferlen < ((*this_status)->writepos + nbytes)){
(*this_status)->bufferlen = (*this_status)->bufferlen + nbytes;
(*this_status)->data = realloc((*this_status)->data, (size_t) ((*this_status)->writepos + nbytes));
}
assert((*this_status)->data != NULL);
memcpy((*this_status)->data + (*this_status)->writepos, ptr, nbytes);
(*this_status)->writepos += nbytes;
return nbytes;
}
void *pull_data (void *my_struct){
struct thread_status *this_struct;
this_struct = (struct thread_status *) my_struct;
this_struct->initialized = false;
cout<<(char *)this_struct->url<<"\n";
CURL *curl;
curl = curl_easy_init();
size_t rc = 0;
while(true){
curl_easy_setopt(curl,
CURLOPT_WRITEFUNCTION, writefunction);
curl_easy_setopt(curl,
CURLOPT_WRITEDATA, (void *) &this_struct);
curl_easy_setopt(curl,
CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl,
CURLOPT_URL, (char *)this_struct->url);
if (curl_easy_perform(curl) != 0){
cout<<"curl did not perform\n";
exit(1);
} else {
if (this_struct->data != NULL){
// Use a binary write.
rc = fwrite(this_struct->data, this_struct->writepos, 1, stdout);
free(this_struct->data);
} else {
cout<<"Data is NULL\n";
}
}
// Tell the babysitter the thread is ready.
this_struct->readyState = READY;
// This would pause the thread until the parent thread has processed the data in it.
// while(this_struct->readyState == READY){;}
// Now get ready for another round!
this_struct->writepos = (size_t) 0;
this_struct->initialized = false;
this_struct->bufferlen = (size_t) 0;
break;
}
curl_easy_cleanup(curl);
return (void *)"a";
}
int main(){
char *urls[] = { "http://www.example.com/", "http://www.google.com", "http://www.touspassagers.com/", "http://www.facebook.com/" };
int i=0;
struct thread_status mystatuses[4];
for (i=0;i<4;i++){
struct thread_status my_status;
char *data;
my_status.id = i;
my_status.readyState = NOT_READY;
my_status.url = urls[i];
my_status.data = data;
my_status.bufferlen = 0;
my_status.writepos = 0;
my_status.initialized = false;
mystatuses[i] = my_status;
}
for (i=0;i<4;i++){
cout<<"pulling #"<<i<<"\n";
pull_data((void *)&mystatuses[i]);
}
}
If anyone can enlighten me as to the source of my error or a remedy for it I would appreciate it.
You might consider using valgrind to help locate the source of the memory problems.
Got it!
Apparently 1KB isn't enough memory to handle the first cURL buffer. I changed 1024 to nbytes and it works!
Before the memory memcpy put in the buffer ran over the allocated memory resulting in corruptions.
I did a post about it if anyone cares to see the full implementation:
http://www.touspassagers.com/2011/01/a-working-curlopt_writefunction-function-for-libcurl/