Open Source Snapmatic picture and Savegame viewer/editor for GTA V https://gta5view.syping.de/

TelemetryClass.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*****************************************************************************
  2. * gta5view Grand Theft Auto V Profile Viewer
  3. * Copyright (C) 2018 Syping
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *****************************************************************************/
  18. #include "TelemetryClassAuthenticator.h"
  19. #include "TelemetryClass.h"
  20. #include "StandardPaths.h"
  21. #include "AppEnv.h"
  22. #include "config.h"
  23. #include <QNetworkAccessManager>
  24. #include <QNetworkRequest>
  25. #include <QHttpMultiPart>
  26. #include <QStringBuilder>
  27. #include <QNetworkReply>
  28. #include <QJsonDocument>
  29. #include <QJsonObject>
  30. #include <QJsonArray>
  31. #include <QSettings>
  32. #include <QSysInfo>
  33. #include <QLocale>
  34. #include <QBuffer>
  35. #include <QDebug>
  36. #include <QFile>
  37. #include <QDir>
  38. #ifndef GTA5SYNC_TELEMETRY_WEBURL
  39. #define GTA5SYNC_TELEMETRY_WEBURL ""
  40. #endif
  41. #ifdef GTA5SYNC_WIN
  42. #include "windows.h"
  43. #include "intrin.h"
  44. #include "d3d9.h"
  45. #endif
  46. TelemetryClass TelemetryClass::telemetryClassInstance;
  47. void TelemetryClass::init()
  48. {
  49. QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
  50. settings.beginGroup("Telemetry");
  51. telemetryEnabled = true;
  52. telemetryStateForced = true;
  53. QString telemetryLegacyClientID = settings.value("ClientID", QString()).toString();
  54. if (telemetryLegacyClientID.isEmpty() || telemetryLegacyClientID == "v2+")
  55. {
  56. telemetryClientID = QString::fromUtf8(QByteArray::fromBase64(settings.value("Identification", QByteArray()).toByteArray()));
  57. }
  58. else
  59. {
  60. QDir dir;
  61. dir.mkpath(StandardPaths::dataLocation());
  62. dir.setPath(StandardPaths::dataLocation());
  63. QString dirPath = dir.absolutePath();
  64. QString portLoc = dirPath % "/.ported";
  65. bool telemetryPortedKey = settings.value("IsPorted", false).toBool();
  66. bool telemetryPortedFile = QFile::exists(portLoc);
  67. if (!telemetryPortedKey && !telemetryPortedFile)
  68. {
  69. QFile portFile(portLoc);
  70. if (portFile.open(QFile::WriteOnly))
  71. {
  72. portFile.write("\n");
  73. portFile.flush();
  74. }
  75. portFile.close();
  76. telemetryClientID = telemetryLegacyClientID;
  77. settings.setValue("Identification", telemetryLegacyClientID.toUtf8().toBase64());
  78. settings.setValue("IsPorted", true);
  79. settings.setValue("ClientID", "v2+");
  80. }
  81. else
  82. {
  83. telemetryClientID = QString();
  84. }
  85. }
  86. telemetryPushAppConf = settings.value("PushAppConf", false).toBool();
  87. settings.endGroup();
  88. }
  89. void TelemetryClass::refresh()
  90. {
  91. init();
  92. }
  93. bool TelemetryClass::canPush()
  94. {
  95. if (!isEnabled() || !isRegistered() || !TelemetryClassAuthenticator::havePushURL()) return false;
  96. return true;
  97. }
  98. bool TelemetryClass::canRegister()
  99. {
  100. QDir dir;
  101. dir.mkpath(StandardPaths::dataLocation());
  102. dir.setPath(StandardPaths::dataLocation());
  103. QString dirPath = dir.absolutePath();
  104. QString regLoc = dirPath % "/.reg";
  105. if (QFile::exists(regLoc)) return false;
  106. if (!isEnabled() || isRegistered() || !TelemetryClassAuthenticator::haveRegURL()) return false;
  107. return true;
  108. }
  109. bool TelemetryClass::isEnabled()
  110. {
  111. return telemetryEnabled;
  112. }
  113. bool TelemetryClass::isStateForced()
  114. {
  115. return telemetryStateForced;
  116. }
  117. bool TelemetryClass::isRegistered()
  118. {
  119. return !telemetryClientID.isEmpty();
  120. }
  121. QString TelemetryClass::getRegisteredID()
  122. {
  123. return telemetryClientID;
  124. }
  125. void TelemetryClass::setEnabled(bool enabled)
  126. {
  127. telemetryEnabled = enabled;
  128. telemetryStateForced = true;
  129. }
  130. void TelemetryClass::setDisabled(bool disabled)
  131. {
  132. telemetryEnabled = !disabled;
  133. telemetryStateForced = true;
  134. }
  135. void TelemetryClass::push(TelemetryCategory category)
  136. {
  137. if (!canPush()) return;
  138. switch (category)
  139. {
  140. case TelemetryCategory::OperatingSystemSpec:
  141. push(category, getOperatingSystem());
  142. break;
  143. case TelemetryCategory::HardwareSpec:
  144. push(category, getSystemHardware());
  145. break;
  146. case TelemetryCategory::UserLocaleData:
  147. push(category, getSystemLocaleList());
  148. break;
  149. case TelemetryCategory::ApplicationConf:
  150. push(category, getApplicationConf());
  151. break;
  152. case TelemetryCategory::ApplicationSpec:
  153. push(category, getApplicationSpec());
  154. break;
  155. case TelemetryCategory::UserFeedback:
  156. break;
  157. case TelemetryCategory::PersonalData:
  158. break;
  159. case TelemetryCategory::CustomEmitted:
  160. break;
  161. }
  162. }
  163. void TelemetryClass::push(TelemetryCategory category, QJsonDocument json)
  164. {
  165. if (!canPush()) return;
  166. QJsonDocument jsonDocument(json);
  167. QJsonObject jsonObject = jsonDocument.object();
  168. jsonObject["ClientID"] = telemetryClientID;
  169. jsonDocument.setObject(jsonObject);
  170. QHttpMultiPart *httpMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
  171. QHttpPart categoryPart;
  172. categoryPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-category\""));
  173. categoryPart.setBody(categoryToString(category).toUtf8());
  174. QHttpPart jsonPart;
  175. jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
  176. jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-deflated\""));
  177. jsonPart.setBody(qCompress(jsonDocument.toJson(QJsonDocument::Compact)));
  178. httpMultiPart->append(categoryPart);
  179. httpMultiPart->append(jsonPart);
  180. QNetworkAccessManager *netManager = new QNetworkAccessManager();
  181. QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingPushURL());
  182. netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
  183. QNetworkReply *netReply = netManager->post(netRequest, httpMultiPart);
  184. httpMultiPart->setParent(netReply);
  185. connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(pushFinished(QNetworkReply*)));
  186. }
  187. QJsonDocument TelemetryClass::getOperatingSystem()
  188. {
  189. QJsonDocument jsonDocument;
  190. QJsonObject jsonObject;
  191. #if QT_VERSION >= 0x050400
  192. jsonObject["KernelType"] = QSysInfo::kernelType();
  193. jsonObject["KernelVersion"] = QSysInfo::kernelVersion();
  194. jsonObject["ProductType"] = QSysInfo::productType();
  195. jsonObject["ProductVersion"] = QSysInfo::productVersion();
  196. jsonObject["OSName"] = QSysInfo::prettyProductName();
  197. jsonObject["OSArch"] = QSysInfo::currentCpuArchitecture();
  198. #endif
  199. jsonDocument.setObject(jsonObject);
  200. return jsonDocument;
  201. }
  202. QJsonDocument TelemetryClass::getSystemHardware()
  203. {
  204. QJsonDocument jsonDocument;
  205. QJsonObject jsonObject;
  206. #ifdef GTA5SYNC_WIN
  207. {
  208. int CPUInfo[4] = {-1};
  209. unsigned nExIds, ic = 0;
  210. char CPUBrandString[0x40];
  211. __cpuid(CPUInfo, 0x80000000);
  212. nExIds = CPUInfo[0];
  213. for (ic = 0x80000000; ic <= nExIds; ic++)
  214. {
  215. __cpuid(CPUInfo, ic);
  216. if (ic == 0x80000002) { memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); }
  217. else if (ic == 0x80000003) { memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); }
  218. else if (ic == 0x80000004) { memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); }
  219. }
  220. jsonObject["CPUName"] = QString::fromLatin1(CPUBrandString).simplified();
  221. SYSTEM_INFO sysInfo;
  222. GetSystemInfo(&sysInfo);
  223. jsonObject["CPUThreads"] = QString::number(sysInfo.dwNumberOfProcessors);
  224. MEMORYSTATUSEX statex;
  225. statex.dwLength = sizeof(statex);
  226. GlobalMemoryStatusEx(&statex);
  227. jsonObject["SystemRAM"] = QString(QString::number((statex.ullTotalPhys / 1024) / 1024) % "MB");
  228. QStringList gpusList;
  229. IDirect3D9 *pD3D = Direct3DCreate9(D3D_SDK_VERSION);
  230. int adapters = pD3D->GetAdapterCount();
  231. for (int ia = 0; ia < adapters; ia++)
  232. {
  233. D3DADAPTER_IDENTIFIER9 d3dIdent;
  234. HRESULT result = pD3D->GetAdapterIdentifier(ia, 0, &d3dIdent);
  235. if (result == D3D_OK)
  236. {
  237. QString gpuAdapter = QString::fromLatin1(d3dIdent.Description);
  238. if (!gpusList.contains(gpuAdapter)) { gpusList << gpuAdapter; }
  239. }
  240. }
  241. pD3D->Release();
  242. jsonObject["GPUs"] = QJsonValue::fromVariant(gpusList);
  243. }
  244. #else
  245. QDir procDir("/proc");
  246. if (procDir.exists())
  247. {
  248. QFile cpuInfo("/proc/cpuinfo");
  249. if (cpuInfo.open(QFile::ReadOnly))
  250. {
  251. QByteArray cpuInfoArray = cpuInfo.readAll();
  252. QBuffer cpuInfoBuffer(&cpuInfoArray);
  253. if (cpuInfoBuffer.open(QBuffer::ReadOnly))
  254. {
  255. QByteArray toFind = "model name";
  256. while (cpuInfoBuffer.canReadLine())
  257. {
  258. QByteArray cpuData = cpuInfoBuffer.readLine();
  259. if (cpuData.left(toFind.length()) == toFind)
  260. {
  261. jsonObject["CPUName"] = QString::fromUtf8(cpuData).split(':').at(1).simplified();
  262. break;
  263. }
  264. }
  265. int cpuThreads = 0;
  266. toFind = "processor";
  267. cpuInfoBuffer.seek(0);
  268. while (cpuInfoBuffer.canReadLine())
  269. {
  270. QByteArray cpuData = cpuInfoBuffer.readLine();
  271. if (cpuData.left(toFind.length()) == toFind)
  272. {
  273. cpuThreads++;
  274. }
  275. }
  276. jsonObject["CPUThreads"] = QString::number(cpuThreads);
  277. }
  278. }
  279. QFile memInfo("/proc/meminfo");
  280. if (memInfo.open(QFile::ReadOnly))
  281. {
  282. QByteArray memInfoArray = memInfo.readAll();
  283. QBuffer memInfoBuffer(&memInfoArray);
  284. if (memInfoBuffer.open(QBuffer::ReadOnly))
  285. {
  286. QByteArray toFind = "MemTotal:";
  287. while (memInfoBuffer.canReadLine())
  288. {
  289. QByteArray memData = memInfoBuffer.readLine();
  290. if (memData.left(toFind.length()) == toFind)
  291. {
  292. QByteArray memDataVal = memData.mid(toFind.length()).trimmed();
  293. int totalMemoryInKB = memDataVal.left(memDataVal.length() - 3).toInt();
  294. jsonObject["SystemRAM"] = QString(QString::number(totalMemoryInKB / 1024) % "MB");
  295. break;
  296. }
  297. }
  298. }
  299. }
  300. }
  301. #endif
  302. jsonDocument.setObject(jsonObject);
  303. return jsonDocument;
  304. }
  305. QJsonDocument TelemetryClass::getApplicationSpec()
  306. {
  307. QJsonDocument jsonDocument;
  308. QJsonObject jsonObject;
  309. #if QT_VERSION >= 0x050400
  310. jsonObject["Arch"] = QSysInfo::buildCpuArchitecture();
  311. #endif
  312. jsonObject["Name"] = GTA5SYNC_APPSTR;
  313. #ifdef GTA5SYNC_COMMIT
  314. jsonObject["Commit"] = GTA5SYNC_COMMIT;
  315. #endif
  316. jsonObject["Version"] = GTA5SYNC_APPVER;
  317. jsonObject["BuildDateTime"] = AppEnv::getBuildDateTime();
  318. jsonObject["BuildType"] = GTA5SYNC_BUILDTYPE;
  319. jsonObject["BuildCode"] = AppEnv::getBuildCode();
  320. jsonObject["QtVersion"] = qVersion();
  321. jsonDocument.setObject(jsonObject);
  322. return jsonDocument;
  323. }
  324. QJsonDocument TelemetryClass::getApplicationConf()
  325. {
  326. QJsonDocument jsonDocument;
  327. QJsonObject jsonObject;
  328. QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
  329. settings.beginGroup("Interface");
  330. QJsonObject interfaceObject;
  331. interfaceObject["AreaLanguage"] = settings.value("AreaLanguage", "Auto").toString();
  332. interfaceObject["Language"] = settings.value("Language", "System").toString();
  333. interfaceObject["NavigationBar"] = settings.value("NavigationBar", false).toBool();
  334. jsonObject["Interface"] = interfaceObject;
  335. settings.endGroup();
  336. settings.beginGroup("Pictures");
  337. QJsonObject picturesObject;
  338. picturesObject["AspectRatio"] = ((Qt::AspectRatioMode)settings.value("AspectRatio").toInt() == Qt::IgnoreAspectRatio) ? "IgnoreAspectRatio" : "KeepAspectRatio";
  339. picturesObject["CustomQuality"] = settings.value("CustomQuality", 100).toInt();
  340. picturesObject["CustomQualityEnabled"] = settings.value("CustomQualityEnabled", false).toBool();
  341. picturesObject["ExportSizeMode"] = settings.value("ExportSizeMode", "Default").toString();
  342. jsonObject["Pictures"] = picturesObject;
  343. settings.endGroup();
  344. settings.beginGroup("Profile");
  345. QJsonObject profileObject;
  346. int contentMode = settings.value("ContentMode", 0).toInt();
  347. switch (contentMode)
  348. {
  349. case 0:
  350. profileObject["ContentMode"] = "OpenWithSingleClick";
  351. break;
  352. case 1:
  353. profileObject["ContentMode"] = "OpenWithDoubleClick";
  354. break;
  355. case 2:
  356. profileObject["ContentMode"] = "SelectWithSingleClick";
  357. break;
  358. }
  359. jsonObject["Profile"] = profileObject;
  360. settings.endGroup();
  361. settings.beginGroup("Startup");
  362. QJsonObject startupObject;
  363. startupObject["AppStyle"] = settings.value("AppStyle", "System").toString();
  364. startupObject["CustomStyle"] = settings.value("CustomStyle", false).toBool();
  365. startupObject["StartCount"] = QString::number(settings.value("StartCount", 0).toUInt());
  366. jsonObject["Startup"] = startupObject;
  367. settings.endGroup();
  368. jsonDocument.setObject(jsonObject);
  369. return jsonDocument;
  370. }
  371. QJsonDocument TelemetryClass::getSystemLocaleList()
  372. {
  373. QJsonDocument jsonDocument;
  374. QJsonObject jsonObject;
  375. QStringList languagesList = QLocale::system().uiLanguages();
  376. if (languagesList.length() >= 1)
  377. {
  378. jsonObject["PrimaryLanguage"] = languagesList.at(0);
  379. }
  380. if (languagesList.length() >= 2)
  381. {
  382. languagesList.removeAt(0);
  383. jsonObject["SecondaryLanguages"] = QJsonValue::fromVariant(languagesList);
  384. }
  385. jsonDocument.setObject(jsonObject);
  386. return jsonDocument;
  387. }
  388. QString TelemetryClass::categoryToString(TelemetryCategory category)
  389. {
  390. switch (category)
  391. {
  392. case TelemetryCategory::OperatingSystemSpec:
  393. return QString("OperatingSystemSpec");
  394. break;
  395. case TelemetryCategory::HardwareSpec:
  396. return QString("HardwareSpec");
  397. break;
  398. case TelemetryCategory::UserLocaleData:
  399. return QString("UserLocaleData");
  400. break;
  401. case TelemetryCategory::ApplicationConf:
  402. return QString("ApplicationConf");
  403. break;
  404. case TelemetryCategory::ApplicationSpec:
  405. return QString("ApplicationSpec");
  406. break;
  407. case TelemetryCategory::UserFeedback:
  408. return QString("UserFeedback");
  409. break;
  410. case TelemetryCategory::PersonalData:
  411. return QString("PersonalData");
  412. break;
  413. case TelemetryCategory::CustomEmitted:
  414. return QString("CustomEmitted");
  415. break;
  416. default:
  417. return QString("UnknownCategory");
  418. break;
  419. }
  420. }
  421. QUrl TelemetryClass::getWebURL()
  422. {
  423. return QUrl(GTA5SYNC_TELEMETRY_WEBURL);
  424. }
  425. void TelemetryClass::registerClient()
  426. {
  427. QNetworkAccessManager *netManager = new QNetworkAccessManager();
  428. QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingRegURL());
  429. netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
  430. netManager->get(netRequest);
  431. connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(registerFinished(QNetworkReply*)));
  432. }
  433. void TelemetryClass::work()
  434. {
  435. if (!canPush() && canRegister())
  436. {
  437. connect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool)));
  438. registerClient();
  439. }
  440. else if (canPush())
  441. {
  442. work_p(true);
  443. }
  444. }
  445. void TelemetryClass::work_p(bool doWork)
  446. {
  447. if (doWork)
  448. {
  449. push(TelemetryCategory::ApplicationSpec);
  450. push(TelemetryCategory::UserLocaleData);
  451. push(TelemetryCategory::OperatingSystemSpec);
  452. push(TelemetryCategory::HardwareSpec);
  453. if (telemetryPushAppConf)
  454. {
  455. push(TelemetryCategory::ApplicationConf);
  456. }
  457. else
  458. {
  459. push(TelemetryCategory::ApplicationConf, QJsonDocument());
  460. }
  461. }
  462. }
  463. void TelemetryClass::work_pd(bool doWork)
  464. {
  465. disconnect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool)));
  466. work_p(doWork);
  467. }
  468. void TelemetryClass::pushFinished(QNetworkReply *reply)
  469. {
  470. bool isSuccessful = false;
  471. if (reply->canReadLine())
  472. {
  473. QByteArray readedData = reply->readLine();
  474. if (QString::fromUtf8(readedData).trimmed() == QString("Submit success!"))
  475. {
  476. #ifdef GTA5SYNC_DEBUG
  477. qDebug() << "Telemetry" << QString("Submit success!");
  478. #endif
  479. isSuccessful = true;
  480. #ifdef GTA5SYNC_DEBUG
  481. if (reply->isReadable())
  482. {
  483. readedData = reply->readAll().trimmed();
  484. if (!readedData.isEmpty()) { qDebug() << "Telemetry Push" << readedData; }
  485. }
  486. #endif
  487. }
  488. else
  489. {
  490. #ifdef GTA5SYNC_DEBUG
  491. qDebug() << "Telemetry" << QString("Submit failed!");
  492. #endif
  493. }
  494. }
  495. else
  496. {
  497. #ifdef GTA5SYNC_DEBUG
  498. qDebug() << "Telemetry" << QString("Submit failed!");
  499. #endif
  500. }
  501. reply->deleteLater();
  502. sender()->deleteLater();
  503. emit pushed(isSuccessful);
  504. }
  505. void TelemetryClass::registerFinished(QNetworkReply *reply)
  506. {
  507. bool isSuccessful = false;
  508. if (reply->canReadLine())
  509. {
  510. QByteArray readedData = reply->readLine();
  511. if (QString::fromUtf8(readedData).trimmed() == QString("Registration success!") && reply->canReadLine())
  512. {
  513. QDir dir;
  514. dir.mkpath(StandardPaths::dataLocation());
  515. dir.setPath(StandardPaths::dataLocation());
  516. QString dirPath = dir.absolutePath();
  517. QString regLoc = dirPath % "/.reg";
  518. readedData = reply->readLine();
  519. telemetryClientID = QString::fromUtf8(readedData).trimmed();
  520. QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
  521. settings.beginGroup("Telemetry");
  522. settings.setValue("Identification", telemetryClientID.toUtf8().toBase64());
  523. settings.endGroup();
  524. QFile regFile(regLoc);
  525. if (regFile.open(QFile::WriteOnly))
  526. {
  527. regFile.write("\n");
  528. regFile.flush();
  529. }
  530. regFile.close();
  531. #ifdef GTA5SYNC_DEBUG
  532. qDebug() << "Telemetry" << QString("Registration success!");
  533. #endif
  534. isSuccessful = true;
  535. }
  536. else
  537. {
  538. #ifdef GTA5SYNC_DEBUG
  539. qDebug() << "Telemetry" << QString("Registration failed!");
  540. #endif
  541. }
  542. }
  543. else
  544. {
  545. #ifdef GTA5SYNC_DEBUG
  546. qDebug() << "Telemetry" << QString("Registration failed!");
  547. #endif
  548. }
  549. reply->deleteLater();
  550. sender()->deleteLater();
  551. emit registered(isSuccessful);
  552. }