rsyncui/mainwindow.cpp

1438 lines
47 KiB
C++

#include "mainwindow.h"
#include <QComboBox>
#include <QTextBlock>
#include "version.h"
using namespace std;
bool display = false;
extern Ui::Configuration config;
extern bool testRsyncReturn(QProcess *);
extern QApplication a;
QMap<int, QString> rsyncErrorStrings {
{0, QTranslator::tr("Success. The rsync command completed successfully without any errors.")},
{1, QTranslator::tr("Syntax or usage error. There was a problem with the syntax of the rsync command or with the options specified.")},
{2, QTranslator::tr("Protocol incompatibility. There was a problem with the protocol version or negotiation between the rsync client and server.")},
{3, QTranslator::tr("Errors selecting input/output files, dirs. There was a problem with the source or destination file or directory specified in the rsync command.")},
{4, QTranslator::tr("Requested action not supported: An attempt was made to use an unsupported action or option.")},
{5, QTranslator::tr("Error starting client-server protocol. There was an error starting the client-server protocol.")},
{6, QTranslator::tr("Daemon unable to append to log-file. The rsync daemon was unable to write to its log file.")},
{10, QTranslator::tr("Error in socket I/O. There was an error with the socket input/output.")},
{11, QTranslator::tr("Error in file I/O. There was an error reading or writing to a file.")},
{12, QTranslator::tr("Error in rsync protocol data stream. There was an error in the rsync protocol data stream.")},
{13, QTranslator::tr("Errors with program diagnostics. There was an error generating program diagnostics.")},
{14, QTranslator::tr("Error in IPC code. There was an error in the inter-process communication (IPC) code.")},
{20, QTranslator::tr("Received SIGUSR1 or SIGINT. The rsync process was interrupted by a signal.")},
{21, QTranslator::tr("Some error returned by waitpid(). An error occurred while waiting for a child process to complete.")},
{22, QTranslator::tr("Error allocating core memory buffers. There was an error allocating memory buffers.")},
{23, QTranslator::tr("Partial transfer due to error. The rsync command completed with an error, but some files may have been transferred successfully.")},
{24, QTranslator::tr("Partial transfer due to vanished source files. Some source files disappeared before they could be transferred.")}
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QCoreApplication::setOrganizationName("RsyncUI");
QCoreApplication::setApplicationName("RsyncUI");
this->setWindowTitle(a.applicationName());
// context menu for treewidget (list of files)
ui->treeWidget->addAction(ui->actionDownload);
// init configuration window
config.setupUi(&Configuration);
// init about window
AboutW.setupUi(&aboutDialog);
// text of About
this->about.version = version;
QString aboutText = tr("<h2>Client for rsync server</h2>") +
"<b>" + tr("Version") + ": " + version + "</b><br>" +
"<b>" + tr("Licence") + ": " + this->about.licence + "</b><br>" +
"<b>" + tr("Author") + ": " + this->about.author + "</b><br>" +
"<b>" + tr("EMail") + ": " + this->about.email + "</b><br>" +
"<b>" + tr("Source code") + ": " + this->about.git + "</b><br>" +
tr("You click on file to enqueue it, and RyncUI Download one file a time");
AboutW.TextAbout->setHtml(aboutText);
// connectors
connect(this, &MainWindow::fileName, ui->progressBar, &QProgressBar::setFormat);
connect(this, &MainWindow::progressSignal, ui->progressBar, &QProgressBar::setValue);
connect(this, &MainWindow::stopDownloading, this, &MainWindow::cancelled);
connect(config.buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
connect(config.comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::on_comboBox_currentIndexChanged);
loadSettings();
// init of widgets
ui->ktreewidgetsearchline->setTreeWidget(ui->treeWidget); // attach search widget to treewidget
ui->ktreewidgetsearchline->setCaseSensitivity(Qt::CaseInsensitive); // and set it case insensitive
ui->treeWidget->setHeaderLabels({tr("Path"), tr("Type"), tr("Size"), tr("Date"), "fullSize"} ); // set header of columns of tree widget
//ui->treeWidget->header()->setContextMenuPolicy();
// setting arrowcursor for treeWidget, listWidget and listDownload to arrow
ui->treeWidget->setCursor(Qt::ArrowCursor);
ui->listWidget->setCursor(Qt::ArrowCursor);
ui->listDownload->setCursor(Qt::ArrowCursor);
// Hiding progress bar
ui->progressBar->hide();
// if last server exists in settings
if (this->settings.contains("connexion/lastServer"))
{
// set window to precedent server/port configuration
ui->portEdit->setText(this->settings.value("connexion/port").toString());
ui->khistorycombobox->setCurrentText(this->settings.value("connexion/lastServer").toString());
}else
{
ui->portEdit->setText(QString::number(this->connexion.port));
ui->khistorycombobox->clear();
}
//setting configuration window
config.comboBox->setCurrentIndex(ui->toolBar->toolButtonStyle()); // setting combobox to saved settings
//setting unit of bandwidth limit
config.UnitCombobox->addItems({tr("KB/s"), tr("MB/s"), tr("GB/s"), tr("TB/s"), tr("PB/s")});
// hide fullsize column of treeview
ui->treeWidget->setColumnHidden(4, true);
initSystemTrayIcon();
}
void MainWindow::init()
{
QAbstractButton * reply;
QMessageBox msgBox;
//if exists list of donwloads in saved settings
if (this->settings.value("Downloads/rows").toInt() != 0)
{
// asking if we load the list and continue downloading
msgBox.setWindowTitle(a.applicationName());
msgBox.setInformativeText(tr("A list of interrupted downloads exists, do you want to continue downloading ? if not the list will be cleared" ));
QPushButton *yes = msgBox.addButton(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.exec();
reply = msgBox.clickedButton();
// if response is yes then loading list
if(reply == yes)
{
loadDownloadList();
}
}
// load list of services
populateList(ui->khistorycombobox->currentIndex());
}
void MainWindow::initSystemTrayIcon()
{
QMenu *trayIconMenu;
QAction * quitAction;
QIcon icon;
icon.addFile(this->icon);
this->trayIcon = new QSystemTrayIcon;
this->trayIcon->setIcon(icon);
quitAction = new QAction(tr("&Quit"), this);
connect(quitAction, &QAction::triggered, this, &MainWindow::quitApp);
trayIconMenu = new QMenu(this);
trayIconMenu->addAction(quitAction);
trayIcon->setContextMenu(trayIconMenu);
connect(this->trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(on_trayIcon_clicked(QSystemTrayIcon::ActivationReason)));
this->trayIcon->show();
}
void MainWindow::on_trayIcon_clicked(QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::Trigger)
{
if (this->isHidden())
{
this->show();
}else
{
this->hide();
}
}
}
void MainWindow::hideWindow()
{
this->hide();
}
void MainWindow::showWindow()
{
this->show();
}
MainWindow::~MainWindow()
{
delete ui;
QCoreApplication::quit();
}
void MainWindow::quitApp()
{
QMessageBox::StandardButton reply;
QMessageBox::StandardButtons param;
QString displayText;
// saving settings
saveSettings();
if (ui->listDownload->count() != 0) // some downloads waiting
{
// Asking for stopping or continuing
param = QMessageBox::Yes|QMessageBox::No;
if (config.autosaveCheckbox->checkState() != Qt::Checked)
{
param |= QMessageBox::Save;
displayText = tr("Clicking Save button, You can save the list of downloads\n");
}
reply = QMessageBox::question(
this,
a.applicationName(),
tr("Exiting will stop downloading, and will clear the download queue.\nDo you want to exit ?") + displayText,
param,
QMessageBox::No);
this->downloading.quit = true;
if(reply == QMessageBox::Yes)
{
if (this->downloading.process->state() != QProcess::NotRunning)
{
// emission of signal to downloading thread and stopping
emit (stopDownloading(this->downloading.process));
}
}else if (reply == QMessageBox::Save)
{
if (config.autosaveCheckbox->checkState() == Qt::Unchecked)
{
//saveDownloadList();
emit (stopDownloading(this->downloading.process));
}
}else if (reply == QMessageBox::No)
{
return;
}
}
QCoreApplication::quit();
}
// Close window has been clicked
void MainWindow::closeEvent (QCloseEvent *event)
{
QMessageBox msgBox;
QCheckBox *cb;
if (!event->spontaneous() || !isVisible())
return;
if (trayIcon->isVisible() and this->settings.value("CloseCheckbox").toBool() == false)
{
cb = new QCheckBox("Don't show this again ?");
msgBox.setWindowTitle(a.applicationName());
msgBox.setInformativeText(tr("The program will keep running in the "
"system tray. To terminate the program, "
"choose <b>Quit</b> in the context menu "
"of the system tray entry."));
msgBox.addButton(QMessageBox::Ok);
//msgBox.addButton(QMessageBox::No);
// msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setCheckBox(cb);
msgBox.exec();
msgBox.clickedButton();
// if response is yes then loading list
if (cb->isChecked())
{
this->settings.setValue("CloseCheckbox", true);
}
hide();
//event->accept();
event->ignore();
}
}
// Populate treeview with list of files
void MainWindow::populateTree()
{
QString path;
// Clear treewidget
ui->treeWidget->clear();
if (!this->connexion.server.isEmpty() and this->connexion.port > 0 and this->connexion.port < 65536)
{
// setting cursor to "Wait"
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
// validating server's address
if (validateServer(this->connexion.server))
{
// server is validated, scanning directory
path = this->connexion.service + "/";
while (this->rescan)
{
scanDir(this->connexion.server, this->connexion.port, nullptr, path);
}
}
// Restoring cursor
QGuiApplication::restoreOverrideCursor();
}
}
// Populate Listview with list of services
void MainWindow::populateList(int item)
{
QString server;
QString service;
QStringList hidden;
int port;
int i;
if (item == -1)
{
server = ui->khistorycombobox->currentText();
}else
{
server = ui->khistorycombobox->itemText(item);
}
port = ui->portEdit->text().toUInt();
if ((server != this->connexion.server) or (port != this->connexion.port))
{
// clearing listwidget
ui->listWidget->clear();
this->connexion.server = server;
this->connexion.port = port;
// setting cursor to "Wait"
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
// verify if server is in history
this->settings.beginGroup("connexion/server");
if (this->settings.contains(server) and this->connexion.comboboxChanged)
{
// server is in history => setting port value
port = this->settings.value(server).toUInt();
ui->portEdit->setText(QString::number(port));
this->connexion.port = port;
//display list of services
listServices();
}else
{
if (!server.isEmpty() and (port > 0 and port < 65536))
{
if (validateServer(server))
{
cout << server.toStdString() << endl;
// storing serverURL and port in settings
this->settings.setValue(server, port);
this->settings.sync();
this->downloading.server = server;
// storing in history of combobox
ui->khistorycombobox->addToHistory(server);
// load and display rsync services of the rsync server
listServices();
}
}
}
this->settings.endGroup();
this->settings.beginGroup("Hidden/" + server);
hidden = this->settings.allKeys();
if (hidden.count() > 0)
{
for (i = 0; i < hidden.size(); i++)
{
service = hidden[i];
//TODO detect if service is already present
if (testServicePresence(service, false))
{
ui->listWidget->addItem(service + "\n\t");
}
}
}
this->settings.endGroup();
QGuiApplication::restoreOverrideCursor(); //setting cursor to default
}
}
// Test if service is already present on the server
bool MainWindow::testServicePresence(QString service, bool askPassword)
{
QString cmd;
QStringList param;
QString line;
QString errorRsync;
QStringList v;
QProcess *myProcess;
bool returnValue = false;
int loop =0;
bool r = false;
cmd = "/usr/bin/rsync";
param << "--contimeout=10" << "-nq" << "--port=" + QString::number(this->connexion.port) << this->connexion.server + "::" + service;
myProcess = new QProcess(this);
myProcess->setProcessChannelMode(QProcess::MergedChannels);
myProcess->start(cmd, param);
myProcess->waitForStarted();
myProcess->write("\n");
//myProcess->waitForFinished(12000);
while(myProcess->waitForReadyRead(10000))
{
while(1)
{
// line empty then buffer is empty so returning to wait new datas
line = QString::fromUtf8(myProcess->readLine());
if (line.isEmpty())
{
break;
}
if (line.contains("auth failed"))
{
if (askPassword == true and loop >=2 and r == true)
{
r = getUserPassword(&this->connexion);
loop++;
}
returnValue = true;
}
}
}
if (myProcess->exitCode() == 0)
{
returnValue = true;
}
myProcess->close();
return returnValue;
}
//list services of the rsync server
void MainWindow::listServices()
{
QString cmd;
QStringList param;
QString line;
QString errorRsync;
QStringList v;
QString service;
QProcess *myProcess;
bool flag = false;
cmd = "/usr/bin/rsync";
param << "--contimeout=20" << "--port=" + QString::number(this->connexion.port) << this->connexion.server + "::";
myProcess = new QProcess(this);
myProcess->start(cmd, param);
// waiting for response of the server with a timeout of 10 seconds
while(myProcess->waitForReadyRead(10000))
{
while(!flag)
{
line = QString::fromUtf8(myProcess->readLine());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
flag = true;
break;
}
// extracting name and comment of the service
v = line.split("\t");
v[0].replace(" ", "");
v[1].replace("\n", "");
service = v[0] + "\n\t" + v[1];
// adding to list of services
ui->listWidget->addItem(service);
}
// buffer empty go to waiting new datas
flag =false;
}
// verifying error code
testRsyncReturn(this, myProcess);
myProcess->close();
}
// connect to rsync server to get list of files
bool MainWindow::scanDir(QString server, int portN, QTreeWidgetItem *parent, QString path)
{
QString cmd;
QStringList param;
QStringList sizeA;
QString line;
QString size;
QString fullsize;
QString filename;
QString fileType;
QString date;
QString dirName = "";
QProcess * myProcess;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
bool isDir = false;
bool flag = false;
bool readOk = false;
bool passwdOk = false;
int nChild = 0;
static uint looping;
QStringList dirs;
myProcess = new QProcess(this);
myProcess->setProcessChannelMode(QProcess::MergedChannels);
if (parent != nullptr)
{
nChild = parent->childCount();
}
if (nChild == 0)
{
if (!this->connexion.user.isEmpty())
{
server.prepend(this->connexion.user + "@");
env.insert("RSYNC_PASSWORD", this->connexion.password); // Add an environment variable
}else
{
server.prepend("anonymous@");
env.insert("RSYNC_PASSWORD", "anonymous"); // Add an environment variable
}
myProcess->setProcessEnvironment(env);
cmd = "rsync";
param << "--contimeout=20" << "--port=" + QString::number(portN) << server + "::" + path;
myProcess->start(cmd, param);
this->rescan = true;
// waiting for response of the server with a timeout of 10 seconds
do
{
readOk = myProcess->waitForReadyRead(10000);
if (readOk)
{
while (!flag)
{
line = QString::fromUtf8(myProcess->readLine());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
flag = true;
break;
}
if (line.contains("auth failed"))
{
myProcess->readAllStandardOutput();
getUserPassword(&this->connexion);
if (looping <= 1)
{
this->rescan = true;
looping++;
}else
{
this->rescan = false;
looping = 0;
QMessageBox::warning(
this,
a.applicationName(),
tr("Authentication failed" )
);
}
return false;
}
// extracting name, size and is dir/file
line = line.simplified();
filename = line.section(" ", 4);
if (filename != '.')
{
size = line.section(" ", 1, 1);
fullsize = size;
fullsize.remove(",");
sizeA = size.split(',');
if (sizeA.count() <= 1)
{
size = sizeA.at(0) + " " + UnitText[0] + " ";
}else
{
myProcess->setProcessEnvironment(env);
size = sizeA.at(0) + "," + sizeA.at(1).left(2) + " " + UnitText[sizeA.count()-1] + " ";
}
if (line[0] == "d")
{
isDir = true;
}else
{
isDir = false;
}
date = line.section(' ', 2, 2);
fileType = getFileType(filename);
if (!path.isEmpty())
{
if (path.endsWith('/'))
{
path.chop(1);
}
dirs = path.split('/');
dirName = dirs[dirs.size()-1];
}
addTreeItem(filename, size, fullsize, fileType, date, isDir, dirName, parent);
if (passwdOk == false and !this->connexion.password.isEmpty())
{
this->settings.setValue("Passwords/" + this->connexion.server + "/" + this->connexion.service + "/" + this->connexion.user, true);
setPassword(this->connexion.user, this->connexion.password);
this->settings.sync();
}
this->rescan = false;
}
}
flag = false;
}else
{
if (myProcess->state() == QProcess::Running)
{
if (myProcess->waitForFinished(10000) == 0)
{
QMessageBox::warning(
this,
a.applicationName(),
tr("The processus does'nt respond: ") + myProcess->errorString());
}
}
}
}while(readOk);
// buffer empty go to waiting new datas
testRsyncReturn(this, myProcess);
myProcess->close();
}
return false;
}
// Verify if server address is IP address
bool MainWindow::isIpAddress(QString server)
{
QStringList r;
int elementN;
bool ok;
r = server.split('.');
if (r.size() == 4)
{
for (auto element : r)
{
elementN = element.toInt(&ok);
if ((elementN < 0) or (elementN > 255) or ok == false)
{
return false;
}
}
return true;
}else
{
return false;
}
}
// validate address server
bool MainWindow::validateServer(QString server)
{
QString cmd;
QStringList param;
QString line;
QProcess * myProcess;
bool flag = false;
bool bflag = false;
cmd = "dig";
param << server;
myProcess = new QProcess(this);
myProcess->start(cmd, param);
// maiking a dig on the server's address
while(myProcess->waitForReadyRead())
{
while (!bflag)
{
line = QString::fromUtf8(myProcess->readAllStandardOutput());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
bflag = true;
break;
}else if (line.indexOf(";; ANSWER SECTION:") != -1)
{
flag = true;
}
}
bflag = false;
}
//testRsyncReturn(myProcess);
if ( flag == false)
{
//server's address is not valid testing if ip address is valid
flag = isIpAddress(server);
}
if ( flag == false)
{
// server-s address not valid
QMessageBox::warning(
this,
a.applicationName(),
tr("server does not exists" )
);
}
myProcess->close();
return flag;
}
// slot activated when combobox is changed
void MainWindow::on_khistorycombobox_currentIndexChanged(int item)
{
this->connexion.comboboxChanged = true;
populateList(item);
}
// slot activated when button connection is clicked
void MainWindow::on_connectButton_clicked()
{
populateList(ui->khistorycombobox->currentIndex());
}
// add parent in treeview
void MainWindow::addTreeItem(QString name, QString fileSize, QString fullsize, QString type, QString date, bool isDir=false, QString dirName="", QTreeWidgetItem *parent=nullptr)
{
QFont font;
QTreeWidgetItem *treeItem;
if (parent != nullptr)
{
treeItem = new QTreeWidgetItem();
}else
{
treeItem = new QTreeWidgetItem(ui->treeWidget);
}
// item is a file
if (QFile::exists(this->downloading.savePath + "/" + name) or QFile::exists(this->downloading.savePath + "/" + dirName + "/" + name))
{
QBrush b (Qt::green);
treeItem->setForeground(0, b);
treeItem->setFont(0, font);
}else if (this->settings.contains(name))
{
QBrush b (Qt::red);
treeItem->setForeground(0, b);
treeItem->setFont(0, font);
}
if (isDir == true)
{
// item is a dir
treeItem->setText(1, tr("Dir"));
treeItem->setIcon(0, QIcon::fromTheme("folder"));
}else
{
this->settings.beginGroup("Downloaded/");
this->settings.endGroup();
treeItem->setText(1,type);
}
treeItem->setText(0, name);
treeItem->setText(2, fileSize);
treeItem->setText(3, date);
treeItem->setText(4, fullsize);
treeItem->setTextAlignment(2, Qt::AlignRight);
if (parent != nullptr)
{
// QTreeWidgetItem::addChild(QTreeWidgetItem * child)
parent->addChild(treeItem);
}
}
// Slot activated when a service in the list is clicked
void MainWindow::on_listWidget_clicked()
{
this->connexion.service = ui->listWidget->currentItem()->text().section("\n", 0 ,0);
ui->treeWidget->clear();
preparePopulateTree();
}
void MainWindow::preparePopulateTree()
{
QString str;
QStringList logins;
this->rescan = true;
this->connexion.user = nullptr;
this->connexion.password = nullptr;
str = "Folder/" + this->connexion.server + "/" + this->connexion.service;
// if service exists in settings for this server
if (this->settings.contains(str))
{
// setting savePath from settings
this->downloading.savePath = this->settings.value(str).toString();
}
//getUserPassword(&this->connexion);
populateTree();
}
// get password and user login
// if object = false ==> searching from connexion object
// else searching from downloading object
bool MainWindow::getUserPassword(Connexion * object)
{
QStringList logins;
QString login = "";
QString password = "";
QString server;
QString service;
int c;
bool returnValue = false;
bool ok = false;
server = object->server;
service = object->service;
object->user = "";
object->password = "";
this->settings.beginGroup("Passwords/" + server + "/" + service);
logins = this->settings.allKeys();
c = logins.count();
if ( c != 1)
{
//choose login in case of multiples logins
login = QInputDialog::getItem(this,
"RsincUI",
tr("Select the user you want to connect with or enter a new one"),
logins,
0,
true,
&ok,
Qt::Popup,
Qt::ImhNoPredictiveText
);
if (ok and !login.isEmpty())
{
if (!logins.contains(login))
{
password = QInputDialog::getText(this,
a.applicationName() + tr(" Request"),
tr("Enter password"), QLineEdit::Password,
"", &ok, Qt::Popup,
Qt::ImhNoPredictiveText);
if (!ok or password.isEmpty())
{
password = "";
returnValue = false;
}
}else
{
password = getPassword(login);
returnValue = true;
}
object->user = login;
object->password = password;
}
}else
{
object->user = logins.at(0);
object->password = getPassword(object->user);
returnValue = true;
}
this->settings.endGroup();
return returnValue;
}
//Slot activated when a file is clicked in the treeview
void MainWindow::on_treeWidget_itemClicked(QTreeWidgetItem *item, bool downloadDir)
{
//#QFileDialog dialog;
QTreeWidgetItem * itemR;
QString path;
QString str;
QMessageBox::StandardButton reply;
int sizeFromRsync;
if (treeviewClicked == true)
{
return;
}
treeviewClicked = true;
itemR = item;
this->rescan = true;
// assembling path from treewidget
path = item->text(0);
sizeFromRsync = item->text(4).toUInt();
while(itemR->parent() != NULL)
{
itemR = itemR->parent();
// concatening parent to path
path.prepend(itemR->text(0) + "/");
};
cout << item->text(1).toStdString() <<endl;
if (item->text(1) != tr("Dir") or downloadDir == true)
{
// exists saving path in settings ?
str = "Folder/" + this->connexion.server + "/" + this->connexion.service;
if(!this->settings.contains(str))
{
// saving path do not exists, asking for it
if(!on_DefaultSaveFolder_triggered())
{
cout << "no directory selectioned, ignoring download request" << endl;
return;
}
}else
{
this->downloading.savePath = this->settings.value(str).toString();
}
// Item is a file
// searching if file exists in savepath
if (QFile::exists(this->downloading.savePath + "/" + path))
{
QFileInfo info(this->downloading.savePath + "/" + path);
if (info.size() < sizeFromRsync)
{
reply = QMessageBox::question(
this,
a.applicationName(),
tr("File is partially downloaded. Do you want to resume download ? if no, the file will be deleted from destination directory"),
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,
QMessageBox::Cancel);
if (reply == QMessageBox::Cancel)
{
return;
}else if(reply == QMessageBox::No)
{
QFile::remove(this->downloading.savePath + "/" + path);
return;
}
}else
{
reply = QMessageBox::question(
this,
a.applicationName(),
tr("File is already downloaded. Do you want to reload it ? The old file will be deleted"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::No)
{
return;
}else
{
QFile::remove(this->downloading.savePath + "/" + path);
}
}
}
if(ui->listDownload->findItems(path, Qt::MatchStartsWith).empty())
{
// is there a downloading process ?
if (this->downloading.process == nullptr)
{
// no downloading process launching it
this->downloading.path = path;
this->downloading.server = this->connexion.server;
this->downloading.port = this->connexion.port;
this->downloading.service = this->connexion.service;
startDownloading();
// wait 1 second to process start
//sleep(1);
}
// Adding download in download list
str = path + " => " + this->connexion.server + "/" + this->connexion.service;
ui->listDownload->addItem(str);
}else
{
QMessageBox::warning(
this,
a.applicationName(),
tr("File is already downloading" )
);
}
}else
{
//Item is a Directory
if (item->isExpanded() == false)
{
while (this->rescan)
{
scanDir(this->connexion.server, this->connexion.port, item, this->connexion.service + "/" + path +"/");
item->setExpanded(true);
}
}
}
if (config.autosaveCheckbox->checkState() == Qt::Checked)
{
saveDownloadList();
}
treeviewClicked = false;
}
// Launch the thread which download the file
void MainWindow::startDownloading()
{
ui->progressBar->setValue(0);
ui->progressBar->show();
//getUserPassword();
//QtConcurrent::run(&this->downloadO, &downloadFile::download, this);
this->download();
this->trayIcon->showMessage(a.applicationName(), tr("Starting downloading\n") + this->downloading.path, QSystemTrayIcon::Information);
}
// Slot stopping download
void MainWindow::stoppingDownload()
{
emit (stopDownloading(this->downloading.process));
}
// when download is finished, launch download of next file in queue
void MainWindow::downloadFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString path;
QString str;
int pos;
QString aborted = tr("finished");
QMessageBox::StandardButton reply;
bool retry = false;
// test if process crashed
if (exitStatus == QProcess::CrashExit)
{
QMessageBox::warning(
NULL,
a.applicationName(),
tr("Rsync process crashed"));
}
//test result code of command (if 20 then command stopped by user)
if (exitCode != 0 and this->stopDlAsked != true)
{
if (exitCode == 20)
{
aborted = tr("stopped by user");
}else if (exitCode == 5) // password asked
{
getUserPassword(&this->downloading);
retry = true;
}
// displaying warning with exit code
reply = QMessageBox::warning(
this,
a.applicationName(),
rsyncErrorStrings[exitCode] + tr("\nDo you want to retry?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::Yes);
if (reply == QMessageBox::Yes)
{
retry = true;
}
}
this->stopDlAsked = false;
this->trayIcon->showMessage(a.applicationName(), tr("Download ") + aborted + "\n" + this->downloading.path, QSystemTrayIcon::Information);
// disconnecting signals to slots
disconnect(this->downloading.process, 0, 0, 0);
// reset variables and window, close process
this->downloading.process->close();
ui->progressBar->hide();
if (retry == false)
{
QString filename = ui->listDownload->item(0)->text();
pos = filename.contains(" => ");
filename.resize(pos);
this->settings.setValue("Downloaded/" + ui->listDownload->item(0)->text(), true);
delete ui->listDownload->takeItem(0);
}
this->downloading.clear();
// Some downloads staying in queue
if (ui->listDownload->count() != 0)
{
// initializing download
path = ui->listDownload->item(0)->text();
pos = path.lastIndexOf("/");
this->downloading.service = path.midRef(pos+1).toString();
path.resize(pos);
pos = path.lastIndexOf(" => ");
this->downloading.server = path.midRef(pos+4).toString();
path.resize(pos);
this->downloading.port = this->settings.value("connexion/server/" + this->downloading.server).toInt();
this->downloading.path = path;
//getUserPassword(true);
testServicePresence(this->downloading.service, true);
// savepath exists in settings ?
str = "Folder/" + this->downloading.server + "/" + this->downloading.service;
if (this->settings.contains(str))
{
// setting savepath from saved settings
this->downloading.savePath = this->settings.value(str).toString();
sleep(2);
startDownloading();
}else
{
// no save path
if(!on_DefaultSaveFolder_triggered())
{
cout << "Error no save path so removing download";
return;
}
}
}
if(config.autosaveCheckbox->checkState() == Qt::Checked and this->downloading.quit == false)
{
saveDownloadList();
}
}
// Slot activated when a line is clicked in queue list
void MainWindow::on_listDownload_itemClicked(QListWidgetItem *item)
{
QMessageBox::StandardButton reply;
if (item->listWidget()->row(item) == 0)
{
// first line clicked on download list
reply = QMessageBox::question(
this,
a.applicationName(),
tr("Do you want to stop downloading and delete this file from download queue ?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::Yes)
{
// stopping download
this->stopDlAsked = true;
emit (stopDownloading(this->downloading.process));
}
}else
{
// not first line on download list
reply = QMessageBox::question(
this,
a.applicationName(),
tr("Do you want to delete this file from download queue ?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::Yes)
{
// removing line from download list
ui->listDownload->removeItemWidget(item);
delete item;
}
}
if (config.autosaveCheckbox->checkState() == Qt::Checked)
{
// autosave acivated,so saving download list
saveDownloadList();
}
}
// load settings
void MainWindow::loadSettings()
{
// restoring geometry and state of window and widgets
this->restoreGeometry(this->settings.value("window/geometry").toByteArray());
this->restoreState(this->settings.value("window/state").toByteArray());
ui->treeWidget->header()->restoreState(this->settings.value("treeWidget/state").toByteArray());
ui->splitter->restoreState(this->settings.value("splitter/state").toByteArray());
ui->splitter_2->restoreState(this->settings.value("splitter2/state").toByteArray());
ui->toolBar->setToolButtonStyle((Qt::ToolButtonStyle)this->settings.value("toolbar/state").toInt());
if (this->settings.value("Autosave").toInt() == Qt::Checked)
{
this->config.autosaveCheckbox->setChecked(true);
}
// loading connexion settings
// loading servers history
this->settings.beginGroup("connexion/server");
QStringList servers = this->settings.allKeys();
this->settings.endGroup();
for( const QString &server : servers )
{
ui->khistorycombobox->addToHistory(server);
}
// loading save path
this->downloading.savePath = this->settings.value("Folder").toString();
// loading bandwidth limit
this->connexion.bandwidthLimit = this->settings.value("bandwidthlimit").toUInt();
this->connexion.bandwidthLimitUnit = this->settings.value("bandwidthlimitunit").toInt();
}
// save settings
void MainWindow::saveSettings()
{
this->settings.setValue("window/geometry", saveGeometry());
this->settings.setValue("window/state", saveState());
this->settings.setValue("treeWidget/state", ui->treeWidget->header()->saveState());
this->settings.setValue("splitter/state", ui->splitter->saveState());
this->settings.setValue("splitter2/state", ui->splitter_2->saveState());
this->settings.setValue("connexion/lastServer", this->connexion.server);
this->settings.setValue("connexion/lastPort", QString::number(this->connexion.port));
this->settings.setValue("toolbar/state", ui->toolBar->toolButtonStyle());
this->settings.setValue("Autosave", this->config.autosaveCheckbox->checkState());
this->settings.sync();
}
// About
void MainWindow::on_actionAbout_triggered()
{
aboutDialog.show();
}
// About QT
void MainWindow::on_actionAbout_Qt_triggered()
{
QMessageBox::aboutQt(this);
}
// Activated when menu "change folder" is clicked
bool MainWindow::on_DefaultSaveFolder_triggered()
{
QFileDialog dialog;
QString folder;
QString path;
// if service not selected display a message
if (this->connexion.service.isEmpty())
{
QMessageBox::warning(
NULL,
a.applicationName(),
tr("Since the save path is linked to service, you need to select a service before you can select a folder"));
return false;
}
// Asking for directory to save files
path = dialog.getExistingDirectory(this, tr("Choose folder where to save file"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!path.isEmpty())
{
this->downloading.savePath = path;
if (!this->connexion.service.isEmpty() and !this->connexion.server.isEmpty())
{
// saving save path in settings
folder = "Folder/" + this->connexion.server + "/" + this->connexion.service;
this->settings.setValue(folder, this->downloading.savePath);
this->settings.sync();
}
}else
{
return false;
}
return true;
}
// Activated when menu "settings" is clicked
void MainWindow::on_action_Settings_triggered()
{
config.UnitCombobox->setCurrentIndex(this->connexion.bandwidthLimitUnit);
config.spinBox->setValue(this->connexion.bandwidthLimit);
Configuration.show();
}
// Acivated when "Ok" is clicked in Configuration window
void MainWindow::on_buttonBox_accepted()
{
QString unit;
QString bw;
bw = config.spinBox->text();
if (bw.toInt() == 0)
{
// bandwidth = 0
this->connexion.bandwidthLimit = 0;
this->connexion.bandwidthLimitUnit = 0;
}else
{
this->connexion.bandwidthLimit = config.spinBox->value();
this->connexion.bandwidthLimitUnit = config.UnitCombobox->currentIndex();
}
this->settings.setValue("bandwidthlimit", this->connexion.bandwidthLimit);
this->settings.setValue("bandwidthlimitunit", this->connexion.bandwidthLimitUnit);
this->settings.setValue("Autosave", this->config.autosaveCheckbox->checkState());
this->settings.sync();
Configuration.hide();
}
// Saving download list
void MainWindow::saveDownloadList()
{
int nRows;
// remove list of downloads
this->settings.remove("Downloads/");
// Saving list of current downloads
nRows = ui->listDownload->count();
this->settings.beginGroup("Downloads");
this->settings.setValue("rows", nRows);
for (int i = 0; i < nRows; i++)
{
this->settings.setValue(QString::number(i), ui->listDownload->item(i)->text());
}
this->settings.endGroup();
this->settings.sync();
}
// Loading download list
void MainWindow::loadDownloadList()
{
QString path;
QString str;
int pos;
this->settings.sync();
this->settings.beginGroup("Downloads");
int size = this->settings.value("rows").toInt();
for (int i = 0; i < size; ++i)
{
ui->listDownload->addItem(this->settings.value(QString::number(i)).toString());
}
this->settings.endGroup();
path = ui->listDownload->item(0)->text();
pos = path.lastIndexOf("/");
this->downloading.service = path.midRef(pos+1).toString();
path.resize(pos);
pos = path.lastIndexOf(" => ");
this->downloading.server = path.midRef(pos+4).toString();
this->downloading.port = this->settings.value("connexion/server/" + this->downloading.server).toInt();
path.resize(pos);
this->downloading.path = path;
str = "Folder/" + this->downloading.server + "/" + this->downloading.service;
if (this->settings.contains(str))
{
this->downloading.savePath = this->settings.value(str).toString();
}
startDownloading();
}
// clear object downloading
void Connexion::clear()
{
this->path.clear();
this->server.clear();
this->savePath.clear();
this->service.clear();
this->user.clear();
this->password.clear();
this->port = 0;
this->process = nullptr;
this->quit = false;
}
// Context menu of file list clicked
void MainWindow::on_actionDownload_triggered()
{
// action made in qt-designer and added in init function.
QTreeWidgetItem *item;
item = ui->treeWidget->currentItem();
on_treeWidget_itemClicked(item, true);
}
/*void MainWindow::downloadingErrorSlot(QString errorString)
{
QMessageBox::warning(
this,
a.applicationName(),
errorString,
QMessageBox::Ok,
QMessageBox::Ok);
}
*/
// Change toolbar style
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
ui->toolBar->setToolButtonStyle((Qt::ToolButtonStyle)index);
}
void MainWindow::on_actionExit_triggered()
{
quitApp();
}
void MainWindow::setDlSpeed(QString speed)
{
speed.squeeze();
}
void MainWindow::on_actionHiddenService_triggered()
{
QInputDialog hiddenFolderDialog;
bool ok;
QString text = QInputDialog::getText(this, a.applicationName() + tr(" Request"),
tr("Hidden service name"), QLineEdit::Normal,
"test", &ok);
if (ok && !text.isEmpty())
{
this->connexion.service = text;
this->settings.setValue("Hidden/" + this->connexion.server + '/' + text, true);
ui->listWidget->addItem(text + "\n\t");
preparePopulateTree();
}
}
void MainWindow::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
{
cout << "double-clicked" << endl;
}