#include "mainwindow.h" #include //#include #include #include "ui_configuration.h" #include "version.h" #include "tools.h" #include "password.h" #include /* #include #include #include */ using namespace std; bool display = false; extern Ui::Configuration config; extern bool testRsyncReturn(QProcess *); extern QApplication a; QMap 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("

Client for rsync server

") + "" + tr("Version") + ": " + version + "
" + "" + tr("Licence") + ": " + this->about.licence + "
" + "" + tr("Author") + ": " + this->about.author + "
" + "" + tr("EMail") + ": " + this->about.email + "
" + "" + tr("Source code") + ": " + this->about.git + "
" + tr("You click on file to enqueue it, and RyncUI Download one file a time"); AboutW.TextAbout->setHtml(aboutText); // initialization = true; info(DEBUGMACRO, "Initialization of widgets"); // 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 // setting arrowcursor for treeWidget, listWidget and listDownload to arrow ui->treeWidget->setCursor(Qt::ArrowCursor); ui->listWidget->setCursor(Qt::ArrowCursor); ui->listDownload->setCursor(Qt::ArrowCursor); info(DEBUGMACRO, "Hiding progress bar"); // Hiding progress bar ui->progressBar->hide(); //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); loadSettings(); initSystemTrayIcon(); init(); // if last server exists in settings if (this->settings.contains("connexion/lastServer")) { info(DEBUGMACRO, "Setting previous server"); // set window to previous server/port configuration ui->portEdit->setText(this->settings.value("connexion/lastPort").toString()); ui->khistorycombobox->setCurrentText(this->settings.value("connexion/lastServer").toString()); }else { info(DEBUGMACRO, "No previous server, so setting only default port"); ui->portEdit->setText(QString::number(this->connexion.port)); ui->khistorycombobox->clear(); } // 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::of(&QComboBox::currentIndexChanged), this, &MainWindow::on_comboBox_currentIndexChanged); populateList(ui->khistorycombobox->currentText(), ui->portEdit->text().toUInt()); initialization = false; } 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 *deleteButton = msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); msgBox.addButton(QMessageBox::No); QPushButton *yes = msgBox.addButton(QMessageBox::Yes); msgBox.setDefaultButton(QMessageBox::Yes); msgBox.exec(); reply = msgBox.clickedButton(); info(DEBUGMACRO, "Response => " + reply->text()); // if response is yes then loading list info(DEBUGMACRO, "reply text is : " + reply->text()); if(reply == yes) { loadDownloadList(); }else if (reply == deleteButton) { // delete saved download list deleteDownloadList(); } } } void MainWindow::initSystemTrayIcon() { QMenu *trayIconMenu; QAction * quitAction; QIcon icon; info(DEBUGMACRO, "initSystemTrayIcon"); 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); info(DEBUGMACRO, "Connecting signal of trayIcon"); 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) { info(DEBUGMACRO, "on_trayIcon_clicked"); if (reason == QSystemTrayIcon::Trigger) { info(DEBUGMACRO, "trayIcon is clicked"); if (this->isHidden()) { this->show(); }else { this->hide(); } } } void MainWindow::hideWindow() { info(DEBUGMACRO, "hideWindow()\n Hiding window"); this->hide(); } void MainWindow::showWindow() { info(DEBUGMACRO, "showWindow()\n showing window"); this->show(); } MainWindow::~MainWindow() { info(DEBUGMACRO, "Exiting application"); delete ui; QCoreApplication::quit(); } void MainWindow::quitApp() { QMessageBox::StandardButton reply; QMessageBox::StandardButtons param; QString displayText; info(DEBUGMACRO, "Exiting application"); // saving settings saveSettings(); if (ui->listDownload->count() != 0) // some downloads waiting { info(DEBUGMACRO, "Asking confirmation to exit"); // 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) { info(DEBUGMACRO,"Stopping downloads"); 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) { info(DEBUGMACRO, "Saving download list"); //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; info(DEBUGMACRO, "closeEvent() => Closing window to systray"); 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 Quit in the context menu " "of the system tray entry.")); msgBox.addButton(QMessageBox::Ok); 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; info(DEBUGMACRO, "populateTree() =>Populating listview"); // 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); info(DEBUGMACRO, "Validating server"); // validating server's address this->connexion.ipversion = validateServer(this->connexion.server); if (this->connexion.ipversion != 0) { // server is validated, scanning directory path = this->connexion.service + "/"; while (this->rescan) { scanDir(&this->connexion, nullptr, path); } } // Restoring cursor QGuiApplication::restoreOverrideCursor(); } } // Populate Listview with list of services void MainWindow::populateList(QString server, uint port) { QString service; QStringList hidden; int i; bool ok = false; info(DEBUGMACRO, "populateList() => Populating list of services"); info(DEBUGMACRO, "port: " + QString::number(port) + " - server: " + server); if (server.isEmpty()) { ui->listWidget->clear(); ui->treeWidget->clear(); return; } if ((server != this->connexion.server)) { // Determine version of Ip Protocol info(DEBUGMACRO, "Server changed"); this->connexion.server = server; if (port != false) { 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)) { info(DEBUGMACRO, "Server configuration exists in settings"); // server is in history => setting port value port = this->settings.value(server).toUInt(); this->connexion.ipversion = this->settings.value("ipversion").toUInt(); ui->portEdit->setText(QString::number(port)); this->connexion.port = port; ok = true; //display list of services //listServices(); // TODO clear in listServices } this->settings.endGroup(); } if (port != this->connexion.port) { info(DEBUGMACRO, "Port changed"); this->connexion.port = port; ok = true; //listServices(); // TODO clear in listServices } if (ok == true) { if (this->connexion.ipversion == 0) { this->connexion.ipversion = validateServer(server); info(DEBUGMACRO, QString::number(this->connexion.ipversion)); } if (this->connexion.ipversion != 0) { info(DEBUGMACRO, QString::number(this->connexion.ipversion)); info(DEBUGMACRO, "server: " + server); info(DEBUGMACRO,"Saving server configuration in settings"); // storing serverURL and port in settings this->settings.setValue(server, port); this->settings.setValue(server, this->connexion.ipversion); this->settings.sync(); this->downloading.server = server; this->downloading.ipversion = this->connexion.ipversion; // storing in history of combobox ui->khistorycombobox->addToHistory(server); // load and display rsync services of the rsync server } }else { info(DEBUGMACRO, "Error server not changed"); } listServices(); 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 (testServerPresence(service, false)) { ui->listWidget->addItem(service + "\n\t"); } } } this->settings.endGroup(); // clearing listview ui->treeWidget->clear(); QGuiApplication::restoreOverrideCursor(); //setting cursor to default } // Test if service is already present on the server bool MainWindow::testServerPresence(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; info(DEBUGMACRO, "testServerPresence() => Test of rsync server"); info(DEBUGMACRO, "ipversion = " + QString::number(this->connexion.ipversion )); cmd = "/usr/bin/rsync"; if (this->connexion.ipversion == 4 || this->connexion.ipversion ==6) { param << "-" + QString::number(this->connexion.ipversion); } 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"); 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; QString server; QProcess *myProcess; bool flag = false; info(DEBUGMACRO, "listServices() => Listing services offered by server"); // clearing listwidget ui->listWidget->clear(); cmd = "/usr/bin/rsync"; if (this->connexion.ipversion == 0) { this->connexion.ipversion = whatIpVersion(this->connexion.server); } if (this->connexion.ipversion == 4) { param << "-" + QString::number(this->connexion.ipversion).trimmed(); server = this->connexion.server.trimmed(); }else if (this->connexion.ipversion == 6) { param << "-" + QString::number(this->connexion.ipversion).trimmed(); server = "[" + this->connexion.server + "]"; } param << "--contimeout=20" << "--port=" + QString::number(this->connexion.port) << server + "::"; myProcess = new QProcess(this); info(DEBUGMACRO, cmd + " " + param.join(" ")); myProcess->start(cmd, param); // waiting for response of the server with a timeout of 10 seconds while(myProcess->waitForReadyRead(10000)) { info(DEBUGMACRO, "Reading"); while(!flag) { line = QString::fromUtf8(myProcess->readLine()); // line empty then buffer is empty so returning to wait new datas info(DEBUGMACRO, "line: #" + line + "#"); if (line.isEmpty()) { info(DEBUGMACRO, "line is empty"); flag = true; break; } info(DEBUGMACRO, "extracting services informations"); // 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]; info(DEBUGMACRO, "service is:" + service); info(DEBUGMACRO, "adding to list widget of services"); ui->listWidget->addItem(service); } // buffer empty go to waiting new datas flag = false; } // verifying error code testRsyncReturn(this, myProcess); myProcess->close(); info(DEBUGMACRO, "listServices finished"); } // connect to rsync server to get list of files bool MainWindow::scanDir(Connexion * connexion, 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; QString server = connexion->server; info(DEBUGMACRO, "scandir() => connect to rsync server to get list of files"); myProcess = new QProcess(this); myProcess->setProcessChannelMode(QProcess::MergedChannels); if (parent != nullptr) { nChild = parent->childCount(); } if (nChild == 0) { if (!connexion->user.isEmpty()) { server.prepend(connexion->user + "@"); env.insert("RSYNC_PASSWORD", connexion->password); // Add an environment variable }else { server.prepend("anonymous@"); env.insert("RSYNC_PASSWORD", "anonymous"); // Add an environment variable } myProcess->setProcessEnvironment(env); info(DEBUGMACRO, "ipversion => " + QString::number(connexion->ipversion)); cmd = "rsync"; if(this->connexion.ipversion == 4 || this->connexion.ipversion == 6) { param << "-" + QString::number(connexion->ipversion).trimmed(); } param << "--contimeout=20" << "--port=" + QString::number(connexion->port) << "[" + server + "]::" + path; info(DEBUGMACRO, cmd + " " + param.join(" ")); myProcess->start(cmd, param); this->rescan = true; info(DEBUGMACRO, "Waiting server response"); // 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(connexion); if (looping <= 1) { this->rescan = true; looping++; }else { this->rescan = false; looping = 0; warning(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 !connexion->password.isEmpty()) { this->settings.setValue("Passwords/" + connexion->server + "/" + connexion->service + "/" + connexion->user, true); setPassword(connexion->user, connexion->password); this->settings.sync(); } this->rescan = false; } } flag = false; }else { if (myProcess->state() == QProcess::Running) { if (myProcess->waitForFinished(10000) == 0) { warning(tr("The processus does'nt respond: ") + myProcess->errorString()); } } } }while(readOk); // buffer empty go to waiting new datas testRsyncReturn(this, myProcess); myProcess->close(); } return false; } // validate address server uint MainWindow::validateServer(QString server) { QString cmd; QStringList param; QString line; QProcess * myProcess; QStringList responseList; uint ipversion; int answerFound = 0; int queryPos = 0; info(DEBUGMACRO, "ValidateServer() => Validating server address"); ipversion = whatIpVersion(server); if (ipversion == 0) // not an Ip address, perhaps a server name { info(DEBUGMACRO, "Digging server name"); cmd = "dig"; param << "-t A"; // IP V4 query param << "-t AAAA"; // IP V6 query param << server.trimmed() ; info(DEBUGMACRO, cmd + " " + param.join(" ")); myProcess = new QProcess(this); myProcess->start(cmd, param); // making a dig on the server's address while(myProcess->waitForReadyRead()) { while (1) //!bflag) { line = QString::fromUtf8(myProcess->readAllStandardOutput()); info(DEBUGMACRO, line); // line empty then buffer is empty so returning to wait new datas if (line.isEmpty()) { break; }else { answerFound = line.indexOf(";; ANSWER SECTION:") +19; info(DEBUGMACRO, "Position of answer line is : " + QString::number(answerFound)); if (answerFound != -1) { info(DEBUGMACRO, "Serching IP address"); queryPos = line.indexOf(";; Query"); info(DEBUGMACRO, "Position of Query line is : " + QString::number(queryPos)); if (answerFound < queryPos) { line = line.mid(answerFound, queryPos - answerFound); info(DEBUGMACRO, "line is:\n" + line); responseList = line.split(QRegExp("\\s+")); info(DEBUGMACRO, "ip Address is => " + responseList.at(4)); if(responseList.at(3) == "A") { return 4; }else if(responseList.at(3) == "AAAA") { return 6; } } } } } } myProcess->close(); } info(DEBUGMACRO, "Returning ip version: " + QString::number(ipversion)); return ipversion; return 0; } // slot activated when button connection is clicked void MainWindow::on_connectButton_clicked() { QString server; uint port; info(DEBUGMACRO, "on_connectButton_clicked() => Connexion button clicked"); port = ui->portEdit->text().toUInt(); if (port > 0 and port < 65535) { server = ui->khistorycombobox->currentText(); populateList(server, port); }else { error(tr("Bad port number, correct it")); ui->portEdit->clear(); ui->portEdit->setFocus(); } } // 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; info(DEBUGMACRO, "addTreeItem() => adding item to treeview"); 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 { // item is a file 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) { parent->addChild(treeItem); } } // Slot activated when a service in the list is clicked void MainWindow::on_listWidget_clicked() { info(DEBUGMACRO, "on_listWidget_clicked() => Slot activated when a service in the list is clicked"); this->connexion.service = ui->listWidget->currentItem()->text().section("\n", 0 ,0); ui->treeWidget->clear(); preparePopulateTree(); } void MainWindow::preparePopulateTree() { QString str; QStringList logins; info(DEBUGMACRO, "preparePopulateTree()"); 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(); } 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; info(DEBUGMACRO, "getUserPassword() => get password and user login"); 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) { QTreeWidgetItem * itemR; QString path; QString str; QMessageBox::StandardButton reply; int sizeFromRsync; info(DEBUGMACRO, "on_treeWidget_itemClicked() => Slot activated when a file is clicked in the treeview"); if (this->treeviewClicked == true) { return; } this->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() <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)) { info(DEBUGMACRO, "saving path do not exists, asking for it"); // saving path do not exists, asking for it if(!on_DefaultSaveFolder_triggered()) { info(DEBUGMACRO, "no directory selectioned, ignoring download request"); this->treeviewClicked = false; return; } }else { this->downloading.savePath = this->settings.value(str).toString(); info(DEBUGMACRO, "saving folder" + this->downloading.savePath); } // Item is a file // searching if file exists in savepath info(DEBUGMACRO, "searching if file exists in savepath"); if (QFile::exists(this->downloading.savePath + "/" + path)) { info(DEBUGMACRO, "file exists in savepath"); QFileInfo fileinfo(this->downloading.savePath + "/" + path); if (fileinfo.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) { this->treeviewClicked = false; return; }else if(reply == QMessageBox::No) { QFile::remove(this->downloading.savePath + "/" + path); this->treeviewClicked = false; 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) { this->treeviewClicked = false; return; }else { QFile::remove(this->downloading.savePath + "/" + path); } } } if(ui->listDownload->findItems(path, Qt::MatchStartsWith).empty()) { // is there a downloading process ? info(DEBUGMACRO, "is there a downloading process ?"); if (this->downloading.process == nullptr) { // no downloading process launching it info(DEBUGMACRO, "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); } info(DEBUGMACRO, "Downloading process exist, adding download to download list"); // Adding download to download list str = path + " => " + this->connexion.server + "/" + this->connexion.service; ui->listDownload->addItem(str); }else { warning(tr("File is already downloading" )); } }else { //Item is a Directory info(DEBUGMACRO, "Item is a Directory"); if (item->isExpanded() == false) { info(DEBUGMACRO, "Item is not expanded"); while (this->rescan) { info(DEBUGMACRO, "Re-scanning path: " + this->connexion.service + "/" + path +"/"); scanDir(&this->connexion, item, this->connexion.service + "/" + path +"/"); item->setExpanded(true); } } } if (config.autosaveCheckbox->checkState() == Qt::Checked) { info(DEBUGMACRO, "Saving download list"); saveDownloadList(); } info(DEBUGMACRO, "Exiting"); this->treeviewClicked = false; } // Launch the thread which download the file void MainWindow::startDownloading() { info(DEBUGMACRO, "startDownloading() => Launch the thread which download the file"); 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() { info(DEBUGMACRO, "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; info(DEBUGMACRO, "downloadFinished() => when download is finished, launch download of next file in queue"); info(DEBUGMACRO, "Download finished with status " + QString::number(exitCode)); // test if process crashed if (exitStatus == QProcess::CrashExit) { warning(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) { if (this->exiting) { return; } 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(), tr(rsyncErrorStrings[exitCode].toStdString().c_str()) + 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); 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); testServerPresence(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; info(DEBUGMACRO, "on_listDownload_itemClicked() => Slot activated when a line is clicked in queue list"); 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() { info(DEBUGMACRO, "loadSettings() => restoring geometry and state of window and widgets"); // 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 info(DEBUGMACRO, "Restoring server combobox history"); this->settings.beginGroup("connexion/server"); QStringList servers = this->settings.allKeys(); this->settings.endGroup(); ui->khistorycombobox->insertItems(1,this->settings.value("kHistoryComboBox").toStringList()); // 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() { info(DEBUGMACRO, "saveSettings() => Saving settings"); 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("connexion/lastIpversion", QString::number(this->connexion.ipversion)); this->settings.setValue("toolbar/state", ui->toolBar->toolButtonStyle()); this->settings.setValue("Autosave", this->config.autosaveCheckbox->checkState()); this->settings.setValue("kHistoryComboBox", ui->khistorycombobox->historyItems()); this->settings.sync(); } // About void MainWindow::on_actionAbout_triggered() { info(DEBUGMACRO, "on_actionAbout_triggered() => display about informations"); aboutDialog.show(); } // About QT void MainWindow::on_actionAbout_Qt_triggered() { info(DEBUGMACRO, "on_actionAbout_Qt_triggered() => display QT about information"); QMessageBox::aboutQt(this); } // Activated when menu "change folder" is clicked bool MainWindow::on_DefaultSaveFolder_triggered() { QFileDialog dialog; QString folder; QString path; QString dir; info(DEBUGMACRO, "on_DefaultSaveFolder_triggered() => Activated when menu 'change folder' is clicked"); // if service not selected display a message if (this->connexion.service.isEmpty() or this->connexion.server.isEmpty()) { warning(tr("You need to select a service before you can select a folder")); return false; } // Asking for directory to save files folder = "Folder/" + this->connexion.server + "/" + this->connexion.service; path = this->settings.value(folder).toString(); info(DEBUGMACRO, "Path = " + path); dir = dialog.getExistingDirectory(this, tr("Choose folder where to save file"), path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!dir.isEmpty()) { this->downloading.savePath = dir; // saving save path in settings 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() { info(DEBUGMACRO, "on_action_Settings_triggered() => Activated when menu 'settings' is clicked"); 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; info(DEBUGMACRO, "on_buttonBox_accepted() => Acivated when 'Ok' is clicked in Configuration window"); 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; info(DEBUGMACRO, "saveDownloadList() => Saving download list"); // 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; info(DEBUGMACRO, "loadDownloadList() => loading download list"); 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(); } // Deleting download list void MainWindow::deleteDownloadList() { info(DEBUGMACRO, "deleteDownloadList() => Deleting existing download list"); this->settings.remove("Downloads"); } // clear object downloading void Connexion::clear() { info(DEBUGMACRO, "clear() => clear object 'downloading'"); 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); } // Change toolbar style void MainWindow::on_comboBox_currentIndexChanged(int index) { info(DEBUGMACRO, "on_comboBox_currentIndexChanged()"); ui->toolBar->setToolButtonStyle((Qt::ToolButtonStyle)index); } void MainWindow::on_actionExit_triggered() { info(DEBUGMACRO, "on_actionExit_triggered() => slot activated when button exit clicked"); this->exiting = true; quitApp(); } void MainWindow::setDlSpeed(QString speed) { info(DEBUGMACRO, "setDlSpeed(= => setting download speed"); speed.squeeze(); } void MainWindow::on_actionHiddenService_triggered() { QInputDialog hiddenFolderDialog; info(DEBUGMACRO, "on_actionHiddenService_triggered() => activated when the 'hidden service' button"); 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(); } }