DataHelper.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #ifndef DATAHELPER_H
  2. #define DATAHELPER_H
  3. #include "QAuthenticator"
  4. #include "QWebEngineView"
  5. #include "QWebEngineProfile"
  6. #include "QWebEngineSettings"
  7. #include "QWebEngineCookieStore"
  8. #include "QNetworkProxy"
  9. #include "QEventLoop"
  10. #include "QDir"
  11. #include "QByteArray"
  12. #include "QMessageBox"
  13. static QDir cacheDir ("cache");
  14. static QDir cookieDir("cookie");
  15. const static QString hCAPTCHA_source = "<form action='' method='POST'>\
  16. <script src='https://hcaptcha.com/1/api.js'></script>\
  17. <div class='h-captcha' data-sitekey='%1'></div>\
  18. <input type='submit' value='Submit' /></form>";
  19. const static QString reCAPTCHA2_source = "<form action='https://google.com' method='GET'>\
  20. <script src='https://www.google.com/recaptcha/api.js'></script>\
  21. <div class='g-recaptcha' data-sitekey='%1'></div>\
  22. <input id='1' type='submit' name='button' value='Enter' ></form>";
  23. const static QString reCAPTCHA3_source1 = "var script=document.createElement('script');script.type='text/javascript';script.src='https://www.google.com/recaptcha/api.js?render=%1';document.head.appendChild(script);";
  24. const static QString reCAPTCHA3_source2 = "var ResponseV3=0;grecaptcha.ready(function(){grecaptcha.execute('%1', {action: '%2'}).then(function(token){ResponseV3=token;});});";
  25. static int newTabId = 0;
  26. enum CAPTCHA{ none, reCap2, reCap3, hCap };
  27. enum PROXYSTATE{ NOT_SET, ON, OFF };
  28. class JS_Capture : public QWebEnginePage{
  29. protected:
  30. virtual void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) override {
  31. if(onJS_error_alert && level == ErrorMessageLevel){
  32. QMessageBox::information(nullptr, "Ошибка в JS", message + " : line " + QString::number(lineNumber));
  33. }
  34. if(captureJS_errors){
  35. if(logAll || level == logLevel){
  36. JS_errors += message + " : line " + QString::number(lineNumber);
  37. }
  38. }
  39. }
  40. public:
  41. bool captureJS_errors = true;
  42. bool logAll = false;
  43. bool onJS_error_alert = false;
  44. QWebEnginePage::JavaScriptConsoleMessageLevel logLevel = ErrorMessageLevel;
  45. QString JS_errors = "";
  46. };
  47. class Proxy{
  48. public:
  49. inline static QNetworkProxy::ProxyType Type = QNetworkProxy::DefaultProxy;
  50. inline static QString Adress;
  51. inline static uint16_t Port;
  52. inline static QString User;
  53. inline static QString Pass;
  54. inline static bool isProxyAuth = false;
  55. static bool set(QStringList *TaskList){
  56. if(TaskList->at(1) == "none"){//убрать прокси
  57. Adress = "";
  58. QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
  59. }
  60. else{
  61. QList<QString> listData = TaskList->at(1).split(":");
  62. if(listData.size() < 3) return false;
  63. if(listData.at(0) == "http" || listData.at(0) == "https")
  64. Type = QNetworkProxy::HttpProxy;
  65. else if(listData.at(0) == "socks5")
  66. Type = QNetworkProxy::Socks5Proxy;
  67. else return false;
  68. Adress = listData.at(1);
  69. Port = listData.at(2).toUInt();
  70. if(listData.size() == 3){//http:1.2.3.4:80
  71. isProxyAuth = false;
  72. QNetworkProxy::setApplicationProxy(QNetworkProxy(Type, Adress, Port));
  73. return true;
  74. }
  75. else if(listData.size() == 5){//http:1.2.3.4:80:user:pass
  76. User = listData.at(3);
  77. Pass = listData.at(4);
  78. isProxyAuth = true;
  79. QNetworkProxy::setApplicationProxy(QNetworkProxy(Type, Adress, Port, User, Pass));
  80. return true;
  81. }
  82. return false;
  83. }
  84. return true;
  85. }
  86. static bool on_off(PROXYSTATE state){
  87. if(state == PROXYSTATE::ON){
  88. if(Adress.length() > 0){
  89. if(isProxyAuth)
  90. QNetworkProxy::setApplicationProxy(QNetworkProxy(Type, Adress, Port, User, Pass));
  91. else
  92. QNetworkProxy::setApplicationProxy(QNetworkProxy(Type, Adress, Port));
  93. }
  94. else return false;
  95. }
  96. else QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
  97. return true;
  98. }
  99. };
  100. class Tab : public QWebEngineView{
  101. #define profile page()->profile()
  102. #define store profile->cookieStore()
  103. public:
  104. Tab(){
  105. this->id = newTabId++;
  106. setPage(new JS_Capture);
  107. settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
  108. settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true);
  109. settings()->setUnknownUrlSchemePolicy(QWebEngineSettings::AllowAllUnknownUrlSchemes);
  110. settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
  111. profile->setHttpUserAgent( "Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0");
  112. profile->setHttpAcceptLanguage("ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3");
  113. profile->setCachePath(cacheDir .path());
  114. //вызывает постоянную нагрузку
  115. //profile->setPersistentStoragePath(cookieDir.path());
  116. connect(this, &QWebEngineView::loadStarted, this, [&](){
  117. isLoaded = false;
  118. });
  119. connect(this, &QWebEngineView::loadFinished, this, [&](){
  120. isLoaded = true;
  121. });
  122. cookiesV = new QVector<QNetworkCookie>;
  123. connect(profile->cookieStore(), &QWebEngineCookieStore::cookieAdded, this, [&](const QNetworkCookie &cookie){
  124. addCookie(cookie);
  125. });
  126. connect(page(), &QWebEnginePage::proxyAuthenticationRequired, this, &Tab::proxyAuthenticationRequired);
  127. }
  128. int id;
  129. bool isLoaded;
  130. QVector<QNetworkCookie>* cookies(){
  131. return cookiesV;
  132. }
  133. void deleteCookies(){
  134. store->deleteAllCookies();
  135. cookiesV->clear();
  136. }
  137. QByteArray runJavaScript(QString script, bool getResponse = false){
  138. QByteArray JSresult;
  139. QEventLoop loop;
  140. page()->runJavaScript(script, [&JSresult, &loop] (const QVariant &result){
  141. JSresult = result.toByteArray();
  142. loop.exit(0);
  143. });
  144. loop.exec();
  145. if(getResponse) return JSresult;
  146. return JSresult.isEmpty() ? "empty result" : JSresult;
  147. }
  148. QString JS_Errors(){
  149. return static_cast<JS_Capture*>(page())->JS_errors;
  150. }
  151. void set_JS_capture(bool state){
  152. static_cast<JS_Capture*>(page())->captureJS_errors = state;
  153. }
  154. void set_JS_capture_level(int lvl){
  155. static_cast<JS_Capture*>(page())->logAll = false;
  156. switch (lvl){
  157. case 0: static_cast<JS_Capture*>(page())->logLevel = QWebEnginePage::InfoMessageLevel; break;
  158. case 1: static_cast<JS_Capture*>(page())->logLevel = QWebEnginePage::WarningMessageLevel; break;
  159. case 2: static_cast<JS_Capture*>(page())->logLevel = QWebEnginePage::ErrorMessageLevel; break;
  160. case 3: static_cast<JS_Capture*>(page())->logAll = true; break;
  161. }
  162. }
  163. void set_JS_on_error_alert(bool state){
  164. static_cast<JS_Capture*>(page())->onJS_error_alert = state;
  165. }
  166. void clearJS_errors(){
  167. static_cast<JS_Capture*>(page())->JS_errors.clear();
  168. }
  169. void saveCookies(QString domain, QString fileName, bool isAppend, bool isDefaultPath){
  170. QByteArray data = "";//Куки в формате miniPoster'а
  171. auto addCookie = [&data](const QNetworkCookie &cook){
  172. data += (cook.domain().at(0) == '.'
  173. ? cook.domain().remove(0, 1).toUtf8() : cook.domain().toUtf8())
  174. + "|" + cook.name() + "|" + cook.value() + "\n";
  175. };
  176. if(domain.length() == 0)//Домен не указан => выгружаем все куки
  177. for(auto c : *cookies()) addCookie(c);
  178. else for(auto c : *cookies())
  179. if(c.domain().contains(domain))//Сохраняем куки только для указанного домена
  180. addCookie(c);
  181. if(data.length() > 0) data.remove(data.length() - 1, 1);//удаляем '\n' в конце
  182. QFile qf((isDefaultPath ? ("../save/cookies/" + fileName) : fileName) + ".cook");
  183. qf.open(isAppend ? QIODevice::Append : QIODevice::WriteOnly);
  184. qf.write(data); qf.close();
  185. }
  186. int set_cookies(QStringList &TaskList, bool fromFile = true){
  187. int added = 0;
  188. auto addCookie = [&](const QByteArray *host, const QByteArray *key, QByteArray value = ""){
  189. if(host->length() == 0 || key->length() == 0)
  190. return true;
  191. QNetworkCookie cookie(*key, value);
  192. ++added;
  193. store->setCookie(cookie, QUrl("https://" + *host));
  194. return false;
  195. };
  196. if(fromFile){
  197. QFile qf(TaskList.at(1));
  198. if(!qf.exists()) return -1;
  199. QByteArrayList split = qf.readAll().split('\n');//Куки из файла
  200. if(split.count() == 0) return -1;
  201. for(auto str : split){
  202. QByteArrayList c = str.split('|');
  203. bool bad = true;
  204. if(c.count() == 2){//Добавляем пустое значение
  205. bad = addCookie(&c.at(0), &c.at(1));
  206. }
  207. else if(c.count() == 3){//Нормальная строка
  208. bad = addCookie(&c.at(0), &c.at(1), c.at(2));
  209. }
  210. if(bad) return -1;//Странная строка
  211. }
  212. }
  213. else{
  214. QByteArrayList split = TaskList.at(1).toLocal8Bit().split('|');//Куки из строки
  215. if(split.count() == 0) return -1;
  216. for(int i = 0; i < split.size(); i += 3){
  217. bool bad = true;
  218. if(i + 2 < split.size()){//Нормальная строка
  219. bad = addCookie(&split.at(i), &split.at(i + 1), split.at(i + 2));
  220. }
  221. else if(i + 1 < split.size()){//Добавляем пустое значение
  222. bad = addCookie(&split.at(i), &split.at(i + 1));
  223. }
  224. if(bad) return -1;//Странная строка
  225. }
  226. }
  227. return added;
  228. }
  229. void get_html(QByteArray &data_to_server){
  230. QString res = "";
  231. QEventLoop loop;
  232. page()->toHtml([&res,&loop](const QString &result){
  233. res = result;
  234. loop.exit(0);
  235. });
  236. loop.exec();
  237. data_to_server = res.toLocal8Bit();
  238. }
  239. void get_cookies(QByteArray &data_to_server){
  240. data_to_server = "";
  241. for(auto c : *cookies())//Преобразуем куки в формат miniPoster'а
  242. data_to_server += (c.domain() + "|" + c.name() + "|" + c.value() + "|").toLocal8Bit();
  243. if(data_to_server.length() > 0) data_to_server.remove(data_to_server.length() - 1, 1);//удаляем '|' в конце
  244. }
  245. void get_cookie_value_by_name(QByteArray &data_to_server, QStringList &TaskList){
  246. data_to_server = "";
  247. for(auto c : *cookies())
  248. if(c.name() == TaskList.at(1)){
  249. data_to_server = c.value();
  250. break;
  251. }
  252. }
  253. void get_cookie_by_domain(QByteArray &data_to_server, QStringList &TaskList){
  254. data_to_server = "";
  255. for(auto c : *cookies())
  256. if(c.domain().contains(TaskList[1]))
  257. data_to_server += c.name() + "|" + c.value() + "|";
  258. if(data_to_server.length() > 0) data_to_server.remove(data_to_server.length() - 1, 1);//удаляем '|' в конце
  259. }
  260. void get_domain_cookie_value_by_name(QByteArray &data_to_server, QStringList &TaskList){
  261. data_to_server = "";
  262. for(auto c : *cookies())
  263. if(c.domain() == TaskList.at(1) && c.name() == TaskList.at(2)){
  264. data_to_server = c.value();
  265. break;
  266. }
  267. }
  268. void screen_shot(QByteArray &data_to_server, QStringList &TaskList){
  269. QList<QString> listData = TaskList.at(2).split(":");
  270. listData.append(TaskList.at(3).split(":"));
  271. if(listData.size() < 4) return;
  272. QDir *dir = new QDir();
  273. static const QString path = "../captcha/";
  274. if(!dir->exists(path)) dir->mkdir(path);
  275. delete dir;
  276. int x = listData.at(0).toInt(), y = listData.at(1).toInt();
  277. QPixmap img = grab(QRect(QPoint(x,y), QSize(listData.at(2).toInt() - x, listData.at(3).toInt() - y)));
  278. img.save(path + TaskList.at(1) + ".png");
  279. data_to_server = "ok";
  280. }
  281. void pixel_exists(QByteArray &data_to_server, QStringList &TaskList){
  282. QList<QString> listData = TaskList.at(1).split(":");
  283. listData.append(TaskList.at(2).split(":"));
  284. data_to_server = "no";
  285. if(listData.size() < 4) return;
  286. int x = listData.at(0).toInt(), y = listData.at(1).toInt();
  287. int x1 = listData.at(2).toInt(), y1 = listData.at(3).toInt();
  288. QImage img = grab(QRect(QPoint(x,y), QSize(x1 - x, y1 - y))).toImage();
  289. QColor pixel(TaskList.at(3));
  290. bool find = false;
  291. for(int y = 0; y < img.height() && !find; ++y)
  292. for(int x = 0; x < img.width() && !find; ++x)
  293. if(img.pixel(x, y) == pixel.rgb())
  294. { data_to_server = "yes"; find = true; }
  295. if(!find)
  296. data_to_server = "no";
  297. }
  298. private:
  299. QVector<QNetworkCookie> *cookiesV;
  300. void addCookie(const QNetworkCookie cookie){
  301. for(auto item : *cookiesV)
  302. if(item.hasSameIdentifier(cookie))
  303. return;//добавляем только новые куки
  304. cookiesV->append(cookie);
  305. }
  306. void proxyAuthenticationRequired(const QUrl &, QAuthenticator *auth, const QString &){
  307. auth->setUser (Proxy::User);
  308. auth->setPassword(Proxy::Pass);
  309. }
  310. };
  311. class Captcha{
  312. public:
  313. static bool getResponse(Tab *tab, QByteArray &data_to_server){
  314. switch (currCAPTCHA) {
  315. case reCap2: { data_to_server = tab->runJavaScript("grecaptcha.getResponse()", true); break; }
  316. case reCap3: { data_to_server = tab->runJavaScript("ResponseV3", true); break; }
  317. case hCap: { data_to_server = tab->runJavaScript("hcaptcha.getResponse()", true); break; }
  318. default: return false;
  319. }
  320. currCAPTCHA = CAPTCHA::none;
  321. return true;
  322. }
  323. static void load_v2reCAPTHCA(Tab *tab, QString *baseURL, QString *siteKey){
  324. tab->isLoaded = true;
  325. currCAPTCHA = CAPTCHA::reCap2;
  326. //загрузка reCAPTCHA2
  327. tab->setHtml(reCAPTCHA2_source.arg(*siteKey).toLocal8Bit(), QUrl(*baseURL));
  328. }
  329. static void load_v3reCAPTHCA(Tab *tab, QStringList &TaskList){
  330. QString baseURL = TaskList[1];
  331. QString siteKey = TaskList[2];
  332. QString action = TaskList.size() == 4 ? TaskList[3] : "";
  333. Captcha::load_v2reCAPTHCA(tab, &baseURL, &siteKey);
  334. currCAPTCHA = CAPTCHA::reCap3;
  335. //загрузка reCAPTCHA3
  336. tab->runJavaScript(reCAPTCHA3_source1.arg(siteKey));
  337. tab->runJavaScript(reCAPTCHA3_source2.arg(siteKey).arg(action.length() == 0 ? "homepage" : action));
  338. }
  339. static void load_hcaptcha(Tab *tab, QStringList &TaskList){
  340. QString baseURL = TaskList[1];
  341. QString siteKey = TaskList[2];
  342. currCAPTCHA = CAPTCHA::hCap;
  343. //загрузка hCaptcha
  344. tab->setHtml(hCAPTCHA_source.arg(siteKey), QUrl(baseURL));
  345. tab->isLoaded = true;
  346. }
  347. private:
  348. inline static CAPTCHA currCAPTCHA = CAPTCHA::none;
  349. };
  350. #endif // DATAHELPER_H