288 lines
7.4 KiB
C++
Executable File
288 lines
7.4 KiB
C++
Executable File
/*
|
|
fix: accept gzip data... see this async wrapper that does it: https://www.codeproject.com/articles/43860/an-asynchronous-http-download-class-for-mfc-atl-an
|
|
fix: HttpOpenRequestA and HttpSendRequestA seem to work passing utf-8 params even though the docs recommend always using the W commands
|
|
|
|
notes
|
|
- cookies currently only set for url without path (domain only)
|
|
- no cookie url encoding
|
|
- session cookies not preserved
|
|
|
|
- in url only query params, not host / path is url encoded
|
|
- due to MS bug timeout is done via worker thread, this means it cant be increased beyond MS default
|
|
also timeouts might cause memory leaks, are all threads, pointers, structs, vars etc deleted/freed?
|
|
according to this SO thread no need to delete or free unless new or malloc called https://stackoverflow.com/questions/5243360/how-to-free-memory-for-structure-variable
|
|
should smart pointers be used in the workers? the docs mainly talk about cases where new is used https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=msvc-160
|
|
|
|
- no auto redirect
|
|
|
|
fixed: post data size limit - fixed by converting postdata string to char array before sending
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
// needed for utf-8 url encoding - see comments at https://alfps.wordpress.com/2011/12/08/unicode-part-2-utf-8-stream-mode/
|
|
// win10 build 17134 (April 2018 Update) and later users can use setlocale(LC_ALL, ".UTF8") see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-160
|
|
#pragma execution_character_set( "utf-8" )
|
|
|
|
#include <string>
|
|
#include <windows.h>
|
|
#include <WinInet.h>
|
|
#include <Winineti.h>
|
|
#include <stdio.h>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <regex>
|
|
#include <initializer_list>
|
|
#include <locale>
|
|
#include <codecvt>
|
|
#include <stdlib.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <urlmon.h>
|
|
#include <mutex>
|
|
#include "Encrypt.h"
|
|
|
|
bool containsWeirdCharacters(const std::string& str) {
|
|
for (char ch : str) {
|
|
if (!std::isprint(static_cast<unsigned char>(ch)) && ch != '\n' && ch != '\t') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
namespace wrap {
|
|
|
|
|
|
|
|
struct Body { //used for postdata
|
|
std::string body;
|
|
};
|
|
|
|
struct File { //used for uploading file multipart post
|
|
std::string file;
|
|
};
|
|
|
|
struct Download {
|
|
std::string dl = "dl"; //default is to download file keeping original filename
|
|
};
|
|
|
|
struct Method {
|
|
std::string method;
|
|
};
|
|
|
|
struct Url {
|
|
std::string adr;
|
|
};
|
|
|
|
struct Header {
|
|
std::map<std::string, std::string> hdr;
|
|
|
|
Header(std::initializer_list<std::pair<const std::string, std::string> > hdr) :
|
|
hdr(hdr) {
|
|
}
|
|
};
|
|
|
|
struct Parameters {
|
|
std::map<std::string, std::string> p;
|
|
|
|
Parameters(std::initializer_list<std::pair<const std::string, std::string> > p) :
|
|
p(p) {
|
|
}
|
|
};
|
|
|
|
struct Payload {
|
|
std::map<std::string, std::string> pd;
|
|
|
|
Payload(std::initializer_list<std::pair<const std::string, std::string> > pd) :
|
|
pd(pd) {
|
|
}
|
|
};
|
|
|
|
struct Multipart {
|
|
std::map<std::string, std::string> mp;
|
|
|
|
Multipart(std::initializer_list<std::pair<const std::string, std::string> > mp) :
|
|
mp(mp) {
|
|
}
|
|
};
|
|
|
|
struct Authentication {
|
|
std::string usr;
|
|
std::string pwd;
|
|
};
|
|
|
|
struct Bearer {
|
|
std::string token;
|
|
};
|
|
|
|
struct Timeout {
|
|
DWORD timeout;
|
|
std::string type = "connection"; // connection or request
|
|
};
|
|
|
|
struct req {
|
|
std::map<std::string, std::string> headers;
|
|
bool set_header(std::string key, std::string value) {
|
|
/* Header names are not case sensitive.
|
|
From RFC 2616 - "Hypertext Transfer Protocol -- HTTP/1.1", Section 4.2, "Message Headers":
|
|
Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.
|
|
The updating RFC 7230 does not list any changes from RFC 2616 at this part.
|
|
so we always use fields / keys with lowercase to avoid duplication */
|
|
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
|
try {
|
|
if (headers.find(key) == headers.end()) { //if entry doesnt exist
|
|
headers.insert(std::pair<std::string, std::string>(key, value)); //add it
|
|
}
|
|
else { //if entry exists, update value
|
|
headers[key] = value;
|
|
}
|
|
return true;
|
|
}
|
|
catch (...) {
|
|
return false;
|
|
}
|
|
};
|
|
bool clear_headers(std::string key = "") {
|
|
if (key == "") { //leave empty to clear all
|
|
try {
|
|
headers.clear();
|
|
return true;
|
|
}
|
|
catch (...) {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
else {
|
|
try {
|
|
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
|
if (headers.find(key) != headers.end()) { // if element exists
|
|
headers.erase(key);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
//std::string postdata;
|
|
std::string ua = EC("Mozilla/5.0 (Windows NT 14_73_31; WOW64)");
|
|
std::string Params;
|
|
std::string PostData; // key is type of data (body,payload or file) and value is the data
|
|
std::string Cookies;
|
|
std::string Dl;
|
|
std::string Proxies;
|
|
std::string Method = "GET";
|
|
std::string Auth;
|
|
std::string Url = "www.example.com";
|
|
Header Header = { {"",""} };
|
|
DWORD TimeoutConnect = 0;
|
|
DWORD TimeoutRequest = 0;
|
|
void reset() {
|
|
ua = EC("Mozilla/5.0 (Windows NT 14_73_31; WOW64)");
|
|
Params = "";
|
|
PostData = "";
|
|
Cookies = "";
|
|
Dl = "";
|
|
Proxies = "";
|
|
Method = "GET";
|
|
Auth = "";
|
|
Url = "www.example.com";
|
|
Header = { {"",""} };
|
|
TimeoutConnect = 0;
|
|
TimeoutRequest = 0;
|
|
};
|
|
};
|
|
|
|
struct Response {
|
|
std::map <std::string, std::string> header;
|
|
std::map <std::string, std::string> sent_headers;
|
|
std::map <std::string, std::string> secinfo;
|
|
std::string url;
|
|
std::string raw;
|
|
std::string text;
|
|
std::string status_code;
|
|
std::string err;
|
|
};
|
|
|
|
|
|
|
|
std::string text_from_html(std::string html);
|
|
|
|
//initial req object that will be used to pass params to cpp source function
|
|
extern req toSource;
|
|
|
|
void Params(wrap::Parameters s);
|
|
|
|
void Params(wrap::Url u);
|
|
|
|
void Params(wrap::Header h);
|
|
|
|
void Params(wrap::Payload pd);
|
|
|
|
void Params(wrap::Multipart mp);
|
|
|
|
void Params(wrap::Authentication auth);
|
|
|
|
void Params(wrap::Bearer token);
|
|
|
|
void Params(wrap::Method m);
|
|
|
|
void Params(wrap::Download dl);
|
|
|
|
void Params(wrap::Body body);
|
|
|
|
void Params(wrap::File file);
|
|
|
|
void Params(wrap::Timeout timeout);
|
|
|
|
// url, headers, params, postdata, cookies, dl flag, proxies, timeout, method, auth
|
|
Response httpsreq(req Request);
|
|
|
|
//this is to achieve the effect of allowing random params of different types
|
|
//https://stackoverflow.com/questions/67089840
|
|
//not even URL is needed - it's set to example.com by default
|
|
|
|
std::mutex Netty;
|
|
|
|
template <typename ...Ts>
|
|
Response HttpsRequest(Ts&& ...args)
|
|
{
|
|
try
|
|
{
|
|
std::lock_guard<std::mutex> Lokkay(Netty); // Lock the mutex
|
|
|
|
std::initializer_list<int> ignore = { (Params(args), 0)... };
|
|
(void)ignore; // Hack that prevents the initializer_list from being optimized and the "ignore variable is unused" warning
|
|
Response x = httpsreq(toSource);
|
|
|
|
if (!containsWeirdCharacters(x.text))
|
|
{
|
|
std::cout << "R:" << std::endl;
|
|
std::cout << x.text << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "EC NP" << std::endl;
|
|
}
|
|
|
|
Sleep(300);
|
|
return x;
|
|
|
|
}
|
|
catch (const std::exception& e) {
|
|
std::cout << "Cg: " << e.what() << std::endl;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
} |