2018-01-01 09:22:11 +01:00
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "ui_mainwindow.h"
|
|
|
|
#include <QClipboard>
|
|
|
|
#include <QProcess>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QTextStream>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
2018-01-02 23:39:30 +01:00
|
|
|
#include <QFileDialog>
|
2018-02-04 16:14:33 +01:00
|
|
|
#include <ctime>
|
2018-01-01 09:22:11 +01:00
|
|
|
|
|
|
|
const QString WORK_FOLDER(QDir::homePath() + "/triage");
|
2018-02-08 22:18:50 +01:00
|
|
|
const QString DACA2_PACKAGES(QDir::homePath() + "/daca2-packages");
|
2018-01-01 09:22:11 +01:00
|
|
|
|
2019-03-09 11:01:22 +01:00
|
|
|
const int MAX_ERRORS = 100;
|
|
|
|
|
2018-01-01 09:22:11 +01:00
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
|
|
|
QMainWindow(parent),
|
|
|
|
ui(new Ui::MainWindow)
|
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
2019-01-12 09:16:43 +01:00
|
|
|
std::srand(static_cast<unsigned int>(std::time(Q_NULLPTR)));
|
|
|
|
QDir workFolder(WORK_FOLDER);
|
|
|
|
if (!workFolder.exists()) {
|
|
|
|
workFolder.mkdir(WORK_FOLDER);
|
|
|
|
}
|
2018-01-01 09:22:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2018-01-02 23:39:30 +01:00
|
|
|
void MainWindow::loadFile()
|
2018-01-01 09:22:11 +01:00
|
|
|
{
|
2019-01-12 09:16:43 +01:00
|
|
|
ui->statusBar->clearMessage();
|
|
|
|
const QString fileName = QFileDialog::getOpenFileName(this, tr("daca results file"), WORK_FOLDER, tr("Text files (*.txt);;All (*.*)"));
|
2018-01-02 23:39:30 +01:00
|
|
|
if (fileName.isEmpty())
|
|
|
|
return;
|
|
|
|
QFile file(fileName);
|
|
|
|
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
|
|
|
QTextStream textStream(&file);
|
2019-01-14 17:49:10 +01:00
|
|
|
load(textStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::loadFromClipboard()
|
|
|
|
{
|
|
|
|
ui->statusBar->clearMessage();
|
|
|
|
QString clipboardContent = QApplication::clipboard()->text();
|
|
|
|
QTextStream textStream(&clipboardContent);
|
|
|
|
load(textStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::load(QTextStream &textStream)
|
|
|
|
{
|
2018-01-01 09:22:11 +01:00
|
|
|
QString url;
|
2018-02-04 09:12:41 +01:00
|
|
|
QString errorMessage;
|
2019-03-09 11:01:22 +01:00
|
|
|
QStringList versions;
|
|
|
|
mAllErrors.clear();
|
2018-01-02 23:39:30 +01:00
|
|
|
while (true) {
|
2019-01-01 17:01:23 +01:00
|
|
|
QString line = textStream.readLine();
|
2018-01-02 23:39:30 +01:00
|
|
|
if (line.isNull())
|
|
|
|
break;
|
2018-02-04 09:12:41 +01:00
|
|
|
if (line.startsWith("ftp://")) {
|
2018-01-01 09:22:11 +01:00
|
|
|
url = line;
|
2019-01-01 17:01:23 +01:00
|
|
|
if (!errorMessage.isEmpty())
|
2019-03-09 11:01:22 +01:00
|
|
|
mAllErrors << errorMessage;
|
2018-02-04 09:12:41 +01:00
|
|
|
errorMessage.clear();
|
2018-05-02 10:57:04 +02:00
|
|
|
} else if (!url.isEmpty() && QRegExp(".*: (error|warning|style|note):.*").exactMatch(line)) {
|
2019-03-09 11:01:22 +01:00
|
|
|
if (QRegExp("^(head|1.[0-9][0-9]) .*").exactMatch(line)) {
|
|
|
|
const QString version = line.mid(0,4);
|
|
|
|
if (versions.indexOf(version) < 0)
|
|
|
|
versions << version;
|
|
|
|
}
|
2018-11-24 21:36:59 +01:00
|
|
|
if (line.indexOf(": note:") > 0)
|
|
|
|
errorMessage += '\n' + line;
|
|
|
|
else if (errorMessage.isEmpty()) {
|
|
|
|
errorMessage = url + '\n' + line;
|
|
|
|
} else {
|
2019-03-09 11:01:22 +01:00
|
|
|
mAllErrors << errorMessage;
|
2018-11-24 21:36:59 +01:00
|
|
|
errorMessage = url + '\n' + line;
|
|
|
|
}
|
2018-01-01 09:22:11 +01:00
|
|
|
}
|
|
|
|
}
|
2018-11-24 21:36:59 +01:00
|
|
|
if (!errorMessage.isEmpty())
|
2019-03-09 11:01:22 +01:00
|
|
|
mAllErrors << errorMessage;
|
|
|
|
|
|
|
|
ui->version->clear();
|
|
|
|
if (versions.size() > 1)
|
|
|
|
ui->version->addItem("");
|
|
|
|
ui->version->addItems(versions);
|
|
|
|
|
|
|
|
filter("");
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::refreshResults()
|
|
|
|
{
|
|
|
|
filter(ui->version->currentText());
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::filter(QString filter)
|
|
|
|
{
|
|
|
|
QStringList allErrors;
|
|
|
|
|
|
|
|
for (const QString errorItem : mAllErrors) {
|
|
|
|
if (filter.isEmpty()) {
|
|
|
|
allErrors << errorItem;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QStringList lines = errorItem.split("\n");
|
|
|
|
if (lines.size() < 2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (lines[1].startsWith(filter))
|
|
|
|
allErrors << errorItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
ui->results->clear();
|
|
|
|
|
|
|
|
if (ui->random100->isChecked() && allErrors.size() > MAX_ERRORS) {
|
2018-02-09 21:44:53 +01:00
|
|
|
// remove items in /test/
|
2019-03-09 11:01:22 +01:00
|
|
|
for (int i = allErrors.size() - 1; i >= 0 && allErrors.size() > MAX_ERRORS; --i) {
|
2018-02-09 21:44:53 +01:00
|
|
|
if (allErrors[i].indexOf("test") > 0)
|
|
|
|
allErrors.removeAt(i);
|
|
|
|
}
|
2018-02-04 16:14:33 +01:00
|
|
|
std::random_shuffle(allErrors.begin(), allErrors.end());
|
2019-03-09 11:01:22 +01:00
|
|
|
ui->results->addItems(allErrors.mid(0,MAX_ERRORS));
|
2018-05-02 12:59:34 +02:00
|
|
|
ui->results->sortItems();
|
2018-02-04 16:14:33 +01:00
|
|
|
} else {
|
|
|
|
ui->results->addItems(allErrors);
|
|
|
|
}
|
2018-01-01 09:22:11 +01:00
|
|
|
}
|
|
|
|
|
2019-01-12 09:16:43 +01:00
|
|
|
bool MainWindow::runProcess(const QString &programName, const QStringList &arguments)
|
2018-02-08 22:18:50 +01:00
|
|
|
{
|
|
|
|
QProcess process;
|
|
|
|
process.setWorkingDirectory(WORK_FOLDER);
|
2019-01-12 09:16:43 +01:00
|
|
|
process.start(programName, arguments);
|
|
|
|
bool success = process.waitForFinished(-1);
|
|
|
|
if (!success) {
|
|
|
|
QString errorstr(programName);
|
|
|
|
errorstr.append(": ");
|
|
|
|
errorstr.append(process.errorString());
|
|
|
|
ui->statusBar->showMessage(errorstr);
|
|
|
|
} else {
|
|
|
|
int exitCode = process.exitCode();
|
|
|
|
if (exitCode != 0) {
|
|
|
|
success = false;
|
|
|
|
const QByteArray stderrOutput = process.readAllStandardError();
|
|
|
|
QString errorstr(programName);
|
|
|
|
errorstr.append(QString(": exited with %1: ").arg(exitCode));
|
|
|
|
errorstr.append(stderrOutput);
|
|
|
|
ui->statusBar->showMessage(errorstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2019-02-16 17:52:36 +01:00
|
|
|
bool MainWindow::wget(const QString &url)
|
2019-01-12 09:16:43 +01:00
|
|
|
{
|
|
|
|
return runProcess("wget", QStringList() << url);
|
2018-02-08 22:18:50 +01:00
|
|
|
}
|
|
|
|
|
2019-02-16 17:52:36 +01:00
|
|
|
bool MainWindow::unpackArchive(const QString &archiveName)
|
2018-02-08 22:18:50 +01:00
|
|
|
{
|
|
|
|
// Unpack archive
|
|
|
|
QStringList args;
|
2019-01-12 09:16:43 +01:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
/* On Windows --force-local is necessary because tar wants to connect to a remote system
|
|
|
|
* when a colon is found in the archiveName. So "C:/Users/blah/triage/package" would not work
|
|
|
|
* without it. */
|
|
|
|
args << "--force-local";
|
|
|
|
#endif
|
2018-02-08 22:18:50 +01:00
|
|
|
if (archiveName.endsWith(".tar.gz"))
|
2019-01-12 09:16:43 +01:00
|
|
|
args << "-xzvf";
|
2018-02-08 22:18:50 +01:00
|
|
|
else if (archiveName.endsWith(".tar.bz2"))
|
2019-01-12 09:16:43 +01:00
|
|
|
args << "-xjvf";
|
2018-02-08 22:18:50 +01:00
|
|
|
else if (archiveName.endsWith(".tar.xz"))
|
2019-01-12 09:16:43 +01:00
|
|
|
args << "-xJvf";
|
|
|
|
else {
|
|
|
|
// Try to automatically find an (un)compressor for this archive
|
|
|
|
args << "-xavf";
|
|
|
|
}
|
2018-02-08 22:18:50 +01:00
|
|
|
args << archiveName;
|
|
|
|
|
2019-01-12 09:16:43 +01:00
|
|
|
return runProcess("tar", args);
|
2018-02-08 22:18:50 +01:00
|
|
|
}
|
|
|
|
|
2018-01-01 09:22:11 +01:00
|
|
|
void MainWindow::showResult(QListWidgetItem *item)
|
|
|
|
{
|
2019-01-12 09:16:43 +01:00
|
|
|
ui->statusBar->clearMessage();
|
2018-01-01 09:22:11 +01:00
|
|
|
if (!item->text().startsWith("ftp://"))
|
|
|
|
return;
|
|
|
|
const QStringList lines = item->text().split("\n");
|
2018-02-04 09:12:41 +01:00
|
|
|
if (lines.size() < 2)
|
2018-01-01 09:22:11 +01:00
|
|
|
return;
|
|
|
|
const QString url = lines[0];
|
2018-08-29 22:07:31 +02:00
|
|
|
QString msg = lines[1];
|
2018-11-25 12:59:08 +01:00
|
|
|
if (QRegExp("^(head|1.[0-9][0-9]) .*").exactMatch(msg))
|
2018-08-29 22:07:31 +02:00
|
|
|
msg = msg.mid(5);
|
2018-01-01 09:22:11 +01:00
|
|
|
const QString archiveName = url.mid(url.lastIndexOf("/") + 1);
|
|
|
|
const int pos1 = msg.indexOf(":");
|
|
|
|
const int pos2 = msg.indexOf(":", pos1+1);
|
2019-01-12 09:16:43 +01:00
|
|
|
const QString fileName = WORK_FOLDER + '/' + msg.left(msg.indexOf(":"));
|
2019-02-16 17:52:36 +01:00
|
|
|
const int lineNumber = msg.midRef(pos1+1,pos2-pos1-1).toInt();
|
2018-01-01 09:22:11 +01:00
|
|
|
|
2019-02-16 17:52:36 +01:00
|
|
|
if (!QFileInfo::exists(fileName)) {
|
|
|
|
if (QFileInfo::exists(DACA2_PACKAGES + '/' + archiveName.mid(0,archiveName.indexOf(".tar.")) + ".tar.xz")) {
|
2018-02-08 22:18:50 +01:00
|
|
|
if (!unpackArchive(DACA2_PACKAGES + '/' + archiveName.mid(0,archiveName.indexOf(".tar.")) + ".tar.xz"))
|
|
|
|
return;
|
|
|
|
} else {
|
2019-02-16 17:52:36 +01:00
|
|
|
if (!QFileInfo::exists(WORK_FOLDER + '/' + archiveName)) {
|
2018-02-08 22:18:50 +01:00
|
|
|
// Download archive
|
|
|
|
if (!wget(url))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!unpackArchive(WORK_FOLDER + '/' + archiveName))
|
|
|
|
return;
|
|
|
|
}
|
2018-01-01 10:52:51 +01:00
|
|
|
}
|
2018-01-01 09:22:11 +01:00
|
|
|
|
|
|
|
// Open file
|
|
|
|
ui->code->setFocus();
|
2019-01-12 09:16:43 +01:00
|
|
|
QFile f(fileName);
|
|
|
|
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
const QString errorMsg =
|
2019-02-16 17:52:36 +01:00
|
|
|
QString("Opening file %1 failed: %2").arg(f.fileName(), f.errorString());
|
2019-01-12 09:16:43 +01:00
|
|
|
ui->statusBar->showMessage(errorMsg);
|
|
|
|
} else {
|
|
|
|
QTextStream textStream(&f);
|
|
|
|
const QString fileData = textStream.readAll();
|
|
|
|
ui->code->setError(fileData, lineNumber, QStringList());
|
2018-09-01 07:36:58 +02:00
|
|
|
|
2019-01-12 09:16:43 +01:00
|
|
|
ui->edit1->setText(url);
|
|
|
|
ui->edit2->setText(fileName);
|
|
|
|
f.close();
|
|
|
|
}
|
2018-01-01 09:22:11 +01:00
|
|
|
}
|