I am creating a very simple webserver, as practice in C++ and sockets. I use OSX.
The code sample is from inside the while(1) loop, a connection has been made and I am starting to process the header. This code works for all text-files but it dosn't work with images. And I figure that I can't use the same method to read text-files and images since images isn't separeted with lines. But how do I read the image data to send through the socket? I might not even be able to use a string, do I have to use char*?
string strFile = "htdocs" + getFileFromHeader(httpRequestHeader);
string strExt = getFileExtension(strFile);
string httpContent = "";
ifstream fileIn(strFile.c_str(), ios::in); // <-- do I have to use ios::binary ?
if(!fileIn)
{
// 404
cout << "File could not be opened" << endl;
httpContent = httpHeader404;
}
else
{
// 200
string contentType = getContentType(strExt);
cout << "Sending " << strFile << " -- " << contentType << endl;
string textInFile = "";
while (fileIn.good())
{
getline (fileIn, textInFile); // replace with what?
httpContent = httpContent + textInFile + "\n";
}
httpContent = httpHeader200 + newLine + contentType + newLine + newLine + httpContent;
}
// Sending httpContent through the socket
The question is about how to read the image data.
*EDIT 2011-05-19 *
So, this is an updated version of my code. The file have been opened with ios::binary, however, there are more problems.
httpContent = httpHeader200 + newLine + contentType + newLine + newLine;
char* fileContents = (char*)httpContent.c_str();
char a[1];
int i = 0;
while(!fileIn.eof())
{
fileIn.read(a, 1);
std::size_t len = std::strlen (fileContents);
char *ret = new char[len + 2];
std::strcpy ( ret, fileContents );
ret[len] = a[0];
ret[len + 1] = '\0';
fileContents = ret;
cout << fileContents << endl << endl << endl;
delete [] ret;
i++;
}
The problem is that is seems that the char * fileContents empty itself every ~240 chars. How can that be? Is there some sort of limit to some of theese functions that they only accept certain length?
Open the file for binary read, store the data in a char* array large enough, then send that array.
As #Blackbear said, but don't forget to send the corresponding HTML-Headers like contentEncoding, transferEncoding, etc. For simplicity try to send the binary Data of the image encoded in base64.
Related
I have a file which is read in as binary. It has some basic plain text at the top and image data after. Due to a process I cannot control I need to ensure that a certain string of characters is not present in the plain text representation of the data. Meaning that if you opened the image data as plain text and the character sequence was present that is a problem. My thought was to read in the data, convert to hex, and replace the the instances of that sequence. The process would then be reversed to restore the file.
std::ifstream stream;
std::stringstream ss;
if (stream.bad())
do.something();
length = stream.rdbuf()->pubseekoff(0, std::ios_base::end);
data = new char[length];
stream.rdbuf()->pubseekoff(0, std::ios_base::beg);
stream.read(data, length);
stream.close();
ss << std::hex;
for (int i = 0; i < length; ++i)
ss << std::setw(2) << std::setfill('0') << (int)data[i];
const std::string& tmp = ss.str();
const char* cstr = tmp.c_str();
CString hexStr(cstr);
This reads the top portion of the file fine but introduces 'FF' characters into the rest of the data. My c++ isn't great so I'm looking for some help as to why and how to correctly do this. The rest looks like:
hexStr.Replace(character sequence to replace, replacement);
std::ofstream datafile(pathToFile, std::ios_base::binary | std::ios_base::out);
char buf[3];
buf[2] = 0;
std::stringstream input(hexStr.GetString());
input.flags(std::ios_base::hex);
while (input)
{
input >> buf[0] >> buf[1];
long val = strtol(buf, nullptr, 16);
datafile << static_cast<unsigned char>(val & 0xff);
}
I have .txt file and there are lines:
username1:123456789:etc:etc:etc:etc
username2:1234:etc:etc:etc:etc
username3:123456:etc:etc:etc:etc
username4:1234567:etc:etc:etc:etc
username1 - username;
123456789 - password;
etc - more text.
I have code to read file and find line where is username what I need. Also code change password, but there is problem if new password is longer that old one then it looks like:
username3:11111111111tc:etc:etc:etc
if new password is shorter then it looks like:
username1:111111789:etc:etc:etc:etc
I have length of new password, but how can I get length of old one and replace it properly?
My code
include<iostream>
#include<fstream>
#include <cstring>
using namespace std;
int main() {
int i=0;
bool found = false;
string line, username;
char newpass[255] = "555555555555555";
long length, plen;
cout<<"Insert username: ";
cin>>username;
username+=":";
fstream changeLINE("/.../testaDoc.txt");
if (!changeLINE) {
cout << "Can't find the file or directory!" << endl;
}
else
while (getline(changeLINE, line) && !found) {
i++;
if (line.find(username) != string::npos) {
length = changeLINE.tellg();
changeLINE.seekg((length - line.length()) + username.length() - 1);
changeLINE.write("", strlen(newpass));
cout << line << " line " << i << endl;
found = true;
}
}
if (!found)
cout << "User with username = " << username << " NOT FOUND!";
changeLINE.close();
}
I'm working on Linux, writing in C++.
Edit
Maybe there are way to add string in text, but not replace it and also way to erase string by not replacing it? Then I could read length of old password compare it with new password and delete/add letters in string to replace it properly.
Unless the line you want to replace is the same length as the new one, you need a different strategy-
This is a strategy you can use:
Create temporary file
Write the lines you dont want to change straight to the temporary file. The line you want to change, you replace with the new line
close both files.
delete original file
rename temporary file to the original files name
Or you could as mentioned in the comments read all lines into memory, e.g. a vector of lines, replace the one you want to change, and then write all lines back into the file, replacing the former content. If the new line is shorter than the previous, you can trunctate the file using e.g: How to truncate a file while it is open with fstream
Could this work:
std::vector<std::string> lines;
while (std::getline(changeLINE, line))
{
i++;
if (line.find(username) != std::string::npos) {
std::cout << line << " line " << i << std::endl;
found = true;
std::string newline = username + ":" + newpass + line.substr(line.find(":", username.length() + 2)) ;
lines.push_back(newline);
}
else
{
lines.push_back(line);
}
}
changeLINE.close();
std::ofstream ofs;
ofs.open("/.../testaDoc.txt", std::ofstream::out | std::ofstream::trunc);
for(auto& s: lines)
ofs << s << std::endl;
ofs.close();
first, I write a template log function as the following:
void Utils::_logMap(std::map<K, V> map) {
cout << "===================[map]=====================\n";
for(auto it: map) {
auto key = it.first;
auto val = it.second;
cout << key << " = " << val << endl;
}
// testing code
cout << "\n>>> for testing: \n";
cout << map.at("S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE") << endl;
cout << map.at("S_HELLO") << endl;
cout << map.at("S_TEST") << endl;
}
then I create a std::map object, and read the text content from a file(a localized language txt file with UTF-8 encoding).
static std::map<string, string> localizedStrings;
then I print out all of its key-value.
Utils::_logMap(localizedStrings);
the result shows:
===================[map]=====================
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = teest1312
S_TEST = Test777
>>> for testing:
teest1312
hello123
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: map::at: key not found
the last line out_of_range exception is caused by this code:
cout << map.at("S_HELLO") << endl; // for testing, app will crash
but how can this be!!?!? the map object indeed contains a key named S_HELLO. why the app gets exception when I try to access the key via typing a constant string value!? I don't get it!
UPDATED:
Well, this is the primary reading content function codes:
string Utils::getLocalizedString(const string key) {
LanguageType type = Application::getInstance()->getCurrentLanguage();
const char* code = Application::getInstance()->getCurrentLanguageCode();
cclog("language type: %d, code: %s", type, code);
const char * fileName;
switch (type) {
case LanguageType::ENGLISH:
fileName = "Localized_en.txt";
break;
case LanguageType::CHINESE:
fileName = "Localized_zh.txt";
break;
default:
fileName = "Localized_en.txt";
break;
}
if (localizedStrings.empty()) {
// Initialize variables needed
ssize_t fileSize = 0;
unsigned char * fileContents = NULL;
string line, fullPath, contents;
// Get absolute path of file
fullPath = FileUtils::getInstance()->fullPathForFilename( fileName );
cout << "fullPath: " << fullPath << endl;
// Get data of file
if( !fullPath.empty() ) {
fileContents = CCFileUtils::getInstance()->getFileData( fullPath.c_str( ) , "rb", &fileSize );
cout << "fileContents: " << fileContents << endl;
contents.assign(fileContents,fileContents+fileSize-1);
// Create a string stream so that we can use getline( ) on it
istringstream fileStringStream( contents );
// Get file contents line by line
while ( std::getline( fileStringStream, line ) )
{
//filter the valid string of one line
if (line.find("/*",0) == string::npos && line.find("//",0) == string::npos) {
std::string::size_type validPos= line.find('=',0);
if ( validPos != string::npos)
{
std::string keyStr = line.substr(0,validPos-1);
std::string subStr = line.substr(validPos+1,line.size()-1); // get valid string
//trim space
keyStr.erase(0, keyStr.find_first_not_of(" \t")); // remove head white-space
keyStr.erase(keyStr.find_last_not_of(" \t") + 1); // remove tail white-space
subStr.erase(0, subStr.find_first_not_of(" \t")); // remove head white-space
subStr.erase(subStr.find_last_not_of(" \t") + 1); // remove tail white-space
//trim \"
keyStr.erase(0, keyStr.find_first_not_of("\""));
keyStr.erase(keyStr.find_last_not_of("\"") + 1);
subStr.erase(0, subStr.find_first_not_of("\""));
//trim ; character and last \" character
subStr.erase(subStr.find_last_not_of(";") + 1);
subStr.erase(subStr.find_last_not_of("\"") + 1);
//replace line feed with \n
string::size_type pos(0);
string old_value("\\n");
if((pos=subStr.find(old_value))!=string::npos)
{
for(; pos!=string::npos; pos+=1)
{
if((pos=subStr.find(old_value,pos))!=string::npos)
{
subStr.erase(pos, 2);
subStr.insert(pos, 1, '\n');
}
else
break;
}
}
localizedStrings.insert(std::pair<std::string, std::string>(keyStr,subStr));
}
}
}
}
//must delete fileContents
if (fileContents!= NULL) {
delete [] fileContents;
fileContents = NULL;
}
}
cout << "key: " + key << endl;
logMap(localizedStrings);
if( localizedStrings.find(key) != localizedStrings.end() ) {
return localizedStrings.at(key);
}
cclog("return key instead");
return key;
}
UPDATED
OMG, I found it seems to be relative with the position of text in the file. only the key-value in the FIRST line of file will cause the problem.
but I still don't know why........
this is the content of the localized string file:
S_TEST = Test777
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = teest13124
see? if I access the key S_HELLO and S_WARNINGxxx, it works fine. but if I access the key S_TEST, it will crash....
If you have this problem only with the first key in your file, then you have most likely a BOM (Byte Order Mark) at the beginning of your file. These are 2 invisible bytes, which are inserted by default in UTF-8 files by many Windows editors.
To remove the BOM open the file with Notepad++. In the Encoding menu select Encode in UTF-8 without BOM. Then save the file again.
Well... this crash problem seems to be due to reading certain invisible characters from the localized txt file at the first line.
so I do a work-around solution: just insert a comment text at first line, then problem solved. It might be the file is UTF-8 format, so it contains some UTF-8 format header at the beginning of file, and it should not be read into the string map.
// !!LEAVE THE FIRST LINE EMTPY!!
S_TEST = Test777
S_HELLO = hello123
S_WARNING_UNABLE_TO_PUT_INTO_WEREHOUSE = Unable to put into werehouse
the difference between map::at and map::[] is that at will check if the key exists in a map. If key does not exist it will throw an exception. Operator [] will add a new key in a map instead. In your case the map doesn't crash but throws exception. Either add a key into the map or handle exception or use operator [] instead of at.
I got a small problem.. Currently im writing a small tool which connects to a Server, downloads some blowfish encrypted Data into a char Array (char buffer[512]) and saves that Array into a file (fstream ios::binary).
Then later i reopen that file (ifstream ios::binary) and send it back. This time i read it into a std::string and not a char[].
My Problem now is that the Data is corrupted after i send them back.
What would be the best way to acomblish this? Is it safe to use char[] and std::string? Or does that somehow corrupt the data? (i guess so).
If you need some code examples i can provide them.
Thanks!
##########################
Okay a quick update to give you more details.
Im writing a Tool for a game (on OS X), its seperated in two parts:
Retrieve the encrypted Data from Game Servers and save them.
Send the saved encrypted Data back to the local Game
Problem: When the saved Data arrives the local Game, the Game just spits out that the Data cant be decrypted => data corruption.
Here's the code for 1. (Its just poc for selftesting purpose):
mode == 0 => no gameDataChunk there
mode == 1 => gameDataChunk there, need to write
mode == 2 => keyFrame there
*/
std::string socketRead(int socket, int mode, std::string num_chunk, std::string num_key, std::string game_id){
std::string response = "";
char buffer[512];
int bytes;
while((bytes = read(socket, buffer, 511)) > 0){
std::string temp(buffer, bytes);
//std::cout << temp;
response = response + temp;
if(!(temp.find("HTTP/1.1 200")<1800000)){
if(temp.find("HTTP/1.1")<1800000){
std::cout << std::endl << std::endl << "GOT NOT A 200 !!!" << std::endl << std::endl;
std::cout << temp << std::endl << std::endl;
return "EXIT";
}
}
if(mode == 1){
if(temp.find("\r\n\r\n")<1800000){
write_gameDataChunk(buffer, 1, temp.find("\r\n\r\n")+4, num_chunk, game_id);
}
else{
write_gameDataChunk(buffer, 0, 0, num_chunk, game_id);
}
}
else if(mode == 2){
if(temp.find("\r\n\r\n")<1800000){
write_keyFrame(buffer, 1, temp.find("\r\n\r\n")+4, num_key, game_id);
}
else{
write_keyFrame(buffer, 0,0, num_key, game_id);
}
}
bzero(buffer, 512);
}
return response;
}
void write_gameDataChunk(char buffer[], int mode, int index, std::string num, std::string game_id){
std::fstream write;
char local_buffer[512];
if(mode == 0){
//complete bin data
write.open("/tmp/" + game_id + "/" + num + "/game_dataChunk.bin", std::ios::out | std::ios::binary | std::ios::app);
write << buffer;
}
else if(mode == 1){
//partial bin data
std::string command = "mkdir /tmp/" + game_id + "/" + num;
std::system(command.c_str());
write.open("/tmp/" + game_id + "/" + num + "/game_dataChunk.bin", std::ios::out | std::ios::binary);
strcpy(local_buffer, buffer+index);
write << local_buffer;
}
write.close();
}
Here is the code for 2.:
std::string get_DataChunk(std::string game_id, int chunkId){
std::ifstream read;
std::string dummy;
std::string command;
std::string result = "";
command = "/tmp/" + game_id + "/" + std::to_string(chunkId) + "/game_dataChunk.bin";
read.open(command.c_str(), std::ios::in | std::ios::binary);
while(getline(read,dummy)){
result = result + dummy;
}
read.close();
return result;
}
This is all testing code for myself and not finished, so its kinda ugly... but i hope you will understand what i mean. I did not often had to deal with binary Data send over Sockets..
std::string is not meant to deal with binary. It is meant to deal with strings.
What you want is a std::vector<char> which doesn't care about what values you are storing.
Any '\0' in your binary data will stop construct the std::string. So the std::string().size() only count to the character before 1st '\0'. Use std::vector<unsigned char> instead.
By the way, what does 1800000 mean? I know that HTTP/1.1 200 is the characters you received at the earliest.(poor english)
I use XOR to encrypt the options I write to a text file, and decrypt them when I read them in.
Below is the code for encrypting the options and writing them to the file:
Settings settings;
const char key = 'x';
std::stringstream ss;
std::string original = "";
std::string encrypted = "";
std::ofstream file("./data/options.txt");
if (file.good()) {
file.clear();
// Build options string
ss << "limitfps=" << (settings.getLimitFramerate() ? "1" : "0") << std::endl;
ss << "fps=" << settings.getFramerateLimit();
// etc...
// Encrypt
original = ss.str();
for (std::size_t temp = 0; temp < original.size(); ++temp) {
encrypted += original[temp] ^ (static_cast<int>(key) + temp) % 255;
}
// Print and write to file
std::cout << "Saving encrypted data to file:\n" << encrypted << std::endl;
file << encrypted;
file.close();
}
Everything works but my PC makes 3 beeps for some reason. How do I make it not make the beeps?
And since it is in the code, another question: I don't need the file.close() at the end, right? I read that close() is automatically called when the end of the scope is reached?
Thanks
encrypted will contain non-printable characters, so when you print them to the console, you'll get some garbage sent to the console.
Some of that garbage is apparently the ASCII code 0x07/^G/BEL, which causes the console to beep.
To fix the problem either don't print encrypted, or print it in such a way that non-printable characters get filtered out or formatted as hexadecimal or something.
I guess your encrypted string, that you pipe to the standard output, contains three times the character 0x07, alias "Bell".