diff --git a/src/app/core/vapplication.cpp b/src/app/core/vapplication.cpp index fdbbdcf01..803e20794 100644 --- a/src/app/core/vapplication.cpp +++ b/src/app/core/vapplication.cpp @@ -34,6 +34,7 @@ #include "../exception/vexceptionwrongid.h" #include "vmaingraphicsview.h" #include "../container/calculator.h" +#include "../version.h" #include #include @@ -44,6 +45,7 @@ #include const qreal VApplication::PrintDPI = 96.0; +const QString VApplication::GistFileName = QStringLiteral("gist.json"); #define DefWidth 1.2//mm @@ -1848,3 +1850,152 @@ bool VApplication::SafeCopy(const QString &source, const QString &destination, Q return result; } + +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) +//--------------------------------------------------------------------------------------------------------------------- +// Catch exception and create report. Use if program build with Mingw compiler. +// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md +void VApplication::DrMingw() +{ + QFile drmingw("exchndl.dll"); + if(drmingw.exists()) + {// If don't want create reports just delete exchndl.dll from installer + LoadLibrary(L"exchndl.dll"); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VApplication::CollectReports() const +{ + // Seek file "binary_name.RPT" + const QString reportName = QString("%1/%2.RPT").arg(applicationDirPath()) + .arg(QFileInfo(arguments().at(0)).baseName()); + QFile reportFile(reportName); + if (reportFile.exists()) + { // Hooray we have found crash + if (settings == nullptr) + { + return;// Settings was not opened. + } + + if (settings->value("configuration/send_report/state", 1).toBool()) + { // Try send report + // Remove gist.json file before close app. + connect(this, &VApplication::aboutToQuit, this, &VApplication::CleanGist, Qt::UniqueConnection); + SendReport(reportName); + } + else + { // Just collect report to /reports directory + CollectReport(reportName); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VApplication::CollectReport(const QString &reportName) const +{ + const QString reportsDir = QString("%1/reports").arg(qApp->applicationDirPath()); + QDir reports(reportsDir); + if (reports.exists() == false) + { + reports.mkpath("."); // Create directory for reports if need + } + + const QDateTime now = QDateTime::currentDateTime(); + const QString timestamp = now.toString(QLatin1String("yyyyMMdd-hhmmsszzz")); + const QString filename = QString("%1/reports/crash-%2.RPT").arg(qApp->applicationDirPath()).arg(timestamp); + + QFile reportFile(reportName); + reportFile.copy(filename); // Collect new crash + reportFile.remove(); // Clear after yourself +} + +//--------------------------------------------------------------------------------------------------------------------- +void VApplication::CleanGist() const +{ + QFile gistFile(GistFileName); + if (gistFile.exists()) + { + gistFile.remove(); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VApplication::SendReport(const QString &reportName) const +{ + QString content; + QFile reportFile(reportName); + if (!reportFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + return; + } + + QTextStream in(&reportFile); + while (!in.atEnd()) + { + content.append(in.readLine()+"\r\n");// Windows end of line + } + reportFile.close(); + + // Additional information + content.append(QString("-------------------------------")+"\r\n"); + content.append(QString("Version:%1").arg(APP_VERSION)+"\r\n"); + content.append(QString("Based on Qt %2 (32 bit)").arg(QT_VERSION_STR)+"\r\n"); + content.append(QString("Built on %3 at %4").arg(__DATE__).arg(__TIME__)+"\r\n"); + + // Creating json with report + // Example: + //{ + // "description":"Crash report", + // "public":"true", + // "files":{ + // "file1.txt":{ + // "content":"Report text here" + // } + // } + //} + + // Useful to know when crash was created + const QDateTime now = QDateTime::currentDateTime(); + const QString timestamp = now.toString(QLatin1String("yyyy/MM/dd hh:mm:ss:zzz")); + const QString report = QString("Crash report was created %2").arg(timestamp); + + QJsonObject reportObject; + reportObject.insert(QStringLiteral("description"), QJsonValue(report)); + reportObject.insert(QStringLiteral("public"), QJsonValue(QString("true"))); + + QJsonObject contentObject; + contentObject.insert(QStringLiteral("content"), QJsonValue(content)); + + QJsonObject fileObject; + fileObject.insert(QFileInfo(reportName).fileName(), QJsonValue(contentObject)); + reportObject.insert(QStringLiteral("files"), QJsonValue(fileObject)); + + QFile gistFile(GistFileName); + if (!gistFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + qDebug("Couldn't open gist file."); + return; + } + + // Save data to file + QJsonDocument saveRep(reportObject); + gistFile.write(saveRep.toJson()); + gistFile.close(); + + QFile curlFile("curl.exe"); + if (curlFile.exists()) + {// Trying send report + // Change token 28df778e0ef75e3724f7b9622fb70b9c69187779 if need + QString arg = QString("curl.exe -k -H \"Authorization: bearer 28df778e0ef75e3724f7b9622fb70b9c69187779\" " + "-H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST " + "--data @gist.json https://api.github.com/gists"); + QProcess::startDetached(arg); + reportFile.remove();// Clear after yourself + } + else + {// We can not send than just collect + CollectReport(reportName); + } +} +#endif //defined(Q_OS_WIN) && defined(Q_CC_GNU) diff --git a/src/app/core/vapplication.h b/src/app/core/vapplication.h index a92a08e9f..bab8c74f2 100644 --- a/src/app/core/vapplication.h +++ b/src/app/core/vapplication.h @@ -103,6 +103,13 @@ public: static QStringList LabelLanguages(); QString STDescription(const QString &id)const; static bool SafeCopy(const QString &source, const QString &destination, QString &error); + +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) + static void DrMingw(); + void CollectReports() const; +private slots: + void CleanGist() const; +#endif private: Q_DISABLE_COPY(VApplication) Unit _patternUnit; @@ -153,6 +160,13 @@ private: void BiasTokens(int position, int bias, QMap &tokens) const; void InitMeasurement(const QString &name, const VTranslation &m, const VTranslation &g, const VTranslation &d); + +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) + static const QString GistFileName; + + void CollectReport(const QString &reportName) const; + void SendReport(const QString &reportName) const; +#endif }; //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/app/dialogs/app/configpages/configurationpage.cpp b/src/app/dialogs/app/configpages/configurationpage.cpp index 5f14491c2..8e1d13bca 100644 --- a/src/app/dialogs/app/configpages/configurationpage.cpp +++ b/src/app/dialogs/app/configpages/configurationpage.cpp @@ -44,14 +44,17 @@ //--------------------------------------------------------------------------------------------------------------------- ConfigurationPage::ConfigurationPage(QWidget *parent) : QWidget(parent), autoSaveCheck(nullptr), autoTime(nullptr), langCombo(nullptr), labelCombo(nullptr), - unitCombo(nullptr), osOptionCheck(nullptr), langChanged(false), unitChanged(false), labelLangChanged(false) + unitCombo(nullptr), osOptionCheck(nullptr), langChanged(false), unitChanged(false), labelLangChanged(false), + sendReportCheck(nullptr) { QGroupBox *saveGroup = SaveGroup(); QGroupBox *langGroup = LangGroup(); + QGroupBox *sendGroup = SendGroup(); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(saveGroup); mainLayout->addWidget(langGroup); + mainLayout->addWidget(sendGroup); mainLayout->addStretch(1); setLayout(mainLayout); } @@ -76,6 +79,8 @@ void ConfigurationPage::Apply() qApp->getSettings()->setValue("configuration/osSeparator", osOptionCheck->isChecked()); + qApp->getSettings()->setValue("configuration/send_report/state", sendReportCheck->isChecked()); + if (langChanged) { QString locale = qvariant_cast(langCombo->itemData(langCombo->currentIndex())); @@ -273,6 +278,35 @@ QGroupBox *ConfigurationPage::LangGroup() return langGroup; } +//--------------------------------------------------------------------------------------------------------------------- +QGroupBox *ConfigurationPage::SendGroup() +{ + QSettings *settings = qApp->getSettings(); + SCASSERT(settings != nullptr); + + QGroupBox *sendGroup = new QGroupBox(tr("Send crash reports")); + + sendReportCheck = new QCheckBox(tr("Send crash reports (recommended)")); + bool sendReportValue = settings->value("configuration/send_report/state", 1).toBool(); + sendReportCheck->setChecked(sendReportValue); + + QLabel *description = new QLabel(tr("After each crash Valentina collect information that may help us fix a " + "problem. We do not collect any personal information. Find more about what " + "kind of information we collect.")); + description->setTextFormat(Qt::RichText); + description->setTextInteractionFlags(Qt::TextBrowserInteraction); + description->setOpenExternalLinks(true); + description->setWordWrap(true); + + QVBoxLayout *sendLayout = new QVBoxLayout; + sendLayout->addWidget(sendReportCheck); + sendLayout->addWidget(description); + + sendGroup->setLayout(sendLayout); + return sendGroup; +} + //--------------------------------------------------------------------------------------------------------------------- void ConfigurationPage::SetLabelComboBox(const QStringList &list) { diff --git a/src/app/dialogs/app/configpages/configurationpage.h b/src/app/dialogs/app/configpages/configurationpage.h index 0bad4043c..3b83e2996 100644 --- a/src/app/dialogs/app/configpages/configurationpage.h +++ b/src/app/dialogs/app/configpages/configurationpage.h @@ -58,9 +58,11 @@ private: bool langChanged; bool unitChanged; bool labelLangChanged; + QCheckBox *sendReportCheck; QGroupBox *SaveGroup(); QGroupBox *LangGroup(); + QGroupBox *SendGroup(); void SetLabelComboBox(const QStringList &list); }; diff --git a/src/app/main.cpp b/src/app/main.cpp index 79dfad9c8..101bca7d4 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -122,48 +122,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con } } -//--------------------------------------------------------------------------------------------------------------------- -// Catch exception and create report. Use if program build with Mingw compiler. -// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md -void DrMingw() -{ -#if defined(Q_OS_WIN) && defined(Q_CC_GNU) - // Put exchndl.dll near binary file - QFile drmingw("exchndl.dll"); - if(drmingw.exists()) - {// If don't want create reports just delete exchndl.dll from installer - LoadLibrary(L"exchndl.dll"); - } -#endif -} - -//--------------------------------------------------------------------------------------------------------------------- -// Check if last run was crash. -// Each new crash we will lose old crashes reports if don't move them in save place. -// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md -void CollectReport() -{ - // Seek file "binary_name.RPT" - const QString reportName = QString("%1/%2.RPT").arg(qApp->applicationDirPath()) - .arg(QFileInfo(qApp->arguments().at(0)).baseName()); - QFile reportFile(reportName); - if (reportFile.exists()) - { // Hooray we have found crash - const QString reportsDir = QString("%1/reports").arg(qApp->applicationDirPath()); - QDir reports(reportsDir); - if (reports.exists() == false) - { - reports.mkpath("."); // Create directory for reports if need - } - - const QDateTime now = QDateTime::currentDateTime(); - const QString timestamp = now.toString(QLatin1String("yyyyMMdd-hhmmsszzz")); - const QString filename = QString("%1/reports/crash-%2.RPT").arg(qApp->applicationDirPath()).arg(timestamp); - reportFile.copy(filename); // Collect new crash - reportFile.remove(); // Clear after yourself - } -} - //--------------------------------------------------------------------------------------------------------------------- int main(int argc, char *argv[]) { @@ -176,11 +134,6 @@ int main(int argc, char *argv[]) VApplication app(argc, argv); -#if defined(Q_OS_WIN) && defined(Q_CC_GNU) - DrMingw(); - CollectReport(); -#endif - #ifdef QT_DEBUG // Because our "noisy" message handler uses the GUI subsystem for message // boxes, we can't install it until after the QApplication is constructed. But it @@ -198,6 +151,11 @@ int main(int argc, char *argv[]) app.OpenSettings(); +#if defined(Q_OS_WIN) && defined(Q_CC_GNU) + VApplication::DrMingw(); + app.CollectReports(); +#endif + QString checkedLocale = qApp->getSettings()->value("configuration/locale", QLocale::system().name()).toString(); QTranslator qtTranslator;