I am using curl to communicate with a server.
When I make a request for data I receive the HTTP headers followed by jpeg data separated by a boundary like so:
I need to parse out
The boundary string
The Content-Length.
I have copied the incoming data to a a char array like so:
static size_t OnReceiveData ( void * pvData, size_t tSize, size_t tCount, void * pvUser )
{
printf("%*.*s", tSize * tCount, tSize * tCount, pvData);
char* _data;
if(pvData != nullptr && 0 != tCount)
{
_data = new char[tCount];
memcpy(_data, pvData, tCount);
}
return ( tCount );
}
How can I best do this in C++?? How do I actually inspect and parse the _data array for the information that I want?? Are the any boost libraries that I can use for example??
You could parse the headers on the fly or put them into a map and post-process later.
Use find, substr methods from the std::string.
Look at Boost String Algorithms Library, it contains lots of algorithms, e.g. trim
e.g. to place headers into the std::map and print them (rough cuts):
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <boost/algorithm/string.hpp>
int main(int argc, char* argv[]) {
const char* s = "HTTP/1.1 200 OK\r\n"
"Content-Type: image/jpeg; charset=utf-8\r\n"
"Content-Length: 19912\r\n\r\n";
std::map<std::string, std::string> m;
std::istringstream resp(s);
std::string header;
std::string::size_type index;
while (std::getline(resp, header) && header != "\r") {
index = header.find(':', 0);
if(index != std::string::npos) {
m.insert(std::make_pair(
boost::algorithm::trim_copy(header.substr(0, index)),
boost::algorithm::trim_copy(header.substr(index + 1))
));
}
}
for(auto& kv: m) {
std::cout << "KEY: `" << kv.first << "`, VALUE: `" << kv.second << '`' << std::endl;
}
return EXIT_SUCCESS;
}
You will get the output:
KEY: `Content-Length`, VALUE: `19912`
KEY: `Content-Type`, VALUE: `image/jpeg; charset=utf-8`
Having the headers, you could extract the required ones for post-processing.
I would put all headers in a map, after which you can easily iterate through it. No boost needed. Here a basic working example with libcurl:
#include <iostream>
#include <string>
#include <map>
#include <curl/curl.h>
static size_t OnReceiveData (void * pData, size_t tSize, size_t tCount, void * pmUser)
{
size_t length = tSize * tCount, index = 0;
while (index < length)
{
unsigned char *temp = (unsigned char *)pData + index;
if ((temp[0] == '\r') || (temp[0] == '\n'))
break;
index++;
}
std::string str((unsigned char*)pData, (unsigned char*)pData + index);
std::map<std::string, std::string>* pmHeader = (std::map<std::string, std::string>*)pmUser;
size_t pos = str.find(": ");
if (pos != std::string::npos)
pmHeader->insert(std::pair<std::string, std::string> (str.substr(0, pos), str.substr(pos + 2)));
return (tCount);
}
int main(int argc, char* argv[])
{
CURL *curl = curl_easy_init();
if (!curl)
return 1;
std::map<std::string, std::string> mHeader;
curl_easy_setopt(curl, CURLOPT_URL, "http://www.example.com");
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, OnReceiveData);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &mHeader);
curl_easy_setopt(curl, CURLOPT_NOBODY, true);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
std::map<std::string, std::string>::const_iterator itt;
for (itt = mHeader.begin(); itt != mHeader.end(); itt++)
{
if (itt->first == "Content-Type" || itt->first == "Content-Length")
std::cout << itt->first << ": " << itt->second << std::endl;
}
}
The cpp-netlib project (based on boost) contains a full MIME parser (written with boost.spirit).
I'm not really that happy with the interface of the parser, but it works well.
Related
I made an exe binder (which can bind multiple exes without any error checking, anyway), it works as expected, only that the antivirus screams instantly :(
Here is the source code:
#undef UNICODE
#include <Windows.h>
#include <fstream>
#include <vector>
#include <string>
#define SEPARATOR "*****"
#define SEPARATOR_SIZE strlen(SEPARATOR)
void FindAllOccurrences(const std::string& data, const std::string& query, std::vector<size_t>& occurancesPoss) {
size_t pos = data.find(query);
while(pos != std::string::npos) {
occurancesPoss.push_back(pos);
pos = data.find(query, pos + query.size());
}
}
inline void FileAsString(const std::string& file, std::string& str, const std::ios_base::openmode iosOM = std::ios::binary) {
std::ifstream ifs(file, iosOM);
str.assign((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
}
void Bind(const std::vector<std::string>& files, const std::string& fileBinded, const std::string& fileOpener) {
std::ofstream ofs(fileBinded, std::ios::binary);
ofs << std::ifstream(fileOpener, std::ios::binary).rdbuf() << SEPARATOR;
size_t index = files.size();
for(auto& file : files) {
ofs << std::ifstream(file, std::ios::binary).rdbuf();
if(--index) {
ofs << SEPARATOR;
}
}
}
void Open(const std::string& file) {
std::string data;
FileAsString(file, data);
std::vector<size_t> occurancesPoss;
FindAllOccurrences(data, SEPARATOR, occurancesPoss);
std::vector<std::string> exes;
for(size_t i = 1; i < occurancesPoss.size() - 1; i++) {
std::string exeName(std::to_string(i) + ".exe");
std::ofstream ofs(exeName, std::ios::binary);
size_t exeStart = occurancesPoss[i] + SEPARATOR_SIZE;
ofs << data.substr(exeStart, occurancesPoss[i + 1] - exeStart);
exes.push_back(exeName);
}
{
std::string exeName(std::to_string(occurancesPoss.size() - 1) + ".exe");
std::ofstream ofs(exeName, std::ios::binary);
ofs << data.substr(occurancesPoss.back() + SEPARATOR_SIZE);
exes.push_back(exeName);
}
for(auto& exe : exes) {
SetFileAttributes(exe.c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
ShellExecute(nullptr, "open", exe.c_str(), nullptr, nullptr, SW_NORMAL);
}
}
int main(int argc, char** argv) {
if(argc > 1) {
Bind(std::vector<std::string>(&argv[1], argv + argc - 1), argv[argc - 1], argv[0]);
} else {
Open(argv[0]);
}
return 0;
}
My question is, what makes an exe binder undetectable and how to make it.
I think that the opener code should be the one which needs to be changed. Correct me if I am wrong.
If you got any feedback for the code hit me up. (about the error checking.. I didn't add it for the sake of simplicity).
Thank you in advance!
What is the purpose of this application? You're using Windows API functions that are mosst commonly used in bad software. That's why the Anti Virus scanners are reacting.
Similarly to how we construct bsoncxx::document::view objects from a buffer with a single binary document, is there a way to extract single documents from a collection in a .bson dump in this framework without having to load them into a DB?
i.e. what works for single document objects
uint8 *buffer; // single bson document
size_t length; // size of buffer
bsoncxx::document::view view(buffer, length);
for (auto elem : view) {
doSomethingWithElem()
}
I'd like to be able to construct a cursor for the whole dump, but without loading it into a collection. Is something like this possible?
Found the solution and it was pretty simple in the end - I utilized the libbson library.
An example of what I used below:
#include <bson.h>
// and other includes
void read_bson() {
bson_reader_t *reader;
const bson_t *doc;
bson_error_t error;
bool eof;
char *path;
reader = bson_reader_new_from_file(path, &error);
if (reader)
{
while ((doc = bson_reader_read(reader, &eof)))
{
const uint8_t *buffer = bson_get_data(doc);
auto view = bsoncxx::document::view(buffer, doc->len);
}
}
}
If you are using mongo-cxx-driver, here is a example for reading bson file dumped by mongodump tool.
#include <iostream>
#include <vector>
#include <fstream>
#include <mongocxx/client.hpp>
#include <mongocxx/uri.hpp>
#include <bsoncxx/json.hpp>
#include <bsoncxx/builder/stream/document.hpp>
#include <sstream>
void parse_one_doc(const char *data, const size_t &length) {
// TODO fill your code
bsoncxx::document::view view((std::uint8_t *) data, length);
std::cout << bsoncxx::to_json(view) << std::endl;
}
int unpack_size(const char *data, size_t position) {
return *(int *) (data + position);
}
bool parse_mongo_dumper(const std::string &data) {
size_t position = 0u, end = data.length() - 1u, data_len = data.size();
size_t obj_size, obj_end;
const char *dc = data.c_str();
while (position < end) {
obj_size = unpack_size(dc, position);
if (position + obj_size > data_len) {
return false;
}
obj_end = position + obj_size;
if (*(dc + obj_end - 1) != '\0') {
return false;
}
parse_one_doc(dc + position, obj_size);
position = obj_end;
}
return true;
}
int main() {
std::string f = "/path/to/data.bson";
// read all data into string
std::ifstream t(f);
std::stringstream buffer;
buffer << t.rdbuf();
std::string s = buffer.str();
// parse bson
parse_mongo_dumper(s);
return 0;
}
update: this question cannot fix my need, addr2line source is not only 400 lines source, it's relative with other binutils source, I wanna a light solution to do the "get backtrace line number"
I use following that can get backtrace line number:
addr2line -e /home/roroco/Dropbox/c/ro-c/cmake-build-debug/ex/test_backtrace_with_line_number 0x400d0b
but if I wanna get backtrace all line numbers, I must invoke addr2line cli line by line, it's slow, is there way to get backtrace line number without cli but use pure c++? or other alternative lib can get line number
I know if I see addr2line, I can do this, if has more convenient c++ lib, please tell me
here is my code to get line number with addr2line, I hope pure c++ solution to instead addr2line cli
#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <zconf.h>
#include "regex"
std::string getexepath() {
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
}
std::string sh(std::string cmd) {
std::array<char, 128> buffer;
std::string result;
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe.get())) {
if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
result += buffer.data();
}
}
return result;
}
void print_backtrace(void) {
void *bt[1024];
int bt_size;
char **bt_syms;
int i;
bt_size = backtrace(bt, 1024);
bt_syms = backtrace_symbols(bt, bt_size);
std::regex re("\\[(.+)\\]");
auto exec_path = getexepath();
for (i = 1; i < bt_size; i++) {
std::string sym = bt_syms[i];
std::smatch ms;
if (std::regex_search(sym, ms, re)) {
std::string addr = ms[1];
std::string cmd = "addr2line -e " + exec_path + " -f -C " + addr;
auto r = sh(cmd);
std::regex re2("\\n$");
auto r2 = std::regex_replace(r, re2, "");
std::cout << r2 << std::endl;
}
}
free(bt_syms);
}
void test_m() {
print_backtrace();
}
int main() {
test_m();
return 0;
}
change addr2line source is so hard and I give up this way,
I receive #500 - Internal Server Error suggestion, addr2line cli can receive multi addrs, so I change my code like following, only run addr2line once
#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <zconf.h>
#include "regex"
#include "vector"
std::string getexepath() {
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
}
std::string sh(std::string cmd) {
std::array<char, 128> buffer;
std::string result;
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe.get())) {
if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
result += buffer.data();
}
}
return result;
}
void print_backtrace(void) {
void *bt[1024];
int bt_size;
char **bt_syms;
int i;
bt_size = backtrace(bt, 1024);
bt_syms = backtrace_symbols(bt, bt_size);
std::regex re("\\[(.+)\\]");
auto exec_path = getexepath();
std::string addrs = "";
for (i = 1; i < bt_size; i++) {
std::string sym = bt_syms[i];
std::smatch ms;
if (std::regex_search(sym, ms, re)) {
std::string m = ms[1];
addrs += " " + m;
}
}
auto r = sh("addr2line -e " + exec_path + " -f -C " + addrs);
std::cout << r << std::endl;
free(bt_syms);
}
void test_m() {
print_backtrace();
}
int main() {
test_m();
return 0;
}
I need to get an access token (for a service account) for the google's OAuth authentication service. I tried several things an studied a lot of on the web but don't succeed.
Basically i followed https://developers.google.com/accounts/docs/OAuth2ServiceAccount
What i have done (VS2013):
int _tmain(int argc, _TCHAR* argv[])
{
Json::Value jwt_header;
Json::Value jwt_claim_set;
std::string jwt_b64;
std::time_t t = std::time(NULL);
Json::FastWriter jfw;
Json::StyledWriter jsw;
/* Create jwt header */
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
std::cout << jsw.write(jwt_header);
/* Create jwt claim set */
jwt_claim_set["iss"] = "myid#developer.gserviceaccount.com"; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/plus.me" /* scope of requested access token */;
jwt_claim_set["aud"] = "https://accounts.google.com/o/oauth2/token"; /* intended target of the assertion for an access token */
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
jwt_claim_set["exp"] = std::to_string(t+3600); /* expire time*/
std::cout << jsw.write(jwt_claim_set);
/* create http POST request body */
/* for header */
std::string json_buffer;
std::string json_buffer1;
json_buffer = jfw.write(jwt_header);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 = json_buffer1;
jwt_b64 += ".";
/* for claim set */
json_buffer = jfw.write(jwt_claim_set);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
/* for signature */
std::string jwt_signature = jws_sign(jwt_b64, "key.p12");
if (!jwt_signature.empty())
{
jwt_b64 += ".";
json_buffer1.clear();
std::remove_copy(jwt_signature.begin(), jwt_signature.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
write2file("jwt.bat", jwt_b64); /* for test purpose calling with curl */
}
else
std::cout << "Error creating signature";
return 0;
}
int write2file(std::string filename, std::string data)
{
std::ofstream f(filename);
f << "%curl% -d \"grant_type=urn%%3Aietf%%3Aparams%%3Aoauth%%3Agrant-type%%3Ajwt-bearer&assertion=";
f << data;
f << "\" https://accounts.google.com/o/oauth2/token";
f.close();
return 0;
}
std::string jws_sign(std::string data, std::string pkcs12_path) {
SHA256_CTX mctx;
unsigned char hash[SHA256_DIGEST_LENGTH];
size_t hlen = SHA256_DIGEST_LENGTH;
const char *buf = data.c_str();
int n = strlen((const char*) buf);
SHA256_Init(&mctx);
SHA256_Update(&mctx, buf, n);
SHA256_Final(hash, &mctx);
std::string signature_b64;
unsigned char *sig = NULL;
size_t slen = 0;
EVP_PKEY_CTX *kctx;
EVP_PKEY *key = getPkey(pkcs12_path);
kctx = EVP_PKEY_CTX_new(key, NULL);
if (!kctx) goto err;
if (EVP_PKEY_sign_init(kctx) <= 0) goto err;
if (EVP_PKEY_CTX_set_rsa_padding(kctx, RSA_PKCS1_PADDING) <= 0) goto err;
if (EVP_PKEY_CTX_set_signature_md(kctx, EVP_sha256()) <= 0) goto err;
/* Determine buffer length */
if (EVP_PKEY_sign(kctx, NULL, &slen, hash, hlen) <= 0) goto err;
sig = (unsigned char *) OPENSSL_malloc(slen);
if (!sig) goto err;
if (EVP_PKEY_sign(kctx, sig, &slen, hash, hlen) <= 0) goto err;
signature_b64 = base64_encode(sig, (unsigned int)slen, true);
return signature_b64;
err:
/* Clean up */
EVP_cleanup();
signature_b64.clear();
return signature_b64;
}
All i receive back is
{
"error" : "invalid_grant"
}
So if someone can point me into the right direction would be great.
It would also help, if someone can point me to get the thing working by manually generating the jwt request out of openssl commands.
I'm working with VS2013
I found my mistake - was simply a typo :(
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
needs to be
jwt_claim_set["iat"] = std::to_string(t); /* issued time */
The code works and generate valid token requests.
I've made a class for authentication on C++, will leave it here, may be someone may need it.
// YOU SHOULD GO TO Credentials SECTION FOR YOUR PROJECT AT https://console.developers.google.com/
// MAKE Service Account AND GET AUTHENTICATION JSON FROM IT,
// PLACE IT TO BUILD FOLDER AND CALL IT google_service_account.json
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
// SSL INCLUDES
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
// https://github.com/nlohmann/json
#include <nlohmann/json.hpp>
using json = nlohmann::json;
class TGoogleAuthCpp {
json serviceAccountJSON;
bool serviceAccountExists;
void readServiceAccountJson();
RSA* createPrivateRSA(std::string key);
bool RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc);
std::string signMessage(std::string privateKey, std::string plainText);
std::string url_encode(const std::string &value);
std::string base64_encode(const std::string &in);
public:
TGoogleAuthCpp();
int createRequest();
};
TGoogleAuthCpp::TGoogleAuthCpp() {
serviceAccountExists = false;
readServiceAccountJson();
}
RSA* TGoogleAuthCpp::createPrivateRSA(std::string key) {
RSA *rsa = NULL;
const char* c_string = key.c_str();
BIO * keybio = BIO_new_mem_buf((void*)c_string, -1);
if (keybio==NULL) {
return 0;
}
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
return rsa;
}
bool TGoogleAuthCpp::RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc) {
EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
EVP_PKEY* priKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(priKey, rsa);
if (EVP_DigestSignInit(m_RSASignCtx,NULL, EVP_sha256(), NULL,priKey)<=0) {
return false;
}
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
return false;
}
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) {
return false;
}
*EncMsg = (unsigned char*)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
return false;
}
EVP_MD_CTX_cleanup(m_RSASignCtx);
return true;
}
std::string TGoogleAuthCpp::signMessage(std::string privateKey, std::string plainText) {
RSA* privateRSA = createPrivateRSA(privateKey);
unsigned char* encMessage;
size_t encMessageLength;
RSASign(privateRSA, (unsigned char*) plainText.c_str(), plainText.length(), &encMessage, &encMessageLength);
std::string str1((char *)(encMessage), encMessageLength);
free(encMessage);
return base64_encode(str1);
}
void TGoogleAuthCpp::readServiceAccountJson() {
std::string fname = "google_service_account.json";
std::string line;
std::ifstream myfile (fname);
if (myfile.good()) {
std::stringstream ss;
if (myfile.is_open()) {
while (getline(myfile, line)) {
ss << line << '\n';
}
myfile.close();
serviceAccountJSON = json::parse(ss.str());
serviceAccountExists = true;
}
}
}
std::string TGoogleAuthCpp::base64_encode(const std::string &in) {
std::string out;
std::string base64_encode_b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//=
int val=0, valb=-6;
for (unsigned char c : in) {
val = (val<<8) + c;
valb += 8;
while (valb>=0) {
out.push_back(base64_encode_b[(val>>valb)&0x3F]);
valb-=6;
}
}
if (valb>-6) out.push_back(base64_encode_b[((val<<8)>>(valb+8))&0x3F]);
while (out.size()%4) out.push_back('=');
return out;
}
std::string TGoogleAuthCpp::url_encode(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << std::uppercase;
escaped << '%' << std::setw(2) << int((unsigned char) c);
escaped << std::nouppercase;
}
return escaped.str();
}
int TGoogleAuthCpp::createRequest() {
if (!serviceAccountExists) return 0;
json jwt_header;
json jwt_claim_set;
std::time_t t = std::time(NULL);
// Create jwt header
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
// Create jwt claim set
jwt_claim_set["iss"] = serviceAccountJSON["client_email"]; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/androidpublisher" /* scope of requested access token */;
jwt_claim_set["aud"] = serviceAccountJSON["token_uri"]; /* intended target of the assertion for an access token */
jwt_claim_set["iat"] = t; /* issued time */
jwt_claim_set["exp"] = t+3600; /* expire time*/
// web token
std::stringstream jwt_ss;
// header
jwt_ss << base64_encode(jwt_header.dump());
jwt_ss << ".";
// claim set
jwt_ss << base64_encode(jwt_claim_set.dump());
// signature
std::string signed_msg = signMessage(serviceAccountJSON["private_key"], jwt_ss.str());
jwt_ss << "." << signed_msg;
std::stringstream post_body_ss;
post_body_ss << "curl -d '";
post_body_ss << "grant_type=" << url_encode("urn:ietf:params:oauth:grant-type:jwt-bearer");
post_body_ss << "&assertion=" << url_encode(jwt_ss.str());
post_body_ss << "' https://oauth2.googleapis.com/token";
std::string post_body = post_body_ss.str();
std::cout << post_body << std::endl;
return 1;
}
int main() {
TGoogleAuthCpp auth;
int res = auth.createRequest();
}
what is the best way of searching files on windows in c++. Should I use boost or there is a better way . I'm encountering some problems with building filesystem library. I found this:
#include <stdio.h>
#include <dir.h>
#include <string.h>
#define ALL_ATTS (FA_DIREC | FA_ARCH)
void walker(const char *, const char *);
void walker(const char *path, const char *findme)
{
struct ffblk finder;
unsigned int res;
chdir(path);
for (res = findfirst("*.*", &finder, ALL_ATTS); res == 0; res = findnext(&finder))
{
if (strcmp(finder.ff_name, ".") == 0) continue; /* current dir */
if (strcmp(finder.ff_name, "..") == 0) continue; /* parent dir */
/*
* If its a directory, examine it
* else compare the filename with the one we're looking for
*/
if (finder.ff_attrib & FA_DIREC)
{
char newpath[MAXPATH];
strcpy(newpath, path);
strcat(newpath, "\\");
strcat(newpath, finder.ff_name);
chdir(finder.ff_name);
walker(newpath, findme);
chdir("..");
}
else
{
if (strcmp(finder.ff_name, findme) == 0)
{
printf("Found in: %s\n", path);
}
}
}
}
int main(void)
{
const char *root = "\\";
char buf[BUFSIZ];
printf ("This program will find a file on the current drive.\n"
"Enter the name of the file to look for: ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin))
{
strtok(buf, "\n"); /* Remove the newline character */
walker(root, buf);
}
return(0);
}
But none of the versions of dir headers works ...
I've found boost::filesystem to work quite well, as long as you know what you are doing. The following works for me:
#include <iostream>
#include <string>
#include "boost/filesystem.hpp"
namespace fs = boost::filesystem;
int _tmain(int argc, _TCHAR* argv[])
{
const std::string start = "C:\\";
const std::string findme = "winsock.dll";
fs::recursive_directory_iterator end;
fs::recursive_directory_iterator rdi(start);
while(rdi != end)
{
const std::string path = rdi->path().string();
if(path.find(findme) != std::string::npos)
{
std::cout << path << std::endl;
}
try
{
++rdi; // various system directories can bork this
}
catch(fs::filesystem_error e)
{
rdi.no_push(); // don't try to recurse into it
++rdi;
}
}
return 0;
}
You may want to consider Win32 APIs like FindFirstFile,FindNextFile, etc.
There are some sample codes on MSDN, like this.
You may want to have a look at the recls library made by Matthew Wilson.
The code you've found looks like it uses functions specific to some particular "standard" library (and may even have been written for MS-DOS). Secondarily, it uses a depth-first search; for directory searching, I usually prefer a breadth-first search.
I'd try to use Windows desktop search and/or Windows Search as the first choices. These will use pre-built indexes for really fast results if the user has that enabled.
If that's not available, I'd use code for a breadth-first search, which looks roughly like this:
#include <windows.h>
#include <queue>
#include <sstream>
#include <iostream>
#include <algorithm>
// I think MS's names for some things are obnoxious.
const HANDLE HNULL = INVALID_HANDLE_VALUE;
const int A_DIR = FILE_ATTRIBUTE_DIRECTORY;
std::ostream &operator<<(std::ostream &os, FILETIME const &ft) {
SYSTEMTIME utc, lt;
FileTimeToSystemTime(&ft, &utc);
SystemTimeToTzSpecificLocalTime(NULL, &utc, <);
return os << lt.wHour << ":" << lt.wMinute << ":" << lt.wSecond << "." << lt.wMilliseconds;
}
void process(std::string const &path, WIN32_FIND_DATA const &file) {
std::cout << file.ftCreationTime << "\t" << path << file.cFileName << "\n";
}
void find_file(std::string const &folder_name, std::string const &fmask) {
HANDLE finder; // for FindFirstFile
WIN32_FIND_DATA file; // data about current file.
std::priority_queue<std::string, std::vector<std::string>, std::greater<std::string> > dirs;
dirs.push(folder_name); // start with passed directory
do {
std::string path = dirs.top();// retrieve directory to search
dirs.pop();
if (path[path.size()-1] != '\\') // normalize the name.
path += "\\";
std::string mask = path + fmask; // create mask for searching
// traverse a directory.
if (HNULL==(finder=FindFirstFile(mask.c_str(), &file))) {
continue;
}
do {
if (!(file.dwFileAttributes & A_DIR)) // print file names
process(path, file);
} while (FindNextFile(finder, &file));
FindClose(finder);
if (HNULL==(finder=FindFirstFile((path + "*").c_str(), &file)))
continue;
do {
if ((file.dwFileAttributes & A_DIR) && (file.cFileName[0] != '.'))
dirs.push(path + file.cFileName);
} while (FindNextFile(finder, &file));
FindClose(finder);
} while (!dirs.empty());
}
int main(int argc, char **argv) {
if (argc > 2)
find_file(argv[1], argv[2]);
else
find_file("C:\\", "*");
return 0;
}