├── .gitignore ├── LICENSE ├── README.md ├── about.cpp ├── about.h ├── converter.cpp ├── converter.h ├── download.cpp ├── download.h ├── ext ├── ccrypt │ ├── ccryptlib.c │ ├── ccryptlib.h │ ├── patch │ │ └── ccrypt.patch │ ├── platform.c │ ├── platform.h │ ├── rijndael.c │ ├── rijndael.h │ ├── tables.c │ └── tables.h ├── qaes │ ├── qaesencryption.cpp │ └── qaesencryption.h ├── qarchive │ ├── qarchive.cpp │ └── qarchive.h └── qsshsocket │ ├── patch │ └── qsshsocket.patch │ ├── qsshsocket.cpp │ └── qsshsocket.h ├── history.cpp ├── history.h ├── installer.cpp ├── installer.h ├── lng ├── rrcc_de.qm └── rrcc_de.ts ├── logger.cpp ├── logger.h ├── mainwindow.cpp ├── mainwindow.h ├── onlineupd.cpp ├── onlineupd.h ├── packager.cpp ├── packager.h ├── res ├── ico │ ├── app.icns │ ├── app.ico │ └── app.png ├── png │ ├── big_charger.png │ ├── big_pause.png │ ├── big_speak.png │ ├── big_spot.png │ ├── big_start.png │ ├── big_stop.png │ ├── brush-m.png │ ├── brush-s.png │ ├── canceled.png │ ├── clipboard.png │ ├── clock.png │ ├── communication.png │ ├── compass.png │ ├── complete.png │ ├── compress.png │ ├── converter.png │ ├── counter.png │ ├── decompress.png │ ├── dock.png │ ├── exit.png │ ├── fan_1.png │ ├── fan_2.png │ ├── fan_3.png │ ├── fan_4.png │ ├── filter.png │ ├── flag.png │ ├── flip.png │ ├── fw_upd.png │ ├── gift-48.png │ ├── gift-64.png │ ├── gift.png │ ├── goto.png │ ├── help.png │ ├── history.png │ ├── home-48.png │ ├── home-64.png │ ├── info.png │ ├── internet.png │ ├── key.png │ ├── logger.png │ ├── mail-48.png │ ├── mail-64.png │ ├── map.png │ ├── misc.png │ ├── msgid.png │ ├── mute.png │ ├── network.png │ ├── open.png │ ├── padlock.png │ ├── pin.png │ ├── plugin.png │ ├── question.png │ ├── refresh.png │ ├── reset.png │ ├── robo.png │ ├── rotate.png │ ├── safe.png │ ├── save.png │ ├── search.png │ ├── sensor.png │ ├── setup.png │ ├── sound.png │ ├── ssh.png │ ├── ssid.png │ ├── start.png │ ├── stop.png │ ├── swap.png │ ├── timer.png │ ├── upd_lin.png │ ├── upd_mac.png │ ├── upd_win.png │ ├── update.png │ ├── user.png │ ├── voice.png │ ├── volume.png │ ├── warning.png │ ├── wifi.png │ ├── zone.png │ └── zoom.png ├── rrcc.qrc └── ui │ ├── about.ui │ ├── converter.ui │ ├── download.ui │ ├── history.ui │ ├── installer.ui │ ├── logger.ui │ ├── mainwindow.ui │ ├── onlineupd.ui │ ├── packager.ui │ ├── search.ui │ ├── setup.ui │ ├── timer.ui │ ├── uninstaller.ui │ ├── unpackager.ui │ ├── update.ui │ ├── upload.ui │ └── zones.ui ├── rrcc.pro ├── search.cpp ├── search.h ├── setup.cpp ├── setup.h ├── timer.cpp ├── timer.h ├── uninstaller.cpp ├── uninstaller.h ├── unpackager.cpp ├── unpackager.h ├── update.cpp ├── update.h ├── upload.cpp ├── upload.h ├── zones.cpp └── zones.h /.gitignore: -------------------------------------------------------------------------------- 1 | rrcc.pro.user* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **RoboRock Control Center** 2 | [![](https://img.shields.io/github/license/lazyt/rrcc.svg?color=blue)](https://github.com/LazyT/rrcc/blob/master/LICENSE) 3 | [![](https://img.shields.io/badge/platform-win%20%7C%20lin%20%7C%20mac-blue.svg)](https://github.com/LazyT/rrcc#download) 4 | [![](https://img.shields.io/badge/paypal-buy%20me%20a%20beer-red.svg)](https://paypal.me/LazyT) 5 | [![](https://img.shields.io/github/downloads/lazyt/rrcc/total.svg?color=orange)](https://github.com/LazyT/rrcc/releases) 6 | [![](https://img.shields.io/github/last-commit/lazyt/rrcc/master.svg?color=yellow)](https://github.com/LazyT/rrcc/commits/master) 7 | [![](https://img.shields.io/github/release-date/lazyt/rrcc.svg?color=brightgreen)](https://github.com/LazyT/rrcc/releases/latest) 8 | [![](https://img.shields.io/github/release/lazyt/rrcc.svg?color=brightgreen)](https://github.com/LazyT/rrcc/releases/latest) 9 | 10 | ## Description 11 | 12 | Xiaomi vacuum cleaners are really nice, but need a cloud connection to use all features. You don't like your data in chinese hands? Then try RRCC for free and use it on Windows, Linux and MacOS! 13 | 14 | The current version supports the following features: 15 | 16 | * control vacuum (start, pause, stop, spot cleaning, return dock, find) 17 | * set fanspeed 18 | * reset consumables 19 | * add, change and delete timers 20 | * show cleaning history 21 | * set sound volume 22 | * decrypt, encrypt and install voice packages 23 | * search, download and update firmware 24 | * control dnd and carpet modes 25 | * set wifi ssid and key 26 | * convert 96->32 byte token 27 | * multi language (English and German) 28 | * cross platform (same look & feel on Windows, Linux, MacOS) 29 | 30 | Only on rooted devices: 31 | 32 | * extract token via ssh 33 | * install/update/uninstall [Valetudo](https://github.com/hypfer/valetudo) 34 | * map functions via [Valetudo](https://github.com/hypfer/valetudo) (goto, zone cleaning, virtual walls and nogo zones, flip, rotate, zoom) 35 | * show installed firmware and valetudo version 36 | 37 | ## Screenshots 38 | 39 | ![main window](https://raw.github.com/LazyT/rrcc/gh-pages/screenshots/mainwindow.png) 40 | 41 | ## Download 42 | 43 | Download the latest version for your operating system. All 3 files (exe, dmg, AppImage) contain the binary, translations and required libraries to run RRCC on the target platform without installing anything. 44 | 45 | * [Windows (exe)](https://github.com/LazyT/rrcc/releases) 46 | 47 | This is an [7zip](https://www.7-zip.org) self extracting archive. It will be automatically extracted to "%temp%\7zxxxxxxxx" and after that the "rrcc.exe" is started. You can copy this directory or extract the file with 7z if you want the content. 48 | 49 | Note: some virus scanners classify RRCC as malicious. This is due to the use of [7zsfx](https://www.7-zip.org/a/lzma1900.7z). If you don't trust me unzip the exe and scan that directory, no warnings should appear now. 50 | 51 | * [MacOS (dmg)](https://github.com/LazyT/rrcc/releases) 52 | 53 | This is an Apple disc image. You can mount and run or copy the application. 54 | 55 | * [Linux (AppImage)](https://github.com/LazyT/rrcc/releases) 56 | 57 | This is an [AppImage](https://appimage.org) package. Don't forget to "chmod +x *.AppImage" after download and then run it. You can use the parameter "--appimage-extract" if you want the content. 58 | 59 | For better system integration it's recommended to install [AppImageD](https://github.com/AppImage/appimaged) or [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher). 60 | 61 | ## Build from Source 62 | 63 | If the release binaries doesn't work for you build it yourself: 64 | 65 | 1) checkout the source code 66 | 67 | git clone https://github.com/LazyT/rrcc 68 | 69 | 2) change into the new rrcc directory and generate the Makefile 70 | 71 | cd rrcc && qmake 72 | 73 | 3) compile the source code 74 | 75 | make 76 | 77 | GUI fans just install the [Qt-Environment](http://www.qt.io/download-open-source), open the "rrcc.pro" project file and click on the build button. 78 | 79 | ***Hints for compiling:*** 80 | 81 | - Qt 5.11 or greater required 82 | - libarchive-dev and libssh-dev required 83 | 84 | ## Contact 85 | 86 | You can reach me at [Roboter-Forum](https://www.roboter-forum.com/index.php?thread/30309-roborock-control-center-desktop-app-f%C3%BCr-win-lin-osx). 87 | 88 | ## Credits 89 | 90 | RRCC is based on 91 | 92 | * [Qt](http://www.qt.io) 93 | * [QAES](https://github.com/bricke/Qt-AES) 94 | * [QArchive](https://github.com/antony-jr/QArchive) 95 | * [QSshSocket](https://github.com/mikemvk/QSshSocket) 96 | * [ccrypt](http://ccrypt.sourceforge.net) 97 | * [libarchive](https://www.libarchive.org) 98 | * [libssh](https://www.libssh.org) 99 | * [openssl](https://www.openssl.org) 100 | * [zlib](https://zlib.net) 101 | 102 | Thanks for this great software! 103 | -------------------------------------------------------------------------------- /about.cpp: -------------------------------------------------------------------------------- 1 | #include "about.h" 2 | 3 | aboutDialog::aboutDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | label_Version->setText(tr("Version %1 - %2").arg(APPVERS, APPDATE)); 11 | 12 | textEdit->setAlignment(Qt::AlignCenter); 13 | textEdit->textCursor().insertText(tr("This program is Freeware and may be installed and used free of charge for non-commercial use on as many computers as you like without limitations.\n\nA liability for any damages resulting from the use is excluded. Use at your own risk!")); 14 | textEdit->moveCursor(QTextCursor::Start); 15 | textEdit->setFixedHeight(5 * textEdit->fontMetrics().height() + static_cast(textEdit->document()->documentMargin()) + 2); 16 | } 17 | 18 | void aboutDialog::mouseReleaseEvent(QMouseEvent *me) 19 | { 20 | QWidget *child = QWidget::childAt(me->pos()); 21 | 22 | if(child) 23 | { 24 | QString name = child->objectName(); 25 | 26 | if(name == "label_Mail") 27 | { 28 | QDesktopServices::openUrl(QUrl(QByteArray::fromBase64("bWFpbHRvOkxhenlUQG1haWxib3gub3JnP3N1YmplY3Q9UlJDQyZib2R5PVdyaXRlIGluIEVuZ2xpc2ggb3IgR2VybWFuIHBsZWFzZS4uLg=="))); 29 | } 30 | else if(name == "label_Forum") 31 | { 32 | QDesktopServices::openUrl(QUrl(QByteArray::fromBase64("aHR0cHM6Ly93d3cucm9ib3Rlci1mb3J1bS5jb20vaW5kZXgucGhwP3RocmVhZC8zMDMwOS1yb2Jvcm9jay1jb250cm9sLWNlbnRlci1kZXNrdG9wLWFwcC1mJUMzJUJDci13aW4tbGluLW9zeA=="))); 33 | } 34 | else if(name == "label_Donation") 35 | { 36 | if(QMessageBox::warning(this, APPNAME, tr("Please note the following points:\n\n* The payment is made voluntarily without the acquisition of claims.\n* You receive no rights to the offered software.\n* Because this is not a donation in juridical sense no certificate can be issued.\n\nWould you like to support the further development of this project nevertheless?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 37 | { 38 | QDesktopServices::openUrl(QUrl(QByteArray::fromBase64("aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X2RvbmF0aW9ucyZidXNpbmVzcz1MYXp5VEBtYWlsYm94Lm9yZyZpdGVtX25hbWU9Um9ib1JvY2srQ29udHJvbCtDZW50ZXImYW1vdW50PTAmY3VycmVuY3lfY29kZT1FVVI="))); 39 | } 40 | } 41 | } 42 | } 43 | 44 | void aboutDialog::on_label_qsshsocket_linkActivated(QString link) 45 | { 46 | QDesktopServices::openUrl(link); 47 | } 48 | 49 | void aboutDialog::on_label_qaes_linkActivated(QString link) 50 | { 51 | QDesktopServices::openUrl(link); 52 | } 53 | 54 | void aboutDialog::on_label_qarchive_linkActivated(QString link) 55 | { 56 | QDesktopServices::openUrl(link); 57 | } 58 | 59 | void aboutDialog::on_label_ccrypt_linkActivated(QString link) 60 | { 61 | QDesktopServices::openUrl(link); 62 | } 63 | 64 | void aboutDialog::on_label_libarchive_linkActivated(QString link) 65 | { 66 | QDesktopServices::openUrl(link); 67 | } 68 | 69 | void aboutDialog::on_label_zlib_linkActivated(QString link) 70 | { 71 | QDesktopServices::openUrl(link); 72 | } 73 | 74 | void aboutDialog::on_label_libssh_linkActivated(QString link) 75 | { 76 | QDesktopServices::openUrl(link); 77 | } 78 | 79 | void aboutDialog::on_label_openssl_linkActivated(QString link) 80 | { 81 | QDesktopServices::openUrl(link); 82 | } 83 | 84 | void aboutDialog::on_label_qt_linkActivated(QString link) 85 | { 86 | QDesktopServices::openUrl(link); 87 | } 88 | -------------------------------------------------------------------------------- /about.h: -------------------------------------------------------------------------------- 1 | #ifndef ABOUTDLG_H 2 | #define ABOUTDLG_H 3 | 4 | #include "ui_about.h" 5 | #include "mainwindow.h" 6 | 7 | class aboutDialog : public QDialog, private Ui::Dialog_About 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | aboutDialog(QWidget*); 14 | 15 | private slots: 16 | 17 | void on_label_qsshsocket_linkActivated(QString); 18 | void on_label_qaes_linkActivated(QString); 19 | void on_label_qarchive_linkActivated(QString); 20 | void on_label_ccrypt_linkActivated(QString); 21 | void on_label_libarchive_linkActivated(QString); 22 | void on_label_zlib_linkActivated(QString); 23 | void on_label_libssh_linkActivated(QString); 24 | void on_label_openssl_linkActivated(QString); 25 | void on_label_qt_linkActivated(QString); 26 | 27 | void mouseReleaseEvent(QMouseEvent*); 28 | }; 29 | 30 | #endif // ABOUTDLG_H 31 | -------------------------------------------------------------------------------- /converter.cpp: -------------------------------------------------------------------------------- 1 | #include "converter.h" 2 | 3 | converterDialog::converterDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | lineEdit_96->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F]{96}"))); 11 | } 12 | 13 | void converterDialog::on_lineEdit_96_textChanged(QString text) 14 | { 15 | lineEdit_96->setText(lineEdit_96->text().toUpper()); 16 | 17 | lineEdit_32->setText(QAESEncryption::Decrypt(QAESEncryption::AES_128, QAESEncryption::ECB, QByteArray::fromHex(text.left(64).toUtf8()), QByteArray::fromHex("00000000000000000000000000000000")).toUpper()); 18 | } 19 | 20 | void converterDialog::on_lineEdit_16_textChanged(QString text) 21 | { 22 | lineEdit_32->setText(text.toUtf8().toHex().toUpper()); 23 | } 24 | 25 | void converterDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 26 | { 27 | close(); 28 | } 29 | -------------------------------------------------------------------------------- /converter.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVERTERDLG_H 2 | #define CONVERTERDLG_H 3 | 4 | #include "ui_converter.h" 5 | #include "mainwindow.h" 6 | 7 | class converterDialog : public QDialog, private Ui::Dialog_Converter 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | converterDialog(QWidget*); 14 | 15 | private slots: 16 | 17 | void on_lineEdit_96_textChanged(QString); 18 | void on_lineEdit_16_textChanged(QString); 19 | 20 | void on_buttonBox_clicked(QAbstractButton*); 21 | }; 22 | 23 | #endif // CONVERTERDLG_H 24 | -------------------------------------------------------------------------------- /download.cpp: -------------------------------------------------------------------------------- 1 | #include "download.h" 2 | 3 | downloadDialog::downloadDialog(QWidget *parent, int model, int server, QString firmware) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | buttonBox->button(QDialogButtonBox::Save)->setText(tr("Download")); 11 | 12 | comboBox_model->setCurrentIndex(model); 13 | comboBox_server->setCurrentIndex(server); 14 | 15 | if(!firmware.isEmpty()) 16 | { 17 | lineEdit->setText(firmware); 18 | 19 | QTimer::singleShot(1, this, SLOT(startDownload())); 20 | } 21 | else 22 | { 23 | lineEdit->setFocus(); 24 | lineEdit->setSelection(6, 4); 25 | } 26 | 27 | lineEdit->setMinimumWidth(lineEdit->fontMetrics().boundingRect(lineEdit->text()).width() + 15); 28 | } 29 | 30 | void downloadDialog::startDownload() 31 | { 32 | emit on_buttonBox_clicked(buttonBox->button(QDialogButtonBox::Save)); 33 | } 34 | 35 | bool downloadDialog::Download(QString url) 36 | { 37 | QNetworkAccessManager *netmgr = new QNetworkAccessManager(this); 38 | QNetworkRequest request = QNetworkRequest(QUrl(url)); 39 | 40 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); 41 | 42 | finish = false; 43 | 44 | reply = netmgr->get(request); 45 | 46 | reply->ignoreSslErrors(); 47 | 48 | connect(netmgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*))); 49 | connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); 50 | 51 | while(!finish) 52 | { 53 | QCoreApplication::processEvents(); 54 | } 55 | 56 | return failed; 57 | } 58 | 59 | void downloadDialog::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) 60 | { 61 | if(bytesTotal) 62 | { 63 | progressBar->setFormat(QString("%p% [%1 / %2]").arg(bytesReceived).arg(bytesTotal)); 64 | progressBar->setValue(static_cast((bytesReceived * 100) / bytesTotal)); 65 | } 66 | } 67 | 68 | void downloadDialog::finished(QNetworkReply *reply) 69 | { 70 | finish = true; 71 | 72 | if(reply->error()) 73 | { 74 | failed = true; 75 | 76 | buttonBox->button(QDialogButtonBox::Save)->setDisabled(false); 77 | 78 | progressBar->setFormat(QString("%p%")); 79 | progressBar->setValue(0); 80 | 81 | QMessageBox::warning(this, APPNAME, tr("Downloading firmware failed!\n\n%1").arg(reply->errorString())); 82 | 83 | return; 84 | } 85 | 86 | download = reply->readAll(); 87 | 88 | failed = false; 89 | } 90 | 91 | void downloadDialog::on_comboBox_model_currentIndexChanged(int index) 92 | { 93 | lineEdit->setText(fw.names[index]); 94 | 95 | lineEdit->setFocus(); 96 | lineEdit->setSelection(index == 2 ? 3 : 6, 4); 97 | } 98 | 99 | void downloadDialog::reject() 100 | { 101 | if(reply && reply->isRunning()) 102 | { 103 | if(QMessageBox::question(this, APPNAME, tr("Really abort download?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) 104 | { 105 | reply->abort(); 106 | } 107 | 108 | return; 109 | } 110 | 111 | accept(); 112 | } 113 | 114 | void downloadDialog::on_buttonBox_clicked(QAbstractButton *button) 115 | { 116 | if(buttonBox->standardButton(button) == QDialogButtonBox::Save) 117 | { 118 | if(lineEdit->text() == FW_NAME_G1 || lineEdit->text() == FW_NAME_G2 || lineEdit->text() == FW_NAME_G3) 119 | { 120 | QMessageBox::warning(this, APPNAME, tr("Enter valid firmware name!")); 121 | 122 | return; 123 | } 124 | 125 | QFile firmware(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QString("/%1").arg(lineEdit->text())); 126 | 127 | if(firmware.open(QIODevice::WriteOnly)) 128 | { 129 | button->setDisabled(true); 130 | 131 | if(!Download(QString("http://%1/%2/%3").arg(comboBox_server->currentText()).arg(fw.dirs[comboBox_model->currentIndex()]).arg(lineEdit->text()))) 132 | { 133 | firmware.write(download); 134 | 135 | hide(); 136 | 137 | QMessageBox::information(this, APPNAME, tr("Downloading firmware successfull.")); 138 | 139 | close(); 140 | } 141 | else 142 | { 143 | firmware.remove(); 144 | } 145 | 146 | firmware.close(); 147 | } 148 | else 149 | { 150 | QMessageBox::warning(this, APPNAME, tr("Could not open firmware file!\n\n%1 : %2").arg(firmware.fileName()).arg(firmware.errorString())); 151 | } 152 | } 153 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Cancel) 154 | { 155 | close(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /download.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNLOADDLG_H 2 | #define DOWNLOADDLG_H 3 | 4 | #include "ui_download.h" 5 | #include "mainwindow.h" 6 | 7 | class downloadDialog : public QDialog, private Ui::Dialog_Download 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | downloadDialog(QWidget*, int, int, QString); 14 | 15 | private: 16 | 17 | struct 18 | { 19 | QString names[3] = { FW_NAME_G1, FW_NAME_G2, FW_NAME_G3 }; 20 | QString dirs[3] = { FW_DIR_G1, FW_DIR_G2, FW_DIR_G3 }; 21 | int versions[3] = { FW_VER_G1, FW_VER_G2, FW_VER_G3 }; 22 | 23 | }fw; 24 | 25 | bool Download(QString); 26 | 27 | QNetworkReply *reply = nullptr; 28 | QByteArray download; 29 | 30 | bool finish, failed; 31 | 32 | private slots: 33 | 34 | void startDownload(); 35 | 36 | void downloadProgress(qint64, qint64); 37 | void finished(QNetworkReply*); 38 | 39 | void on_comboBox_model_currentIndexChanged(int); 40 | void reject(); 41 | void on_buttonBox_clicked(QAbstractButton*); 42 | }; 43 | 44 | #endif // DOWNLOADDLG_H 45 | -------------------------------------------------------------------------------- /ext/ccrypt/ccryptlib.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2000-2018 Peter Selinger. 2 | This file is part of ccrypt. It is free software and it is covered 3 | by the GNU general public license. See the file COPYING for details. */ 4 | 5 | /* ccryptlib.h: library for encrypting/decrypting a character stream */ 6 | 7 | #ifndef _CCRYPTLIB_H 8 | #define _CCRYPTLIB_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | struct ccrypt_stream_s { 15 | char *next_in; /* next input byte */ 16 | unsigned int avail_in; /* number of bytes available at next_in */ 17 | 18 | char *next_out; /* next output byte should be put there */ 19 | unsigned int avail_out; /* remaining free space at next_out */ 20 | 21 | void *state; /* internal state, not visible by applications */ 22 | }; 23 | typedef struct ccrypt_stream_s ccrypt_stream_t; 24 | 25 | /* 26 | The application may update next_in and avail_in when avail_in has 27 | dropped to zero. It must update next_out and avail_out when 28 | avail_out has dropped to zero. All other fields are set by the 29 | compression library and must not be updated by the application. 30 | */ 31 | 32 | int ccencrypt_init(ccrypt_stream_t *b, const char *key); 33 | int ccencrypt (ccrypt_stream_t *b); 34 | int ccencrypt_end (ccrypt_stream_t *b); 35 | 36 | int ccdecrypt_init(ccrypt_stream_t *b, const char *key, int flags); 37 | int ccdecrypt (ccrypt_stream_t *b); 38 | int ccdecrypt_end (ccrypt_stream_t *b); 39 | 40 | /* The interface for encryption and decryption is the same. The 41 | application must first call the respective init function to 42 | initialize the internal state. Then it calls the encrypt/decrypt 43 | function repeatedly, as follows: next_in and next_out must point to 44 | valid, non-overlapping regions of memory of size at least avail_in 45 | and avail_out, respectively. Avail_out must be non-zero. Avail_in 46 | may be zero to retrieve some pending output when no input is 47 | available, for instance, in an interactive application or at the 48 | end of stream. Otherwise, avail_in should be non-zero. 49 | 50 | The encryption/decryption function will read and process as many 51 | input bytes as possible as long as there is room in the output 52 | buffer. It will update next_in, avail_in, next_out, and avail_out 53 | accordingly. It will always flush as much output as possible. 54 | However, it is possible that some input bytes produce no output, 55 | because some part of the input may be used for internal purposes. 56 | Conversely, it is possible that output is produced without reading 57 | any input. When the encryption/decryption function returns, at 58 | least one of avail_in or avail_out is 0. If avail_out is non-zero 59 | after a call, the application may conclude that no more output is 60 | pending. 61 | 62 | Finally, the internal state must be freed by calling the respective 63 | end function. This function will discard any unprocessed input 64 | and/or output, so it should only be called after the application 65 | has retrieved any pending output. Note: the call to the end 66 | function should be checked for errors, because this is the only 67 | place where certain format errors (like a file that is too short) 68 | are detected. 69 | 70 | All functions return 0 on success, or -1 on error with errno set, 71 | or -2 on error with ccrypt_errno set. The _end functions do not 72 | return -1 or set errno. Callers must check for errors. If an error 73 | occurs, the stream is invalid and its resources are freed (and the 74 | state field set to NULL). The stream may not be used again. It is 75 | then safe, but not required, to call the corresponding *_end 76 | function. 77 | 78 | The flags argument to ccdecrypt_init should be 0 by default, or 79 | CCRYPT_MISMATCH if non-matching keys should be ignored. All other 80 | values are undefined and reserved for future use. 81 | */ 82 | 83 | /* ccdecrypt_multi_init this is a variant of ccdecrypt_init that 84 | allows a list of n>=1 keys to be defined. During decryption, the 85 | first matching key is used. This is useful when batch decrypting a 86 | set of files that have non-uniform keys. If flags includes 87 | CCRYPT_MISMATCH, then the first key is always used regardless of 88 | whether it matches or not. */ 89 | int ccdecrypt_multi_init(ccrypt_stream_t *b, int n, const char **keylist, int flags); 90 | 91 | /* errors */ 92 | 93 | #define CCRYPT_EFORMAT 1 /* bad file format */ 94 | #define CCRYPT_EMISMATCH 2 /* key does not match */ 95 | #define CCRYPT_EBUFFER 3 /* buffer overflow */ 96 | 97 | /* flags */ 98 | 99 | #define CCRYPT_MISMATCH 1 /* ignore non-matching key */ 100 | 101 | extern int ccrypt_errno; 102 | 103 | #ifdef __cplusplus 104 | } // end of extern "C" 105 | #endif 106 | 107 | #endif /* _CCRYPTLIB_H */ 108 | -------------------------------------------------------------------------------- /ext/ccrypt/patch/ccrypt.patch: -------------------------------------------------------------------------------- 1 | --- platform.h.org 2018-03-03 00:37:29.000000000 +0100 2 | +++ platform.h 2018-08-07 13:59:12.000000000 +0200 3 | @@ -17,7 +17,7 @@ 4 | /* Windows/MinGW, dummy replacements for some POSIX functions */ 5 | #ifdef __MINGW32__ 6 | 7 | -#error "Ccrypt cannot currently be compiled under MinGW. For details, see platform.h" 8 | +//#error "Ccrypt cannot currently be compiled under MinGW. For details, see platform.h" 9 | /* Some problems with MinGW are: 10 | - there is no way to read passwords from console in MinGW that 11 | works for Windows console, Msys console, Cygwin console, and 12 | -------------------------------------------------------------------------------- /ext/ccrypt/platform.c: -------------------------------------------------------------------------------- 1 | /* Platform specific stuff. */ 2 | 3 | #ifdef __MINGW32__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "platform.h" 12 | 13 | /* Windows replacement for gethostname() */ 14 | int mingw_compat_gethostname(char *name, size_t len) { 15 | char buf[MAX_COMPUTERNAME_LENGTH + 1]; 16 | int r; 17 | DWORD len1 = sizeof(buf); 18 | 19 | r = GetComputerNameA(buf, &len1); 20 | if (r==0) { 21 | name[0] = '\0'; 22 | } else { 23 | strncpy(name, buf, len-1); 24 | name[len-1] = '\0'; 25 | } 26 | return 0; 27 | } 28 | 29 | /* Windows replacement for gettimeofday(). Fills a struct timeval with 30 | the current time, ignoring the timezone argument. Note: although 31 | the result is in microseconds, it has only millisecond resolution. */ 32 | int gettimeofday(struct timeval *tv, void *tz) { 33 | SYSTEMTIME st; 34 | FILETIME ft; 35 | long long int t; 36 | 37 | GetSystemTime(&st); /* get current time (millisec resolution) */ 38 | SystemTimeToFileTime(&st, &ft); /* converts to file time */ 39 | t = ((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime; 40 | /* 100 nanosec since Jan 1, 1601 UTC */ 41 | t -= 116444736000000000; /* 100 nanosec since Jan 1, 1970 UTC */ 42 | t /= 10; /* microsec since Jan 1, 1970 UTC */ 43 | tv->tv_usec = t % 1000000; 44 | tv->tv_sec = t / 1000000; 45 | return 0; 46 | } 47 | 48 | /* generic replacement for mkstemp() */ 49 | int mkstemp(char *template) { 50 | int len = strlen(template); 51 | int errno_save = errno; 52 | int fd; 53 | char *p; 54 | int i; 55 | const int max_tries = 1024; 56 | 57 | if (len < 6) { 58 | errno = EINVAL; 59 | return -1; 60 | } 61 | for (i=0; i= 0) { 68 | errno = errno_save; 69 | return fd; 70 | } 71 | if (errno != EEXIST) { 72 | return -1; 73 | } 74 | /* there was a race condition: a file of this name was created 75 | between the mktemp and open calls. We just try again */ 76 | memset(template+len-6, 'X', 6); 77 | } 78 | errno = EEXIST; 79 | return -1; 80 | } 81 | 82 | #endif /* __MINGW32__ */ 83 | 84 | /* ISO C forbids an empty translation unit, so we put a dummy 85 | declaration here to turn off the warning. */ 86 | typedef int dummy_declaration; 87 | -------------------------------------------------------------------------------- /ext/ccrypt/platform.h: -------------------------------------------------------------------------------- 1 | /* Platform-specific stuff. */ 2 | 3 | /* The general approach is to: (1) program for a generic POSIX-like 4 | system with added support for text/binary files, and (2) put 5 | platform-dependent hacks here. There are some cases where this may 6 | not work, such as terminal I/O. In this case, some platform 7 | dependent code may appear in individual source files. 8 | 9 | Note: the dummy replacements for missing functions provided here 10 | are not fully functional; they just work well enough for our 11 | purposes. In many cases they are no-ops. */ 12 | 13 | #ifndef PLATFORM_H 14 | #define PLATFORM_H 15 | 16 | /* ---------------------------------------------------------------------- */ 17 | /* Windows/MinGW, dummy replacements for some POSIX functions */ 18 | #ifdef __MINGW32__ 19 | 20 | //#error "Ccrypt cannot currently be compiled under MinGW. For details, see platform.h" 21 | /* Some problems with MinGW are: 22 | - there is no way to read passwords from console in MinGW that 23 | works for Windows console, Msys console, Cygwin console, and 24 | xterm under X 25 | - "unlink" will not work on open files, and "rename" does not work 26 | if the destination exists 27 | - getpid() does not return a meaningful value 28 | - stat() returns an empty inode field 29 | - progress display using VT100 terminal codes does not work on Windows 30 | console 31 | */ 32 | 33 | #include /* for struct timeval */ 34 | 35 | /* traverse.c */ 36 | 37 | /* treat symbolic links, permissions, and ownership as no-ops */ 38 | #define lstat(x,y) stat(x,y) 39 | #define S_ISLNK(x) 0 40 | 41 | #define S_IWGRP 0 42 | #define S_IWOTH 0 43 | 44 | static inline int fchown(int fd, int owner, int group) { 45 | return 0; 46 | } 47 | static inline int fchmod(int fd, mode_t mode) { 48 | return 0; 49 | } 50 | 51 | /* mkstemp replacement */ 52 | int mkstemp(char *template); 53 | 54 | /* ccrypt.c */ 55 | 56 | #define ftruncate(fd, len) chsize(fd, len) 57 | 58 | /* ccryptlib.c */ 59 | 60 | /* avoid conflict with a declaraion in winsock2.h */ 61 | #define gethostname(name, len) mingw_compat_gethostname(name, len) 62 | int mingw_compat_gethostname(char *name, size_t len); 63 | 64 | int gettimeofday(struct timeval *tv, void *tz); 65 | 66 | #endif /* __MINGW32__ */ 67 | 68 | /* ---------------------------------------------------------------------- */ 69 | /* setmode() and O_BINARY */ 70 | 71 | #if defined(__CYGWIN__) 72 | 73 | #include /* for setmode() */ 74 | 75 | #elif defined(__MINGW32__) || defined(__EMX__) 76 | 77 | /* nothing needed */ 78 | 79 | #else /* on a POSIX system, map these to no-ops */ 80 | 81 | /* BSD defines setmode() with a different meaning, in unistd.h */ 82 | static inline void ccrypt_setmode(int fd, int mode) { 83 | return; 84 | } 85 | #undef setmode 86 | #define setmode(x,y) ccrypt_setmode(x,y) 87 | 88 | #ifndef O_BINARY 89 | #define O_BINARY 0 90 | #endif /* O_BINARY */ 91 | 92 | #endif /* __CYGWIN__ || __MINGW32__ || etc */ 93 | 94 | /* ---------------------------------------------------------------------- */ 95 | #endif /* PLATFORM_H */ 96 | -------------------------------------------------------------------------------- /ext/ccrypt/rijndael.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2000-2018 Peter Selinger. 2 | This file is part of ccrypt. It is free software and it is covered 3 | by the GNU general public license. See the file COPYING for details. */ 4 | 5 | /* rijndael.c - optimized version of the Rijndeal cipher */ 6 | 7 | /* derived from original source: rijndael-alg-ref.c v2.0 August '99 8 | * Reference ANSI C code for NIST competition 9 | * authors: Paulo Barreto 10 | * Vincent Rijmen 11 | */ 12 | 13 | #include "rijndael.h" 14 | #include "tables.h" 15 | 16 | static int xshifts[3][2][4] = { 17 | {{0, 1, 2, 3}, 18 | {0, 3, 2, 1}}, 19 | 20 | {{0, 1, 2, 3}, 21 | {0, 5, 4, 3}}, 22 | 23 | {{0, 1, 3, 4}, 24 | {0, 7, 5, 4}}, 25 | }; 26 | 27 | /* Exor corresponding text input and round key input bytes */ 28 | /* the result is written to res, which can be the same as a */ 29 | static inline void xKeyAddition(xword32 res[MAXBC], xword32 a[MAXBC], 30 | xword32 rk[MAXBC], int BC) 31 | { 32 | int j; 33 | 34 | for (j = 0; j < BC; j++) { 35 | res[j] = a[j] ^ rk[j]; 36 | } 37 | } 38 | 39 | #if 0 /* code included for reference */ 40 | 41 | /* shift rows a, return result in res. This avoids having to copy a 42 | tmp array back to a. res must not be a. */ 43 | static inline void xShiftRow(xword32 res[MAXBC], xword32 a[MAXBC], int shift[4], 44 | int BC) 45 | { 46 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 47 | xword8 (*res8)[4] = (xword8 (*)[4]) res; 48 | 49 | /* Row 0 remains unchanged 50 | * The other three rows are shifted a variable amount 51 | */ 52 | int i, j; 53 | int s; 54 | 55 | for (j = 0; j < BC; j++) { 56 | res8[j][0] = a8[j][0]; 57 | } 58 | for (i = 1; i < 4; i++) { 59 | s = shift[i]; 60 | for (j = 0; j < BC; j++) { 61 | res8[j][i] = a8[(j + s) % BC][i]; 62 | } 63 | } 64 | } 65 | 66 | static inline void xSubstitution(xword32 a[MAXBC], xword8 box[256], int BC) 67 | { 68 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 69 | 70 | /* Replace every byte of the input by the byte at that place 71 | * in the nonlinear S-box 72 | */ 73 | int i, j; 74 | 75 | for (i = 0; i < 4; i++) { 76 | for (j = 0; j < BC; j++) { 77 | a8[j][i] = box[a[j][i]]; 78 | } 79 | } 80 | } 81 | 82 | #endif /* code included for reference */ 83 | 84 | /* profiling shows that the ccrypt program spends about 50% of its 85 | time in the function xShiftSubst. Splitting the inner "for" 86 | statement into two parts - versus using the expensive "%" modulo 87 | operation, makes this function about 44% faster, thereby making the 88 | entire program about 28% faster. With -O3 optimization, the time 89 | savings are even more dramatic - ccrypt runs between 55% and 65% 90 | faster on most platforms. */ 91 | 92 | /* do ShiftRow and Substitution together. res must not be a. */ 93 | static inline void xShiftSubst(xword32 res[MAXBC], xword32 a[MAXBC], 94 | int shift[4], int BC, xword8 box[256]) 95 | { 96 | int i, j; 97 | int s; 98 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 99 | xword8 (*res8)[4] = (xword8 (*)[4]) res; 100 | 101 | for (j = 0; j < BC; j++) { 102 | res8[j][0] = box[a8[j][0]]; 103 | } 104 | for (i = 1; i < 4; i++) { 105 | s = shift[i]; 106 | for (j = 0; j < BC - s; j++) { 107 | res8[j][i] = box[a8[(j + s)][i]]; 108 | } 109 | for (j = BC - s; j < BC; j++) { 110 | res8[j][i] = box[a8[(j + s) - BC][i]]; 111 | } 112 | } 113 | } 114 | 115 | #if 0 /* code included for reference */ 116 | 117 | /* Mix the four bytes of every column in a linear way */ 118 | /* the result is written to res, which may equal a */ 119 | static inline void xMixColumn(xword32 res[MAXBC], xword32 a[MAXBC], int BC) 120 | { 121 | int j; 122 | xword32 b; 123 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 124 | 125 | for (j = 0; j < BC; j++) { 126 | b = M0[0][a8[j][0]].w32; 127 | b ^= M0[1][a8[j][1]].w32; 128 | b ^= M0[2][a8[j][2]].w32; 129 | b ^= M0[3][a8[j][3]].w32; 130 | res[j] = b; 131 | } 132 | } 133 | 134 | #endif /* code included for reference */ 135 | 136 | /* do MixColumn and KeyAddition together */ 137 | static inline void xMixAdd(xword32 res[MAXBC], xword32 a[MAXBC], 138 | xword32 rk[MAXBC], int BC) 139 | { 140 | int j; 141 | xword32 b; 142 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 143 | 144 | for (j = 0; j < BC; j++) { 145 | b = M0[0][a8[j][0]].w32; 146 | b ^= M0[1][a8[j][1]].w32; 147 | b ^= M0[2][a8[j][2]].w32; 148 | b ^= M0[3][a8[j][3]].w32; 149 | b ^= rk[j]; 150 | res[j] = b; 151 | } 152 | } 153 | 154 | /* Mix the four bytes of every column in a linear way 155 | * This is the opposite operation of xMixColumn */ 156 | /* the result is written to res, which may equal a */ 157 | static inline void xInvMixColumn(xword32 res[MAXBC], xword32 a[MAXBC], int BC) 158 | { 159 | int j; 160 | xword32 b; 161 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 162 | 163 | for (j = 0; j < BC; j++) { 164 | b = M1[0][a8[j][0]].w32; 165 | b ^= M1[1][a8[j][1]].w32; 166 | b ^= M1[2][a8[j][2]].w32; 167 | b ^= M1[3][a8[j][3]].w32; 168 | res[j] = b; 169 | } 170 | } 171 | 172 | #if 0 /* code included for reference */ 173 | 174 | /* do KeyAddition and InvMixColumn together */ 175 | static inline void xAddInvMix(xword32 res[MAXBC], xword32 a[MAXBC], 176 | xword32 rk[MAXBC], int BC) 177 | { 178 | int j; 179 | xword32 b; 180 | xword8 (*a8)[4] = (xword8 (*)[4]) a; 181 | 182 | for (j = 0; j < BC; j++) { 183 | a[j] = a[j] ^ rk[j]; 184 | b = M1[0][a8[j][0]].w32; 185 | b ^= M1[1][a8[j][1]].w32; 186 | b ^= M1[2][a8[j][2]].w32; 187 | b ^= M1[3][a8[j][3]].w32; 188 | res[j] = b; 189 | } 190 | } 191 | 192 | #endif /* code included for reference */ 193 | 194 | int xrijndaelKeySched(xword32 key[], int keyBits, int blockBits, 195 | roundkey *rkk) 196 | { 197 | /* Calculate the necessary round keys 198 | * The number of calculations depends on keyBits and blockBits */ 199 | int KC, BC, ROUNDS; 200 | int i, j, t, rconpointer = 0; 201 | xword8 (*k8)[4] = (xword8 (*)[4]) key; 202 | 203 | switch (keyBits) { 204 | case 128: 205 | KC = 4; 206 | break; 207 | case 192: 208 | KC = 6; 209 | break; 210 | case 256: 211 | KC = 8; 212 | break; 213 | default: 214 | return -1; 215 | } 216 | 217 | switch (blockBits) { 218 | case 128: 219 | BC = 4; 220 | break; 221 | case 192: 222 | BC = 6; 223 | break; 224 | case 256: 225 | BC = 8; 226 | break; 227 | default: 228 | return -2; 229 | } 230 | 231 | ROUNDS = KC > BC ? KC + 6 : BC + 6; 232 | 233 | t = 0; 234 | /* copy values into round key array */ 235 | for (j = 0; (j < KC) && (t < (ROUNDS + 1) * BC); j++, t++) 236 | rkk->rk[t] = key[j]; 237 | 238 | while (t < (ROUNDS + 1) * BC) { /* while not enough round key material */ 239 | /* calculate new values */ 240 | for (i = 0; i < 4; i++) { 241 | k8[0][i] ^= xS[k8[KC - 1][(i + 1) % 4]]; 242 | } 243 | k8[0][0] ^= xrcon[rconpointer++]; 244 | 245 | if (KC != 8) { 246 | for (j = 1; j < KC; j++) { 247 | key[j] ^= key[j - 1]; 248 | } 249 | } else { 250 | for (j = 1; j < 4; j++) { 251 | key[j] ^= key[j - 1]; 252 | } 253 | for (i = 0; i < 4; i++) { 254 | k8[4][i] ^= xS[k8[3][i]]; 255 | } 256 | for (j = 5; j < 8; j++) { 257 | key[j] ^= key[j - 1]; 258 | } 259 | } 260 | /* copy values into round key array */ 261 | for (j = 0; (j < KC) && (t < (ROUNDS + 1) * BC); j++, t++) { 262 | rkk->rk[t] = key[j]; 263 | } 264 | } 265 | 266 | /* make roundkey structure */ 267 | rkk->BC = BC; 268 | rkk->KC = KC; 269 | rkk->ROUNDS = ROUNDS; 270 | for (i = 0; i < 2; i++) { 271 | for (j = 0; j < 4; j++) { 272 | rkk->shift[i][j] = xshifts[(BC - 4) >> 1][i][j]; 273 | } 274 | } 275 | 276 | return 0; 277 | } 278 | 279 | /* Encryption of one block. */ 280 | 281 | void xrijndaelEncrypt(xword32 block[], roundkey *rkk) 282 | { 283 | xword32 block2[MAXBC]; /* hold intermediate result */ 284 | int r; 285 | 286 | int *shift = rkk->shift[0]; 287 | int BC = rkk->BC; 288 | int ROUNDS = rkk->ROUNDS; 289 | xword32 *rp = rkk->rk; 290 | 291 | /* begin with a key addition */ 292 | xKeyAddition(block, block, rp, BC); 293 | rp += BC; 294 | 295 | /* ROUNDS-1 ordinary rounds */ 296 | for (r = 1; r < ROUNDS; r++) { 297 | xShiftSubst(block2, block, shift, BC, xS); 298 | xMixAdd(block, block2, rp, BC); 299 | rp += BC; 300 | } 301 | 302 | /* Last round is special: there is no xMixColumn */ 303 | xShiftSubst(block2, block, shift, BC, xS); 304 | xKeyAddition(block, block2, rp, BC); 305 | } 306 | 307 | void xrijndaelDecrypt(xword32 block[], roundkey *rkk) 308 | { 309 | xword32 block2[MAXBC]; /* hold intermediate result */ 310 | int r; 311 | 312 | int *shift = rkk->shift[1]; 313 | int BC = rkk->BC; 314 | int ROUNDS = rkk->ROUNDS; 315 | xword32 *rp = rkk->rk + ROUNDS * BC; 316 | 317 | /* To decrypt: apply the inverse operations of the encrypt routine, 318 | * in opposite order 319 | * 320 | * (xKeyAddition is an involution: it's equal to its inverse) 321 | * (the inverse of xSubstitution with table S is xSubstitution with the 322 | * inverse table of S) 323 | * (the inverse of xShiftRow is xShiftRow over a suitable distance) 324 | */ 325 | 326 | /* First the special round: 327 | * without xInvMixColumn 328 | * with extra xKeyAddition 329 | */ 330 | xKeyAddition(block2, block, rp, BC); 331 | xShiftSubst(block, block2, shift, BC, xSi); 332 | rp -= BC; 333 | 334 | /* ROUNDS-1 ordinary rounds 335 | */ 336 | for (r = ROUNDS - 1; r > 0; r--) { 337 | xKeyAddition(block, block, rp, BC); 338 | xInvMixColumn(block2, block, BC); 339 | xShiftSubst(block, block2, shift, BC, xSi); 340 | rp -= BC; 341 | } 342 | 343 | /* End with the extra key addition 344 | */ 345 | 346 | xKeyAddition(block, block, rp, BC); 347 | } 348 | -------------------------------------------------------------------------------- /ext/ccrypt/rijndael.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2000-2018 Peter Selinger. 2 | This file is part of ccrypt. It is free software and it is covered 3 | by the GNU general public license. See the file COPYING for details. */ 4 | 5 | /* rijndael.h */ 6 | 7 | /* derived from original source: rijndael-alg-ref.h v2.0 August '99 8 | * Reference ANSI C code for NIST competition 9 | * authors: Paulo Barreto 10 | * Vincent Rijmen 11 | */ 12 | 13 | #ifndef __RIJNDAEL_H 14 | #define __RIJNDAEL_H 15 | 16 | #ifdef HAVE_CONFIG_H 17 | #include /* generated by configure */ 18 | #endif 19 | 20 | typedef unsigned char xword8; 21 | 22 | #ifdef UINT32_TYPE 23 | typedef UINT32_TYPE xword32; 24 | #else 25 | typedef int xword32; /* should be a 32-bit integer type */ 26 | #endif 27 | 28 | /* a type to hold 32 bits accessible as 1 integer or 4 bytes */ 29 | union xword8x4_u { 30 | xword8 w8[4]; 31 | xword32 w32; 32 | }; 33 | typedef union xword8x4_u xword8x4; 34 | 35 | #define MAXBC (256/32) 36 | #define MAXKC (256/32) 37 | #define MAXROUNDS 14 38 | #define MAXRK ((MAXROUNDS+1)*MAXBC) 39 | 40 | typedef struct { 41 | int BC; 42 | int KC; 43 | int ROUNDS; 44 | int shift[2][4]; 45 | xword32 rk[MAXRK]; 46 | } roundkey; 47 | 48 | /* keys and blocks are externally treated as xword32 arrays, to 49 | make sure they are aligned on 4-byte boundaries on architectures 50 | that require it. */ 51 | 52 | /* make a roundkey rkk from key. key must have appropriate size given 53 | by keyBits. keyBits and blockBits may only be 128, 196, or 54 | 256. Returns non-zero if arguments are invalid. */ 55 | 56 | int xrijndaelKeySched(xword32 key[], int keyBits, int blockBits, 57 | roundkey *rkk); 58 | 59 | /* encrypt, resp. decrypt, block using rijndael roundkey rkk. rkk must 60 | have been created with xrijndaelKeySched. Size of block, in bits, 61 | must be equal to blockBits parameter that was used to make rkk. In 62 | all other cases, behavior is undefined - for reasons of speed, no 63 | check for error conditions is done. */ 64 | 65 | void xrijndaelEncrypt(xword32 block[], roundkey *rkk); 66 | void xrijndaelDecrypt(xword32 block[], roundkey *rkk); 67 | 68 | #endif /* __RIJNDAEL_H */ 69 | -------------------------------------------------------------------------------- /ext/ccrypt/tables.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2000-2018 Peter Selinger. 2 | This file is part of ccrypt. It is free software and it is covered 3 | by the GNU general public license. See the file COPYING for details. */ 4 | 5 | #ifndef TABLES_H 6 | #define TABLES_H 7 | 8 | #include "rijndael.h" 9 | 10 | extern xword8x4 M0[4][256]; 11 | extern xword8x4 M1[4][256]; 12 | extern int xrcon[30]; 13 | extern xword8 xS[256]; 14 | extern xword8 xSi[256]; 15 | 16 | #endif /* TABLES_H */ 17 | -------------------------------------------------------------------------------- /ext/qaes/qaesencryption.h: -------------------------------------------------------------------------------- 1 | #ifndef QAESENCRYPTION_H 2 | #define QAESENCRYPTION_H 3 | 4 | #include 5 | #include 6 | 7 | class QAESEncryption : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | enum Aes { 12 | AES_128, 13 | AES_192, 14 | AES_256 15 | }; 16 | 17 | enum Mode { 18 | ECB, 19 | CBC, 20 | CFB, 21 | OFB 22 | }; 23 | 24 | enum Padding { 25 | ZERO, 26 | PKCS7, 27 | ISO 28 | }; 29 | 30 | static QByteArray Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, 31 | const QByteArray &iv = NULL, QAESEncryption::Padding padding = QAESEncryption::ISO); 32 | static QByteArray Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, 33 | const QByteArray &iv = NULL, QAESEncryption::Padding padding = QAESEncryption::ISO); 34 | static QByteArray ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key); 35 | static QByteArray RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding); 36 | 37 | QAESEncryption(QAESEncryption::Aes level, QAESEncryption::Mode mode, 38 | QAESEncryption::Padding padding = QAESEncryption::ISO); 39 | 40 | QByteArray encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = NULL); 41 | QByteArray decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv = NULL); 42 | QByteArray removePadding(const QByteArray &rawText); 43 | QByteArray expandKey(const QByteArray &key); 44 | 45 | signals: 46 | 47 | public slots: 48 | 49 | private: 50 | int m_nb; 51 | int m_blocklen; 52 | int m_level; 53 | int m_mode; 54 | int m_nk; 55 | int m_keyLen; 56 | int m_nr; 57 | int m_expandedKey; 58 | int m_padding; 59 | QByteArray* m_state; 60 | 61 | struct AES256{ 62 | int nk = 8; 63 | int keylen = 32; 64 | int nr = 14; 65 | int expandedKey = 240; 66 | }; 67 | 68 | struct AES192{ 69 | int nk = 6; 70 | int keylen = 24; 71 | int nr = 12; 72 | int expandedKey = 209; 73 | }; 74 | 75 | struct AES128{ 76 | int nk = 4; 77 | int keylen = 16; 78 | int nr = 10; 79 | int expandedKey = 176; 80 | }; 81 | 82 | quint8 getSBoxValue(quint8 num){return sbox[num];} 83 | quint8 getSBoxInvert(quint8 num){return rsbox[num];} 84 | 85 | void addRoundKey(const quint8 round, const QByteArray expKey); 86 | void subBytes(); 87 | void shiftRows(); 88 | void mixColumns(); 89 | void invMixColumns(); 90 | void invSubBytes(); 91 | void invShiftRows(); 92 | QByteArray getPadding(int currSize, int alignment); 93 | QByteArray cipher(const QByteArray &expKey, const QByteArray &plainText); 94 | QByteArray invCipher(const QByteArray &expKey, const QByteArray &plainText); 95 | QByteArray byteXor(const QByteArray &in, const QByteArray &iv); 96 | 97 | const quint8 sbox[256] = { 98 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 99 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 100 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 101 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 102 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 103 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 104 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 105 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 106 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 107 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 108 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 109 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 110 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 111 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 112 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 113 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 114 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 115 | 116 | const quint8 rsbox[256] = 117 | { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 118 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 119 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 120 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 121 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 122 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 123 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 124 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 125 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 126 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 127 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 128 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 129 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 130 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 131 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 132 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 133 | 134 | // The round constant word array, Rcon[i], contains the values given by 135 | // x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 136 | // Only the first 14 elements are needed 137 | const quint8 Rcon[256] = { 138 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab/*, 0x4d, 0x9a, 139 | 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 140 | 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 141 | 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 142 | 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 143 | 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 144 | 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 145 | 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 146 | 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 147 | 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 148 | 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 149 | 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 150 | 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 151 | 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 152 | 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 153 | 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d */}; 154 | }; 155 | 156 | #endif // QAESENCRYPTION_H 157 | -------------------------------------------------------------------------------- /ext/qsshsocket/patch/qsshsocket.patch: -------------------------------------------------------------------------------- 1 | --- qsshsocket.h.org 2017-02-23 22:25:12.000000000 +0100 2 | +++ qsshsocket.h 2018-08-10 19:14:33.146938895 +0200 3 | @@ -36,7 +36,9 @@ 4 | /*! \brief There was an error writing to a remote file. This could possibly be due to user permissions.*/ 5 | ScpWriteError, 6 | /*! \brief The credentials of a user on the remote host could not be authenticated.*/ 7 | - PasswordAuthenticationFailedError 8 | + PasswordAuthenticationFailedError, 9 | + /*! \brief The private key of a user on the remote host could not be authenticated.*/ 10 | + KeyAuthenticationFailedError 11 | }; 12 | 13 | /*! 14 | @@ -94,6 +96,14 @@ 15 | void login(QString user, QString password); 16 | 17 | /*! 18 | + \param prv The private key to login with; 19 | + \brief This function to login to the currently connected host given private key. 20 | + On success, the signal authenticated is emitted while error is emmited on failure. 21 | + */ 22 | + 23 | + void setKey(QString key); 24 | + 25 | + /*! 26 | \brief Returns the port of the current connection. If not connected to a remote host, this returns -1. 27 | */ 28 | int port(); 29 | @@ -204,7 +214,7 @@ 30 | int m_port; 31 | bool m_loggedIn ; 32 | QThread * m_thread; 33 | - QString m_workingDirectory,m_nextWorkingDir,m_user, m_host,m_password; 34 | + QString m_workingDirectory,m_nextWorkingDir,m_user, m_host,m_password,m_key; 35 | SSHOperation m_currentOperation; 36 | ssh_session m_session; 37 | bool m_connected,m_run; 38 | --- qsshsocket.cpp.org 2017-02-23 22:25:12.000000000 +0100 39 | +++ qsshsocket.cpp 2018-08-11 11:09:38.142030435 +0200 40 | @@ -1,7 +1,7 @@ 41 | #include "qsshsocket.h" 42 | #include 43 | // if compiling in windows, add needed flags. 44 | -#ifdef _WIN32 45 | +#if defined(_WIN32) && !defined(__MINGW32__) 46 | # include 47 | 48 | typedef int mode_t; 49 | @@ -45,6 +45,7 @@ 50 | m_host = ""; 51 | m_user = ""; 52 | m_password = ""; 53 | + m_key = ""; 54 | m_port = -1; 55 | m_loggedIn = false; 56 | m_session = NULL; 57 | @@ -78,8 +79,8 @@ 58 | int verbosity = SSH_LOG_PROTOCOL; 59 | 60 | // set the pertinant ssh session options 61 | - ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.toAscii().data()); 62 | - ssh_options_set(m_session, SSH_OPTIONS_USER, m_user.toAscii().data()); 63 | + ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.toUtf8().data()); 64 | + ssh_options_set(m_session, SSH_OPTIONS_USER, m_user.toUtf8().data()); 65 | ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); 66 | ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port); 67 | 68 | @@ -108,7 +109,7 @@ 69 | { 70 | 71 | // try authenticating current user at remote host 72 | - int worked = ssh_userauth_password(m_session, m_user.toAscii().data(), m_password.toAscii().data()); 73 | + int worked = ssh_userauth_password(m_session, m_user.toUtf8().data(), m_password.toUtf8().data()); 74 | 75 | 76 | // if successful, store user password. 77 | @@ -126,6 +127,36 @@ 78 | 79 | 80 | } 81 | + // check to see if a username and a private key have been given 82 | + else if(m_user != "" && m_key != "") 83 | + { 84 | + ssh_key private_key; 85 | + 86 | + if(ssh_pki_import_privkey_base64(m_key.toUtf8().data(), NULL, NULL, NULL, &private_key) == SSH_OK) 87 | + { 88 | + // try authenticating current user at remote host 89 | + int worked = ssh_userauth_publickey(m_session, m_user.toUtf8().data(), private_key); 90 | + 91 | + // if successful, store user key. 92 | + if (worked == SSH_OK) 93 | + { 94 | + loginSuccessful(); 95 | + m_loggedIn = true; 96 | + } 97 | + else 98 | + { 99 | + m_user = ""; 100 | + m_key = ""; 101 | + error(PasswordAuthenticationFailedError); 102 | + } 103 | + } 104 | + else 105 | + { 106 | + m_user = ""; 107 | + m_key = ""; 108 | + error(KeyAuthenticationFailedError); 109 | + } 110 | + } 111 | } 112 | // if all ssh setup has been completed, check to see if we have any commands to execute 113 | else if (!m_currentOperation.executed) 114 | @@ -147,7 +178,7 @@ 115 | 116 | // attempt to execute shell command 117 | while (requestResponse == SSH_AGAIN) 118 | - requestResponse = ssh_channel_request_exec(channel, m_currentOperation.adminCommand.toAscii().data()); 119 | + requestResponse = ssh_channel_request_exec(channel, m_currentOperation.adminCommand.toUtf8().data()); 120 | 121 | // if attempt not executed, close connection then return 122 | if (requestResponse != SSH_OK) 123 | @@ -168,6 +199,17 @@ 124 | totalBytes += newBytes; 125 | }while (newBytes > 0); 126 | 127 | + // read in command error 128 | + if(!totalBytes) 129 | + { 130 | + do 131 | + { 132 | + newBytes = ssh_channel_read(channel, &(buffer.data()[totalBytes]), buffer.size() - totalBytes, 1); 133 | + if (newBytes > 0) 134 | + totalBytes += newBytes; 135 | + }while (newBytes > 0); 136 | + } 137 | + 138 | // close channel 139 | ssh_channel_send_eof(channel); 140 | ssh_channel_close(channel); 141 | @@ -189,7 +231,7 @@ 142 | // if all ssh setup has been completed, check to see if we have any file transfers to execute 143 | else if (m_currentOperation.type == Pull) 144 | { 145 | - ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_READ, m_currentOperation.remotePath.toAscii().data()); 146 | + ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_READ, m_currentOperation.remotePath.toUtf8().data()); 147 | if (scpSession == NULL) 148 | error(ScpChannelCreationError); 149 | 150 | @@ -254,7 +296,7 @@ 151 | else if (m_currentOperation.type == Push) 152 | { 153 | // attempt to create new scp from ssh session. 154 | - ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_WRITE, m_currentOperation.remotePath.toAscii().data()); 155 | + ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_WRITE, m_currentOperation.remotePath.toUtf8().data()); 156 | 157 | // if creation failed, return 158 | if (scpSession == NULL) 159 | @@ -291,7 +333,7 @@ 160 | 161 | // attempt to authorize pushing bytes over scp socket 162 | // if this fails, close scp session and return. 163 | - if (ssh_scp_push_file(scpSession, m_currentOperation.remotePath.toAscii().data(), buffer.size(), S_IRUSR | S_IWUSR) != SSH_OK) 164 | + if (ssh_scp_push_file(scpSession, m_currentOperation.remotePath.toUtf8().data(), buffer.size(), S_IRUSR | S_IWUSR) != SSH_OK) 165 | { 166 | ssh_scp_close(scpSession); 167 | ssh_scp_free(scpSession); 168 | @@ -334,12 +376,15 @@ 169 | m_host = ""; 170 | m_user = ""; 171 | m_password = ""; 172 | + m_key = ""; 173 | m_port = -1; 174 | m_loggedIn = false; 175 | if (m_session != NULL) 176 | { 177 | ssh_disconnect(m_session); 178 | ssh_free(m_session); 179 | + 180 | + disconnected(); 181 | } 182 | m_session = NULL; 183 | } 184 | @@ -354,6 +399,10 @@ 185 | m_user = user; 186 | m_password = password; 187 | } 188 | +void QSshSocket::setKey(QString key) 189 | +{ 190 | + m_key = key; 191 | +} 192 | void QSshSocket::executeCommand(QString command) 193 | { 194 | m_currentOperation.type = Command; 195 | -------------------------------------------------------------------------------- /ext/qsshsocket/qsshsocket.h: -------------------------------------------------------------------------------- 1 | #ifndef QSSHSOCKET_H 2 | #define QSSHSOCKET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class QSshSocket: public QThread 14 | { 15 | Q_OBJECT 16 | public: 17 | 18 | enum SshError 19 | { 20 | /*! \brief There was trouble creating a socket. This was most likely due to the lack of an internet connection.*/ 21 | SocketError, 22 | /*! \brief The ssh session could not be created due to inability to find the remote host.*/ 23 | SessionCreationError, 24 | /*! \brief An ssh channel could not be created for the previous operation.*/ 25 | ChannelCreationError, 26 | /*! \brief An scp channel could not be created for the previous file transfer operation.*/ 27 | ScpChannelCreationError, 28 | /*! \brief There was an error requesting a pull file transfer.*/ 29 | ScpPullRequestError, 30 | /*! \brief There was an error requesting a push file transfer.*/ 31 | ScpPushRequestError, 32 | /*! \brief The destination file for the previous transfer does not exist.*/ 33 | ScpFileNotCreatedError, 34 | /*! \brief There was an error reading a remote file. This could possibly be due to user permissions.*/ 35 | ScpReadError, 36 | /*! \brief There was an error writing to a remote file. This could possibly be due to user permissions.*/ 37 | ScpWriteError, 38 | /*! \brief The credentials of a user on the remote host could not be authenticated.*/ 39 | PasswordAuthenticationFailedError, 40 | /*! \brief The private key of a user on the remote host could not be authenticated.*/ 41 | KeyAuthenticationFailedError 42 | }; 43 | 44 | /*! 45 | \param position The center position of the box. 46 | \param size The size of the box. 47 | \brief The constructor. 48 | */ 49 | explicit QSshSocket(QObject * parent = 0); 50 | 51 | /*! 52 | \brief The deconstructor. 53 | */ 54 | ~QSshSocket(); 55 | 56 | /*! 57 | \param host The hostname to establish an ssh connection with. 58 | \param port The port to establish an ssh connection over. 59 | \brief This function connects this socket to the specified host over the specified port. On success, the signal connected is emitted while error is emmited on failure. 60 | */ 61 | void connectToHost(QString host, int port =22); 62 | 63 | /*! 64 | \brief This function disconnects the socket from the current host (if there is one. On success, the signal disconnected is emitted while error is emmited on failure. 65 | */ 66 | void disconnectFromHost(); 67 | 68 | /*! 69 | \param command The command to be executed. 70 | \brief This function executes a remote command on the connected host. If not connected to a remote host, the command is not executed. 71 | On success, the signal commandExecuted is emitted while error is emmited on failure. 72 | */ 73 | void executeCommand(QString command); 74 | 75 | /*! 76 | \brief Returns the hostname of the remote machine this socket is connected to. If not connected to a remote host, this returns "". 77 | */ 78 | QString host(); 79 | 80 | /*! 81 | \brief Returns whether or not a user has been logged in at remote host. 82 | */ 83 | bool isLoggedIn(); 84 | 85 | /*! 86 | \brief Returns whether or not this socket is currently connected to a remote host. 87 | */ 88 | bool isConnected(); 89 | 90 | /*! 91 | \param user The username to login with. 92 | \param password The password of the account for the specified username. 93 | \brief This function to login to the currently connected host given credentials. 94 | On success, the signal authenticated is emitted while error is emmited on failure. 95 | */ 96 | void login(QString user, QString password); 97 | 98 | /*! 99 | \param prv The private key to login with; 100 | \param prv The private key passphrase to login with; 101 | \brief This function to login to the currently connected host given private key. 102 | On success, the signal authenticated is emitted while error is emmited on failure. 103 | */ 104 | 105 | void loginKey(QString user, QString key, QString passphrase); 106 | 107 | /*! 108 | \brief Returns the port of the current connection. If not connected to a remote host, this returns -1. 109 | */ 110 | int port(); 111 | 112 | /*! 113 | \param localPath A path to a file stored on the local machine. 114 | \param password A path to a file stored on the remote machine. 115 | \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand. 116 | On success, the signal pullSuccessful is emitted while error is emmited on failure. 117 | If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted. 118 | */ 119 | void pullFile(QString localPath, QString remotePath); 120 | 121 | /*! 122 | \param localPath A path to a file stored on the local machine. 123 | \param password A path to a file stored on the remote machine. 124 | \brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand. 125 | On success, the signal pushSuccessful is emitted while error is emmited on failure. 126 | If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted. 127 | */ 128 | void pushFile(QString localPath, QString remotePath); 129 | 130 | /*! 131 | \param path A relative or absolute path to a directory on the remote host. 132 | \brief This function attempts to set the working directory of the connection to path and emits workingDirectorySet upon completion. 133 | If workingDirectorySet indicates no change in the working directory, the path could not be found. 134 | If not connected to a remote host the signal error will be emitted. 135 | */ 136 | void setWorkingDirectory(QString path); 137 | 138 | /*! 139 | \brief Returns the username of the current authenticated user on the remote host. If not connected to a remote host, or if a user has not been authenticated this returns "". 140 | */ 141 | QString user(); 142 | 143 | 144 | 145 | signals: 146 | 147 | /*! 148 | \brief This signal is emitted when remote host has been connected to." 149 | */ 150 | void connected(); 151 | 152 | /*! 153 | \brief This signal is emitted when this class has been properly disconnected from a remote host. 154 | */ 155 | void disconnected(); 156 | 157 | /*! 158 | \param error The type of error that occured. 159 | \brief This signal is emitted when an error occurs. 160 | */ 161 | void error(QSshSocket::SshError error); 162 | 163 | /*! 164 | \param command The command that was executed on the remote host. 165 | \param response The response to the command that was executed. 166 | \brief This signal is emitted when a response from the remote host is received regarding a command. 167 | */ 168 | void commandExecuted(QString command,QString response); 169 | 170 | /*! 171 | \brief This signal is emitted when a user has been loggen in to the remote host." 172 | */ 173 | void loginSuccessful(); 174 | 175 | /*! 176 | \param localFile The path to a local file that the remote file was pulled to. 177 | \param remoteFile The path to a file pulled from the remote host. 178 | \brief This signal is emitted when a remote file is successfully transfered to a local file. 179 | */ 180 | void pullSuccessful(QString localFile, QString remoteFile); 181 | 182 | /*! 183 | \param localFile The path to a local file pushed to the remote host. 184 | \param remoteFile The path to a remote file that the local file was pushed to. 185 | \brief This signal is emitted when a local file is successfully transfered to a remote file. 186 | */ 187 | void pushSuccessful(QString localFile, QString remoteFile); 188 | 189 | /*! 190 | \param cwd The current working directory of the session on the remote host. 191 | \brief This signal is emitted when a current working directory is set. 192 | */ 193 | void workingDirectorySet(QString cwd); 194 | 195 | private slots: 196 | void run(); 197 | 198 | private: 199 | 200 | enum SSHOperationType 201 | { 202 | Command, 203 | WorkingDirectoryTest, 204 | Pull, 205 | Push 206 | }; 207 | 208 | struct SSHOperation 209 | { 210 | SSHOperationType type; 211 | QString adminCommand,command, localPath, remotePath; 212 | bool executed; 213 | }; 214 | 215 | int m_port; 216 | bool m_loggedIn ; 217 | QThread * m_thread; 218 | QString m_workingDirectory,m_nextWorkingDir,m_user,m_host,m_password,m_key,m_key_passphrase; 219 | SSHOperation m_currentOperation; 220 | ssh_session m_session; 221 | bool m_connected,m_run; 222 | }; 223 | 224 | 225 | #endif // QSSHSOCKET_H 226 | -------------------------------------------------------------------------------- /history.cpp: -------------------------------------------------------------------------------- 1 | #include "history.h" 2 | 3 | historyDialog::historyDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | buttonBox->button(QDialogButtonBox::Save)->setText(tr("Export")); 11 | 12 | foreach(int id, reinterpret_cast(parent)->robo.cleansummary.id) 13 | { 14 | record = QDateTime::fromTime_t(static_cast(id)); 15 | 16 | comboBox->addItem(record.toString("dddd, dd.MM.yyyy - hh:mm"), id); 17 | } 18 | } 19 | 20 | void historyDialog::on_comboBox_currentIndexChanged(int index) 21 | { 22 | if(reinterpret_cast(parent())->sendUDP(QString(MIIO_GET_CLEAN_RECORD).arg(comboBox->itemData(index).toInt()).arg("%1"))) 23 | { 24 | QTime time(0, 0, 0, 0); 25 | 26 | time = time.addSecs(reinterpret_cast(parent())->robo.cleanrecord.duration); 27 | 28 | label_state->setPixmap((reinterpret_cast(parent())->robo.cleanrecord.complete ? QPixmap(":/png/png/complete.png") : QPixmap(":/png/png/canceled.png"))); 29 | 30 | record = QDateTime::fromTime_t(static_cast(reinterpret_cast(parent())->robo.cleanrecord.begin)); 31 | lcdNumber_start->display(record.toString("hh:mm:ss")); 32 | 33 | record = QDateTime::fromTime_t(static_cast(reinterpret_cast(parent())->robo.cleanrecord.finish)); 34 | lcdNumber_stop->display(record.toString("hh:mm:ss")); 35 | 36 | lcdNumber_duration->display(time.toString("hh:mm:ss")); 37 | 38 | lcdNumber_area->display(QString("%1").arg(reinterpret_cast(parent())->robo.cleanrecord.area / 1000000.0)); 39 | 40 | label_error_text->setText(error_strings.at(reinterpret_cast(parent())->robo.cleanrecord.error)); 41 | label_error_icon->setPixmap((reinterpret_cast(parent())->robo.cleanrecord.error ? QPixmap(":/png/png/warning.png") : QPixmap(":/png/png/info.png"))); 42 | } 43 | } 44 | 45 | void historyDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 46 | { 47 | if(buttonBox->standardButton(button) == QDialogButtonBox::Save) 48 | { 49 | QString data("ID;DATE;BEGIN;FINISH;DURATION;AREA;ERROR;COMPLETE\n"); 50 | 51 | foreach(int id, reinterpret_cast(parent())->robo.cleansummary.id) 52 | { 53 | if(reinterpret_cast(parent())->sendUDP(QString(MIIO_GET_CLEAN_RECORD).arg(id).arg("%1"))) 54 | { 55 | QTime time(0, 0, 0, 0); 56 | 57 | time = time.addSecs(reinterpret_cast(parent())->robo.cleanrecord.duration); 58 | 59 | data.append(QString("%1;%2;%3;%4;%5;%6;%7;%8\n").arg(id).arg(QDateTime::fromTime_t(static_cast(reinterpret_cast(parent())->robo.cleanrecord.begin)).toString("dd.MM.yyyy")).arg(QDateTime::fromTime_t(static_cast(reinterpret_cast(parent())->robo.cleanrecord.begin)).toString("hh:mm:ss")).arg(QDateTime::fromTime_t(static_cast(reinterpret_cast(parent())->robo.cleanrecord.finish)).toString("hh:mm:ss")).arg(time.toString("hh:mm:ss")).arg(reinterpret_cast(parent())->robo.cleanrecord.area / 1000000.0).arg(error_strings.at(reinterpret_cast(parent())->robo.cleanrecord.error)).arg(reinterpret_cast(parent())->robo.cleanrecord.complete)); 60 | } 61 | else 62 | { 63 | QMessageBox::warning(this, APPNAME, "Communication error during export!"); 64 | 65 | break; 66 | } 67 | } 68 | 69 | QFile csv(QFileDialog::getSaveFileName(this, tr("Select file to export cleaning history"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.csv", nullptr, QFileDialog::DontUseNativeDialog)); 70 | 71 | if(!csv.fileName().isEmpty()) 72 | { 73 | if(csv.open(QIODevice::WriteOnly | QIODevice::Text)) 74 | { 75 | csv.write(data.toUtf8()); 76 | csv.close(); 77 | 78 | QMessageBox::information(this, APPNAME, tr("Cleaning history successfully exported.")); 79 | } 80 | else 81 | { 82 | QMessageBox::warning(this, APPNAME, tr("Could not open csv export!\n\n%1 : %2").arg(csv.fileName()).arg(csv.errorString())); 83 | } 84 | } 85 | } 86 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Close) 87 | { 88 | close(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /history.h: -------------------------------------------------------------------------------- 1 | #ifndef HISTORYDLG_H 2 | #define HISTORYDLG_H 3 | 4 | #include "ui_history.h" 5 | #include "mainwindow.h" 6 | 7 | class historyDialog : public QDialog, private Ui::Dialog_History 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | historyDialog(QWidget*); 14 | 15 | private: 16 | 17 | QStringList error_strings 18 | { 19 | tr("No error"), 20 | tr("Laser distance sensor error"), 21 | tr("Collision sensor error"), 22 | tr("Wheels on top of void, move robot"), 23 | tr("Clean hovering sensors, move robot"), 24 | tr("Clean main brush"), 25 | tr("Clean side brush"), 26 | tr("Main wheel stuck?"), 27 | tr("Device stuck, clean area"), 28 | tr("Dust collector missing"), 29 | tr("Clean filter"), 30 | tr("Stuck in magnetic barrier"), 31 | tr("Low battery"), 32 | tr("Charging fault"), 33 | tr("Battery fault"), 34 | tr("Wall sensors dirty, wipe them"), 35 | tr("Place me on flat surface"), 36 | tr("Side brushes problem, reboot me"), 37 | tr("Suction fan problem"), 38 | tr("Unpowered charging station") 39 | }; 40 | 41 | QDateTime record; 42 | 43 | private slots: 44 | 45 | void on_comboBox_currentIndexChanged(int); 46 | 47 | void on_buttonBox_clicked(QAbstractButton *button); 48 | }; 49 | 50 | #endif // HISTORYDLG_H 51 | -------------------------------------------------------------------------------- /installer.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTALLERDLG_H 2 | #define INSTALLERDLG_H 3 | 4 | #include "ui_installer.h" 5 | #include "mainwindow.h" 6 | 7 | #define VALETUDO_REL "https://api.github.com/repos/hypfer/valetudo/releases" 8 | #define VALETUDO_CFG "https://github.com/Hypfer/Valetudo/raw/master/deployment/valetudo.conf" 9 | #define VALETUDO_HOSTS "https://github.com/Hypfer/Valetudo/raw/master/deployment/etc/hosts" 10 | #define VALETUDO_RCLOCAL "https://github.com/Hypfer/Valetudo/raw/master/deployment/etc/rc.local" 11 | 12 | #define VALETUDORE_REL "https://api.github.com/repos/rand256/valetudo/releases" 13 | #define VALETUDORE_CFG "https://github.com/rand256/Valetudo/raw/testing/deployment/valetudo.conf" 14 | #define VALETUDORE_HOSTS "https://github.com/rand256/Valetudo/raw/testing/deployment/etc/hosts" 15 | #define VALETUDORE_RCLOCAL "https://github.com/rand256/Valetudo/raw/testing/deployment/etc/rc.local" 16 | 17 | #define VALETUDO_BIN_SRC QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/valetudo" 18 | #define VALETUDO_CFG_SRC QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/valetudo.conf" 19 | #define VALETUDO_HOSTS_SRC QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/hosts" 20 | #define VALETUDO_RCLOCAL_SRC QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/rc.local" 21 | 22 | #define VALETUDO_BIN_DST "/usr/local/bin/valetudo" 23 | #define VALETUDO_CFG_DST "/etc/init/valetudo.conf" 24 | #define VALETUDO_HOSTS_DST "/etc/hosts" 25 | #define VALETUDO_RCLOCAL_DST "/etc/rc.local" 26 | 27 | #define VALETUDO_CMD_CHMOD "chmod +x " VALETUDO_BIN_DST 28 | #define VALETUDO_CMD_STOP "%1 stop" 29 | #define VALETUDO_CMD_START "%1 start" 30 | 31 | #define HOSTS_INIT "### VALETUDO HOSTS INIT ###" 32 | #define HOSTS_EXIT "### VALETUDO HOSTS EXIT ###" 33 | #define RCLOCAL_INIT "### VALETUDO RC.LOCAL INIT ###" 34 | #define RCLOCAL_EXIT "### VALETUDO RC.LOCAL EXIT ###" 35 | 36 | enum {HOSTS, RCLOCAL}; 37 | 38 | class installerDialog : public QDialog, private Ui::Dialog_Installer 39 | { 40 | Q_OBJECT 41 | 42 | public: 43 | 44 | installerDialog(QWidget*); 45 | 46 | private: 47 | 48 | bool Download(QString); 49 | void mergeFiles(int); 50 | void updateProgress(); 51 | 52 | QNetworkReply *reply = nullptr; 53 | QByteArray download; 54 | QSshSocket *ssh = nullptr; 55 | bool finish, failed; 56 | int step; 57 | bool vre = false; 58 | 59 | private slots: 60 | 61 | void getReleases(); 62 | 63 | void downloadProgress(qint64, qint64); 64 | void finished(QNetworkReply*); 65 | 66 | void ssh_connected(); 67 | void ssh_disconnected(); 68 | void ssh_error(QSshSocket::SshError); 69 | void ssh_loginSuccessful(); 70 | void ssh_pullSuccessful(QString, QString); 71 | void ssh_pushSuccessful(QString, QString); 72 | void ssh_commandExecuted(QString, QString); 73 | 74 | void on_radioButton_valetudo_clicked(); 75 | void on_radioButton_valetudore_clicked(); 76 | 77 | void reject(); 78 | 79 | void on_buttonBox_clicked(QAbstractButton*); 80 | }; 81 | 82 | #endif // INSTALLERDLG_H 83 | -------------------------------------------------------------------------------- /lng/rrcc_de.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/lng/rrcc_de.qm -------------------------------------------------------------------------------- /logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | 3 | loggerDialog::loggerDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); 8 | 9 | connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint))); 10 | 11 | resize(QApplication::primaryScreen()->availableGeometry().width(), 0); 12 | move(0, 0); 13 | 14 | QTimer::singleShot(1, this, SLOT(calcHeight())); 15 | } 16 | 17 | void loggerDialog::calcHeight() 18 | { 19 | treeWidget->setMinimumHeight(10 * treeWidget->visualItemRect(treeWidget->topLevelItem(0)).height() + treeWidget->header()->height() + style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 2); 20 | } 21 | 22 | void loggerDialog::log(QString p1, QString p2, QString p3, QString p4, QString p5, QString p6) 23 | { 24 | static qlonglong counter = 0; 25 | 26 | QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << QString::number(++counter) << QDateTime::currentDateTime().toString("dd.MM hh:mm:ss") << p1 << p2 << p3 << p4 << p5 << (p6.isEmpty() ? "" : p6.insert(4, '-').insert(9, '-').insert(18, '-').insert(27, '-').insert(36, '-'))); 27 | 28 | item->setTextAlignment(0, Qt::AlignHCenter); 29 | item->setTextAlignment(1, Qt::AlignHCenter); 30 | item->setTextAlignment(2, Qt::AlignHCenter); 31 | item->setTextAlignment(3, Qt::AlignHCenter); 32 | item->setTextAlignment(4, Qt::AlignHCenter); 33 | item->setTextAlignment(5, Qt::AlignHCenter); 34 | 35 | treeWidget->addTopLevelItem(item); 36 | 37 | if(treeWidget->topLevelItemCount() == 1) 38 | { 39 | treeWidget->topLevelItem(treeWidget->topLevelItemCount() - 1)->setSelected(true); 40 | } 41 | 42 | treeWidget->resizeColumnToContents(0); 43 | treeWidget->resizeColumnToContents(1); 44 | treeWidget->resizeColumnToContents(2); 45 | treeWidget->resizeColumnToContents(3); 46 | treeWidget->resizeColumnToContents(4); 47 | treeWidget->resizeColumnToContents(5); 48 | treeWidget->resizeColumnToContents(6); 49 | treeWidget->resizeColumnToContents(7); 50 | 51 | treeWidget->scrollToBottom(); 52 | } 53 | 54 | void loggerDialog::on_customContextMenuRequested(QPoint pos) 55 | { 56 | QMenu menu(this); 57 | QAction action1(QIcon(":/png/png/clipboard.png"), tr("Copy all to clipboard"), this); 58 | QAction action2(QIcon(":/png/png/clipboard.png"), tr("Copy line to clipboard"), this); 59 | QAction action3(QIcon(":/png/png/clipboard.png"), tr("Copy field to clipboard"), this); 60 | QAction *selected; 61 | 62 | menu.addAction(&action1); 63 | menu.addAction(&action2); 64 | menu.addAction(&action3); 65 | 66 | selected = menu.exec(treeWidget->viewport()->mapToGlobal(pos)); 67 | 68 | if(selected) 69 | { 70 | QClipboard *clipboard = QGuiApplication::clipboard(); 71 | QTreeWidgetItem *item = treeWidget->itemAt(pos); 72 | 73 | if(selected->text() == action1.text()) 74 | { 75 | QString text; 76 | 77 | for(int i = 0; i < treeWidget->topLevelItemCount(); i++) 78 | { 79 | item = treeWidget->topLevelItem(i); 80 | 81 | text.append(QString("%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\n").arg(item->text(0)).arg(item->text(1)).arg(item->text(2)).arg(item->text(3)).arg(item->text(4)).arg(item->text(5)).arg(item->text(6)).arg(item->text(7))); 82 | } 83 | 84 | clipboard->setText(text); 85 | } 86 | else if(selected->text() == action2.text()) 87 | { 88 | clipboard->setText(QString("%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\n").arg(item->text(0)).arg(item->text(1)).arg(item->text(2)).arg(item->text(3)).arg(item->text(4)).arg(item->text(5)).arg(item->text(6)).arg(item->text(7))); 89 | } 90 | else if(selected->text() == action3.text()) 91 | { 92 | item = treeWidget->itemAt(pos); 93 | 94 | if(item) 95 | { 96 | int colums[7]; 97 | 98 | colums[0] = treeWidget->header()->sectionSize(0); 99 | colums[1] = colums[0] + treeWidget->header()->sectionSize(1); 100 | colums[2] = colums[1] + treeWidget->header()->sectionSize(2); 101 | colums[3] = colums[2] + treeWidget->header()->sectionSize(3); 102 | colums[4] = colums[3] + treeWidget->header()->sectionSize(4); 103 | colums[5] = colums[4] + treeWidget->header()->sectionSize(5); 104 | colums[6] = colums[5] + treeWidget->header()->sectionSize(6); 105 | 106 | if(pos.x() > colums[6]) 107 | { 108 | clipboard->setText(item->text(7)); 109 | } 110 | else if(pos.x() > colums[5]) 111 | { 112 | clipboard->setText(item->text(6)); 113 | } 114 | else if(pos.x() > colums[4]) 115 | { 116 | clipboard->setText(item->text(5)); 117 | } 118 | else if(pos.x() > colums[3]) 119 | { 120 | clipboard->setText(item->text(4)); 121 | } 122 | else if(pos.x() > colums[2]) 123 | { 124 | clipboard->setText(item->text(3)); 125 | } 126 | else if(pos.x() > colums[1]) 127 | { 128 | clipboard->setText(item->text(2)); 129 | } 130 | else if(pos.x() > colums[0]) 131 | { 132 | clipboard->setText(item->text(1)); 133 | } 134 | else 135 | { 136 | clipboard->setText(item->text(0)); 137 | } 138 | } 139 | } 140 | } 141 | } 142 | 143 | void loggerDialog::on_buttonBox_clicked(QAbstractButton *button) 144 | { 145 | if(buttonBox->standardButton(button) == QDialogButtonBox::Save) 146 | { 147 | QFile logfile(QFileDialog::getSaveFileName(this, tr("Select file to save log"), LOG, "*.*", nullptr, QFileDialog::DontUseNativeDialog)); 148 | 149 | if(!logfile.fileName().isEmpty()) 150 | { 151 | if(logfile.open(QIODevice::WriteOnly | QIODevice::Text)) 152 | { 153 | for(int i = 0; i < treeWidget->topLevelItemCount(); i++) 154 | { 155 | QTreeWidgetItem *item = treeWidget->topLevelItem(i); 156 | 157 | logfile.write(QString("%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\n").arg(item->text(0)).arg(item->text(1)).arg(item->text(2)).arg(item->text(3)).arg(item->text(4)).arg(item->text(5)).arg(item->text(6)).arg(item->text(7)).toUtf8()); 158 | } 159 | 160 | logfile.close(); 161 | } 162 | else 163 | { 164 | QMessageBox::warning(this, APPNAME, tr("Could not open logfile!\n\n%1 : %2").arg(logfile.fileName()).arg(logfile.errorString())); 165 | } 166 | } 167 | } 168 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Close) 169 | { 170 | close(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGERDLG_H 2 | #define LOGGERDLG_H 3 | 4 | #include "ui_logger.h" 5 | #include "mainwindow.h" 6 | 7 | class loggerDialog : public QDialog, private Ui::Dialog_Logger 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | loggerDialog(QWidget*); 14 | 15 | void log(QString, QString, QString, QString, QString, QString); 16 | 17 | private slots: 18 | 19 | void calcHeight(); 20 | 21 | void on_customContextMenuRequested(QPoint); 22 | 23 | void on_buttonBox_clicked(QAbstractButton*); 24 | }; 25 | 26 | #endif // LOGGERDLG_H 27 | -------------------------------------------------------------------------------- /onlineupd.cpp: -------------------------------------------------------------------------------- 1 | #include "onlineupd.h" 2 | 3 | onlineUpdDialog::onlineUpdDialog(QWidget *parent, bool mode) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | silent = mode; 11 | 12 | textEdit->setFixedHeight(5 * textEdit->fontMetrics().height() + static_cast(textEdit->document()->documentMargin()) + 2); 13 | 14 | buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Ignore")); 15 | buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Update")); 16 | 17 | label_icon->setPixmap(QPixmap(IMG)); 18 | 19 | QTimer::singleShot(1, this, SLOT(checkUpdate())); 20 | } 21 | 22 | void onlineUpdDialog::checkUpdate() 23 | { 24 | QElapsedTimer timeout; 25 | netmgr = new QNetworkAccessManager(this); 26 | request = QNetworkRequest(QUrl(RELEASE)); 27 | 28 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); 29 | 30 | reply = netmgr->get(request); 31 | 32 | reply->ignoreSslErrors(); 33 | 34 | timeout.start(); 35 | 36 | while(reply->isRunning()) 37 | { 38 | QCoreApplication::processEvents(); 39 | 40 | if(timeout.hasExpired(5000)) 41 | { 42 | reply->abort(); 43 | } 44 | } 45 | 46 | if(!reply->error()) 47 | { 48 | QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); 49 | QJsonObject obj = doc.object(); 50 | QJsonArray arr; 51 | QString version, date, changelog; 52 | 53 | if(obj.contains("tag_name")) 54 | { 55 | version = obj.value("tag_name").toString(); 56 | } 57 | 58 | if(obj.contains("published_at")) 59 | { 60 | date = obj.value("published_at").toString(); 61 | } 62 | 63 | if(obj.contains("body")) 64 | { 65 | changelog = obj.value("body").toString(); 66 | } 67 | 68 | if(obj.contains("assets")) 69 | { 70 | arr = obj.value("assets").toArray(); 71 | 72 | for(int i = 0; i < arr.count(); i++) 73 | { 74 | if(arr[i].toObject().contains("size")) 75 | { 76 | size = arr[i].toObject().value("size").toInt(); 77 | } 78 | 79 | if(arr[i].toObject().contains("browser_download_url")) 80 | { 81 | url = arr[i].toObject().value("browser_download_url").toString(); 82 | 83 | if(url.endsWith(BIN)) 84 | { 85 | break; 86 | } 87 | } 88 | } 89 | } 90 | 91 | if(QVersionNumber::compare(QVersionNumber::fromString(version), QVersionNumber::fromString(APPVERS)) == 1) 92 | { 93 | label_installed->setText(QString("%1 [ %2 ]").arg(APPVERS).arg(APPDATE)); 94 | label_available->setText(QString("%1 [ %2 ]").arg(version).arg(QDate::fromString(date.left(10), "yyyy-MM-dd").toString("dd.MM.yyyy"))); 95 | 96 | textEdit->setText(changelog); 97 | 98 | progressBar->setFormat(QString("%p% [ 0 / %1 ]").arg(size)); 99 | 100 | show(); 101 | } 102 | else 103 | { 104 | if(!silent) 105 | { 106 | QMessageBox::information(reinterpret_cast(parent()), APPNAME, tr("No new version found.")); 107 | } 108 | 109 | close(); 110 | } 111 | } 112 | else 113 | { 114 | if(!silent) 115 | { 116 | QMessageBox::warning(reinterpret_cast(parent()), APPNAME, tr("Online update check failed!\n\n%1").arg(reply->errorString())); 117 | } 118 | 119 | close(); 120 | } 121 | } 122 | 123 | void onlineUpdDialog::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) 124 | { 125 | if(bytesReceived && bytesTotal) 126 | { 127 | progressBar->setValue(static_cast((100 * bytesReceived) / bytesTotal)); 128 | progressBar->setFormat(QString("%p% [ %1 / %2 ]").arg(bytesReceived).arg(bytesTotal)); 129 | } 130 | } 131 | 132 | void onlineUpdDialog::finished(QNetworkReply *reply) 133 | { 134 | if(reply->error()) 135 | { 136 | QMessageBox::warning(this, APPNAME, tr("Download from Github failed!\n\n%1").arg(reply->errorString())); 137 | } 138 | else 139 | { 140 | download = reply->readAll(); 141 | 142 | QFile file(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + url.split('/').last()); 143 | 144 | if(file.open(QIODevice::WriteOnly)) 145 | { 146 | file.write(download); 147 | #ifdef Q_OS_LINUX 148 | file.setPermissions(file.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); 149 | #endif 150 | file.close(); 151 | 152 | if(QMessageBox::question(this, APPNAME, tr("Download successfully saved to %1.\n\nRun new version now?").arg(file.fileName()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) 153 | { 154 | #ifdef Q_OS_OSX 155 | if(QProcess::startDetached("open", {file.fileName()})) 156 | #else 157 | if(QProcess::startDetached(file.fileName())) 158 | #endif 159 | { 160 | reinterpret_cast(parent())->forceclose = true; 161 | 162 | reinterpret_cast(parent())->close(); 163 | } 164 | else 165 | { 166 | QMessageBox::warning(this, APPNAME, tr("Could not run new version!")); 167 | } 168 | } 169 | } 170 | else 171 | { 172 | QMessageBox::warning(this, APPNAME, tr("Could not save download to %1!\n\n%2").arg(file.fileName()).arg(file.errorString())); 173 | } 174 | } 175 | 176 | close(); 177 | } 178 | 179 | void onlineUpdDialog::reject() 180 | { 181 | if(reply->isRunning()) 182 | { 183 | if(QMessageBox::question(this, APPNAME, tr("Really abort download?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) 184 | { 185 | reply->abort(); 186 | } 187 | else 188 | { 189 | return; 190 | } 191 | } 192 | 193 | accept(); 194 | } 195 | 196 | void onlineUpdDialog::on_buttonBox_clicked(QAbstractButton *button) 197 | { 198 | if(buttonBox->standardButton(button) == QDialogButtonBox::Cancel) 199 | { 200 | close(); 201 | } 202 | else 203 | { 204 | netmgr = new QNetworkAccessManager(this); 205 | request = QNetworkRequest(url); 206 | 207 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); 208 | 209 | reply = netmgr->get(request); 210 | 211 | reply->ignoreSslErrors(); 212 | 213 | connect(netmgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*))); 214 | connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); 215 | 216 | buttonBox->button(QDialogButtonBox::Apply)->setDisabled(true); 217 | buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /onlineupd.h: -------------------------------------------------------------------------------- 1 | #ifndef ONLINEUPDDLG_H 2 | #define ONLINEUPDDLG_H 3 | 4 | #include "ui_onlineupd.h" 5 | #include "mainwindow.h" 6 | 7 | #ifdef Q_OS_LINUX 8 | #define BIN ".AppImage" 9 | #define IMG ":/png/png/upd_lin.png" 10 | #elif defined Q_OS_WIN 11 | #define BIN ".exe" 12 | #define IMG ":/png/png/upd_win.png" 13 | #elif defined Q_OS_OSX 14 | #define BIN ".dmg" 15 | #define IMG ":/png/png/upd_mac.png" 16 | #endif 17 | 18 | #define RELEASE "https://api.github.com/repos/lazyt/rrcc/releases/latest" 19 | 20 | class onlineUpdDialog : public QDialog, private Ui::Dialog_OnlineUpdate 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | 26 | onlineUpdDialog(QWidget*, bool); 27 | 28 | private: 29 | 30 | bool silent; 31 | 32 | QNetworkAccessManager *netmgr; 33 | QNetworkRequest request; 34 | QNetworkReply *reply; 35 | QByteArray download; 36 | QString url; 37 | int size; 38 | 39 | private slots: 40 | 41 | void checkUpdate(); 42 | void downloadProgress(qint64, qint64); 43 | void finished(QNetworkReply*); 44 | void reject(); 45 | void on_buttonBox_clicked(QAbstractButton*); 46 | }; 47 | 48 | #endif // ONLINEUPDDLG_H 49 | -------------------------------------------------------------------------------- /packager.cpp: -------------------------------------------------------------------------------- 1 | #include "packager.h" 2 | 3 | packagerDialog::packagerDialog(QWidget *parent, QFile *file) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | connect(&timer, SIGNAL(timeout()), this, SLOT(timer_refreshTime())); 11 | 12 | dir = file; 13 | 14 | finished = false; 15 | canceled = false; 16 | 17 | time.start(); 18 | timer.start(500); 19 | 20 | QTimer::singleShot(100, this, SLOT(startPackaging())); 21 | } 22 | 23 | void packagerDialog::startPackaging() 24 | { 25 | QFileInfoList entries; 26 | QStringList files; 27 | QFile file; 28 | QByteArray tgz; 29 | QByteArray pkg; 30 | ccrypt_stream_t ccrypt; 31 | int index = 0; 32 | short err_code; 33 | QString err_msg; 34 | 35 | entries = QDir(dir->fileName(), "*.wav", QDir::Name, QDir::Files).entryInfoList(); 36 | 37 | foreach(QFileInfo fi, entries) 38 | { 39 | files.append(fi.canonicalFilePath()); 40 | } 41 | 42 | file.setFileName(TGZ); 43 | file.remove(); 44 | 45 | compressor 46 | .setArchive(file.fileName(), files) 47 | .setArchiveFormat(QArchive::GZIP) 48 | .setFunc(QArchive::COMPRESSED, [&](__attribute__ ((unused)) QString file) 49 | { 50 | label_file->setText(entries.at(index).fileName()); 51 | label_count->setText(QString("%1 / %2").arg(1 + index).arg(entries.count())); 52 | label_size->setText(QString("%1 Byte").arg(entries.at(index++).size())); 53 | }) 54 | .setFunc([&](int prog) 55 | { 56 | progressBar_compress->setValue(prog); 57 | }) 58 | .setFunc(QArchive::FINISHED, [&]() 59 | { 60 | finished = true; 61 | }) 62 | .setFunc(QArchive::CANCELED, [&]() 63 | { 64 | canceled = true; 65 | }) 66 | .setFunc([&](short errorCode, QString eMsg) 67 | { 68 | canceled = true; 69 | finished = true; 70 | err_code = errorCode; 71 | err_msg = eMsg; 72 | }) 73 | .start(); 74 | 75 | while(!finished && !canceled) 76 | { 77 | QThread::msleep(10); 78 | QCoreApplication::processEvents(); 79 | } 80 | 81 | if(canceled) 82 | { 83 | timer.stop(); 84 | 85 | if(!finished) 86 | { 87 | QMessageBox::warning(this, APPNAME, tr("Packaging aborted by user!")); 88 | } 89 | else 90 | { 91 | QMessageBox::critical(this, APPNAME, tr("Packaging error!\n\n%1\n\n%2").arg(reinterpret_cast(parent())->qarchive_error_strings.at(err_code)).arg(err_msg)); 92 | } 93 | 94 | close(); 95 | 96 | return; 97 | } 98 | 99 | if(file.open(QIODevice::ReadOnly) && file.size()) 100 | { 101 | tgz = file.readAll(); 102 | 103 | file.close(); 104 | } 105 | else 106 | { 107 | QMessageBox::warning(this, APPNAME, tr("Creating voice package failed!")); 108 | 109 | return; 110 | } 111 | 112 | pkg.resize(32 + tgz.size()); 113 | 114 | ccrypt.next_in = tgz.data(); 115 | ccrypt.avail_in = static_cast(tgz.size()); 116 | ccrypt.next_out = pkg.data(); 117 | ccrypt.avail_out = static_cast(pkg.size()); 118 | 119 | progressBar_encrypt->setValue(50); 120 | 121 | ccencrypt_init(&ccrypt, PKG_KEY); 122 | ccencrypt(&ccrypt); 123 | ccencrypt_end(&ccrypt); 124 | 125 | progressBar_encrypt->setValue(100); 126 | 127 | QCoreApplication::processEvents(); 128 | 129 | timer.stop(); 130 | 131 | file.setFileName(PKG); 132 | file.remove(); 133 | 134 | if(file.open(QIODevice::WriteOnly) && pkg.size() > 32) 135 | { 136 | file.write(pkg); 137 | 138 | file.close(); 139 | 140 | if(QMessageBox::question(this, APPNAME, tr("Install created voice package now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 141 | { 142 | hide(); 143 | 144 | file.open(QIODevice::ReadOnly); 145 | 146 | uploadDialog(reinterpret_cast(parent()), &file).exec(); 147 | } 148 | else 149 | { 150 | QFile save(QFileDialog::getSaveFileName(this, tr("Select file to save voice package"), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/rrcc-voice.pkg", "*.pkg", nullptr, QFileDialog::DontUseNativeDialog)); 151 | 152 | if(!save.fileName().isEmpty()) 153 | { 154 | if(save.exists()) 155 | { 156 | save.remove(); 157 | } 158 | 159 | file.copy(save.fileName()); 160 | } 161 | } 162 | } 163 | else 164 | { 165 | QMessageBox::warning(this, APPNAME, tr("Crypting voice file failed!")); 166 | } 167 | 168 | close(); 169 | } 170 | 171 | void packagerDialog::timer_refreshTime() 172 | { 173 | QTime elapsed = QTime::fromMSecsSinceStartOfDay(time.elapsed()); 174 | 175 | label_time->setText(QString("%1h:%2m:%3s").arg(elapsed.hour(), 2, 10, QChar('0')).arg(elapsed.minute(), 2, 10, QChar('0')).arg(elapsed.second(), 2, 10, QChar('0'))); 176 | } 177 | 178 | void packagerDialog::reject() 179 | { 180 | if(!finished && !canceled) 181 | { 182 | if(QMessageBox::question(this, APPNAME, tr("Really abort packaging?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 183 | { 184 | compressor.cancel(); 185 | } 186 | } 187 | else 188 | { 189 | accept(); 190 | } 191 | } 192 | 193 | void packagerDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 194 | { 195 | close(); 196 | } 197 | -------------------------------------------------------------------------------- /packager.h: -------------------------------------------------------------------------------- 1 | #ifndef PACKAGERDLG_H 2 | #define PACKAGERDLG_H 3 | 4 | #include "ui_packager.h" 5 | #include "mainwindow.h" 6 | 7 | class packagerDialog : public QDialog, private Ui::Dialog_Packager 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | packagerDialog(QWidget*, QFile*); 14 | 15 | private: 16 | 17 | QArchive::Compressor compressor; 18 | QTime time; 19 | QTimer timer; 20 | QFile *dir; 21 | bool finished; 22 | bool canceled; 23 | 24 | private slots: 25 | 26 | void startPackaging(); 27 | void timer_refreshTime(); 28 | 29 | void on_buttonBox_clicked(QAbstractButton*); 30 | 31 | void reject(); 32 | }; 33 | 34 | #endif // PACKAGERDLG_H 35 | -------------------------------------------------------------------------------- /res/ico/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/ico/app.icns -------------------------------------------------------------------------------- /res/ico/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/ico/app.ico -------------------------------------------------------------------------------- /res/ico/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/ico/app.png -------------------------------------------------------------------------------- /res/png/big_charger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_charger.png -------------------------------------------------------------------------------- /res/png/big_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_pause.png -------------------------------------------------------------------------------- /res/png/big_speak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_speak.png -------------------------------------------------------------------------------- /res/png/big_spot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_spot.png -------------------------------------------------------------------------------- /res/png/big_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_start.png -------------------------------------------------------------------------------- /res/png/big_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/big_stop.png -------------------------------------------------------------------------------- /res/png/brush-m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/brush-m.png -------------------------------------------------------------------------------- /res/png/brush-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/brush-s.png -------------------------------------------------------------------------------- /res/png/canceled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/canceled.png -------------------------------------------------------------------------------- /res/png/clipboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/clipboard.png -------------------------------------------------------------------------------- /res/png/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/clock.png -------------------------------------------------------------------------------- /res/png/communication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/communication.png -------------------------------------------------------------------------------- /res/png/compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/compass.png -------------------------------------------------------------------------------- /res/png/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/complete.png -------------------------------------------------------------------------------- /res/png/compress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/compress.png -------------------------------------------------------------------------------- /res/png/converter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/converter.png -------------------------------------------------------------------------------- /res/png/counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/counter.png -------------------------------------------------------------------------------- /res/png/decompress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/decompress.png -------------------------------------------------------------------------------- /res/png/dock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/dock.png -------------------------------------------------------------------------------- /res/png/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/exit.png -------------------------------------------------------------------------------- /res/png/fan_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/fan_1.png -------------------------------------------------------------------------------- /res/png/fan_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/fan_2.png -------------------------------------------------------------------------------- /res/png/fan_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/fan_3.png -------------------------------------------------------------------------------- /res/png/fan_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/fan_4.png -------------------------------------------------------------------------------- /res/png/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/filter.png -------------------------------------------------------------------------------- /res/png/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/flag.png -------------------------------------------------------------------------------- /res/png/flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/flip.png -------------------------------------------------------------------------------- /res/png/fw_upd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/fw_upd.png -------------------------------------------------------------------------------- /res/png/gift-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/gift-48.png -------------------------------------------------------------------------------- /res/png/gift-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/gift-64.png -------------------------------------------------------------------------------- /res/png/gift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/gift.png -------------------------------------------------------------------------------- /res/png/goto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/goto.png -------------------------------------------------------------------------------- /res/png/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/help.png -------------------------------------------------------------------------------- /res/png/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/history.png -------------------------------------------------------------------------------- /res/png/home-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/home-48.png -------------------------------------------------------------------------------- /res/png/home-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/home-64.png -------------------------------------------------------------------------------- /res/png/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/info.png -------------------------------------------------------------------------------- /res/png/internet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/internet.png -------------------------------------------------------------------------------- /res/png/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/key.png -------------------------------------------------------------------------------- /res/png/logger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/logger.png -------------------------------------------------------------------------------- /res/png/mail-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/mail-48.png -------------------------------------------------------------------------------- /res/png/mail-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/mail-64.png -------------------------------------------------------------------------------- /res/png/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/map.png -------------------------------------------------------------------------------- /res/png/misc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/misc.png -------------------------------------------------------------------------------- /res/png/msgid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/msgid.png -------------------------------------------------------------------------------- /res/png/mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/mute.png -------------------------------------------------------------------------------- /res/png/network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/network.png -------------------------------------------------------------------------------- /res/png/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/open.png -------------------------------------------------------------------------------- /res/png/padlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/padlock.png -------------------------------------------------------------------------------- /res/png/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/pin.png -------------------------------------------------------------------------------- /res/png/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/plugin.png -------------------------------------------------------------------------------- /res/png/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/question.png -------------------------------------------------------------------------------- /res/png/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/refresh.png -------------------------------------------------------------------------------- /res/png/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/reset.png -------------------------------------------------------------------------------- /res/png/robo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/robo.png -------------------------------------------------------------------------------- /res/png/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/rotate.png -------------------------------------------------------------------------------- /res/png/safe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/safe.png -------------------------------------------------------------------------------- /res/png/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/save.png -------------------------------------------------------------------------------- /res/png/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/search.png -------------------------------------------------------------------------------- /res/png/sensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/sensor.png -------------------------------------------------------------------------------- /res/png/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/setup.png -------------------------------------------------------------------------------- /res/png/sound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/sound.png -------------------------------------------------------------------------------- /res/png/ssh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/ssh.png -------------------------------------------------------------------------------- /res/png/ssid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/ssid.png -------------------------------------------------------------------------------- /res/png/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/start.png -------------------------------------------------------------------------------- /res/png/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/stop.png -------------------------------------------------------------------------------- /res/png/swap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/swap.png -------------------------------------------------------------------------------- /res/png/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/timer.png -------------------------------------------------------------------------------- /res/png/upd_lin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/upd_lin.png -------------------------------------------------------------------------------- /res/png/upd_mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/upd_mac.png -------------------------------------------------------------------------------- /res/png/upd_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/upd_win.png -------------------------------------------------------------------------------- /res/png/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/update.png -------------------------------------------------------------------------------- /res/png/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/user.png -------------------------------------------------------------------------------- /res/png/voice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/voice.png -------------------------------------------------------------------------------- /res/png/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/volume.png -------------------------------------------------------------------------------- /res/png/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/warning.png -------------------------------------------------------------------------------- /res/png/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/wifi.png -------------------------------------------------------------------------------- /res/png/zone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/zone.png -------------------------------------------------------------------------------- /res/png/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LazyT/rrcc/35cbd4f02082b2dc6f95bb8ecce4c872116c5133/res/png/zoom.png -------------------------------------------------------------------------------- /res/rrcc.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ico/app.ico 4 | 5 | 6 | png/brush-m.png 7 | png/brush-s.png 8 | png/clock.png 9 | png/compass.png 10 | png/counter.png 11 | png/exit.png 12 | png/filter.png 13 | png/help.png 14 | png/history.png 15 | png/info.png 16 | png/reset.png 17 | png/sensor.png 18 | png/big_charger.png 19 | png/big_start.png 20 | png/big_spot.png 21 | png/big_pause.png 22 | png/big_stop.png 23 | png/big_speak.png 24 | png/timer.png 25 | png/fan_1.png 26 | png/fan_2.png 27 | png/fan_3.png 28 | png/fan_4.png 29 | png/padlock.png 30 | png/save.png 31 | png/search.png 32 | png/network.png 33 | png/wifi.png 34 | png/ssid.png 35 | png/key.png 36 | png/communication.png 37 | png/setup.png 38 | png/logger.png 39 | png/msgid.png 40 | png/start.png 41 | png/stop.png 42 | png/canceled.png 43 | png/complete.png 44 | png/warning.png 45 | png/clipboard.png 46 | png/refresh.png 47 | png/sound.png 48 | png/volume.png 49 | png/voice.png 50 | png/mute.png 51 | png/open.png 52 | png/compress.png 53 | png/decompress.png 54 | png/converter.png 55 | png/ssh.png 56 | png/user.png 57 | png/safe.png 58 | png/pin.png 59 | png/gift.png 60 | png/mail-48.png 61 | png/mail-64.png 62 | png/home-48.png 63 | png/home-64.png 64 | png/gift-48.png 65 | png/gift-64.png 66 | png/map.png 67 | png/misc.png 68 | png/robo.png 69 | png/dock.png 70 | png/goto.png 71 | png/zoom.png 72 | png/zone.png 73 | png/rotate.png 74 | png/flip.png 75 | png/swap.png 76 | png/question.png 77 | png/fw_upd.png 78 | png/internet.png 79 | png/flag.png 80 | png/upd_win.png 81 | png/upd_lin.png 82 | png/upd_mac.png 83 | png/update.png 84 | png/plugin.png 85 | 86 | 87 | -------------------------------------------------------------------------------- /res/ui/converter.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Converter 4 | 5 | 6 | 7 | 0 8 | 0 9 | 692 10 | 176 11 | 12 | 13 | 14 | Token Converter 15 | 16 | 17 | 18 | :/png/png/converter.png:/png/png/converter.png 19 | 20 | 21 | 22 | 23 | 24 | Paste your 96 / 16 Byte Token to convert into 32 Byte Token 25 | 26 | 27 | 28 | 29 | 30 | 31 | 650 32 | 0 33 | 34 | 35 | 36 | 96 Byte Token 37 | 38 | 39 | 40 | 41 | 42 | 96 43 | 44 | 45 | Qt::AlignCenter 46 | 47 | 48 | 49 | 50 | 51 | 52 | 16 Byte Token 53 | 54 | 55 | 16 56 | 57 | 58 | Qt::AlignCenter 59 | 60 | 61 | 62 | 63 | 64 | 65 | Qt::Horizontal 66 | 67 | 68 | 69 | 70 | 71 | 72 | 32 Byte Token 73 | 74 | 75 | HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH 76 | 77 | 78 | 79 | 80 | 81 | 32 82 | 83 | 84 | Qt::AlignCenter 85 | 86 | 87 | true 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | QDialogButtonBox::Close 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /res/ui/download.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Download 4 | 5 | 6 | 7 | 0 8 | 0 9 | 288 10 | 182 11 | 12 | 13 | 14 | Download Firmware 15 | 16 | 17 | 18 | :/png/png/save.png:/png/png/save.png 19 | 20 | 21 | 22 | 23 | 24 | Download Parameter 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Select Model 33 | 34 | 35 | 0 36 | 37 | 38 | QComboBox::AdjustToContents 39 | 40 | 41 | 42 | Generation 1 43 | 44 | 45 | 46 | 47 | Generation 2 48 | 49 | 50 | 51 | 52 | Generation 3 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Enter Firmware 61 | 62 | 63 | v11_00????.fullos.pkg 64 | 65 | 66 | Qt::AlignCenter 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Select Server 76 | 77 | 78 | 79 | cdn.awsbj0.fds.api.mi-img.com 80 | 81 | 82 | 83 | 84 | cdn.awsde0.fds.api.mi-img.com 85 | 86 | 87 | 88 | 89 | cdn.cnbj0.fds.api.mi-img.com 90 | 91 | 92 | 93 | 94 | cdn.cnbj2.fds.api.mi-img.com 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Download Progress 103 | 104 | 105 | 0 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | QDialogButtonBox::Cancel|QDialogButtonBox::Save 116 | 117 | 118 | 119 | 120 | 121 | 122 | comboBox_model 123 | lineEdit 124 | comboBox_server 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /res/ui/history.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_History 4 | 5 | 6 | 7 | 0 8 | 0 9 | 173 10 | 343 11 | 12 | 13 | 14 | Cleaning History 15 | 16 | 17 | 18 | :/png/png/history.png:/png/png/history.png 19 | 20 | 21 | 22 | 23 | 24 | Details 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | :/png/png/canceled.png 36 | 37 | 38 | 39 | 40 | 41 | 42 | Select Cleaning Record 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Qt::Horizontal 52 | 53 | 54 | 55 | 56 | 57 | 58 | 5 59 | 60 | 61 | 5 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | :/png/png/start.png 70 | 71 | 72 | 73 | 74 | 75 | 76 | Starting Time 77 | 78 | 79 | 8 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | :/png/png/stop.png 90 | 91 | 92 | 93 | 94 | 95 | 96 | Ending Time 97 | 98 | 99 | 8 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | :/png/png/clock.png 110 | 111 | 112 | 113 | 114 | 115 | 116 | Cleaning Duration 117 | 118 | 119 | 8 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | :/png/png/compass.png 130 | 131 | 132 | 133 | 134 | 135 | 136 | Cleaned Area 137 | 138 | 139 | 8 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | Qt::Horizontal 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | :/png/png/warning.png 161 | 162 | 163 | 164 | 165 | 166 | 167 | Cleaning Result 168 | 169 | 170 | QFrame::StyledPanel 171 | 172 | 173 | ? 174 | 175 | 176 | Qt::AlignCenter 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | QDialogButtonBox::Close|QDialogButtonBox::Save 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /res/ui/logger.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Logger 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1088 10 | 159 11 | 12 | 13 | 14 | Logger 15 | 16 | 17 | 18 | :/png/png/logger.png:/png/png/logger.png 19 | 20 | 21 | 22 | 23 | 24 | Network Analyzer 25 | 26 | 27 | 28 | 29 | 30 | Qt::CustomContextMenu 31 | 32 | 33 | true 34 | 35 | 36 | false 37 | 38 | 39 | 0 40 | 41 | 42 | 43 | Packet 44 | 45 | 46 | AlignCenter 47 | 48 | 49 | 50 | 51 | Timestamp 52 | 53 | 54 | AlignCenter 55 | 56 | 57 | 58 | 59 | Source 60 | 61 | 62 | AlignCenter 63 | 64 | 65 | 66 | 67 | Destination 68 | 69 | 70 | AlignCenter 71 | 72 | 73 | 74 | 75 | Bytes 76 | 77 | 78 | AlignCenter 79 | 80 | 81 | 82 | 83 | Delay 84 | 85 | 86 | AlignCenter 87 | 88 | 89 | 90 | 91 | Message 92 | 93 | 94 | 95 | 96 | Payload 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | QDialogButtonBox::Close|QDialogButtonBox::Save 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /res/ui/onlineupd.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_OnlineUpdate 4 | 5 | 6 | 7 | 0 8 | 0 9 | 283 10 | 339 11 | 12 | 13 | 14 | Online Update 15 | 16 | 17 | 18 | :/png/png/update.png:/png/png/update.png 19 | 20 | 21 | 22 | 23 | 24 | Version 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | :/png/png/upd_lin.png 34 | 35 | 36 | 37 | 38 | 39 | 40 | 0 41 | 42 | 43 | 44 | 45 | : 46 | 47 | 48 | 49 | 50 | 51 | 52 | Installed 53 | 54 | 55 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 56 | 57 | 58 | 59 | 60 | 61 | 62 | : 63 | 64 | 65 | 66 | 67 | 68 | 69 | ?.?.? [ ??.??.???? ] 70 | 71 | 72 | 73 | 74 | 75 | 76 | ?.?.? [ ??.??.???? ] 77 | 78 | 79 | 80 | 81 | 82 | 83 | Available 84 | 85 | 86 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Changes 99 | 100 | 101 | 102 | 103 | 104 | Changelog compared to last Version 105 | 106 | 107 | true 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Download 118 | 119 | 120 | 121 | 122 | 123 | Download Progress 124 | 125 | 126 | 0 127 | 128 | 129 | %p% [ ? / ? ] 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | QDialogButtonBox::Apply|QDialogButtonBox::Cancel 140 | 141 | 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /res/ui/packager.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Packager 4 | 5 | 6 | 7 | 0 8 | 0 9 | 292 10 | 185 11 | 12 | 13 | 14 | Voice Packager 15 | 16 | 17 | 18 | :/png/png/compress.png:/png/png/compress.png 19 | 20 | 21 | 22 | 23 | 24 | Progress 25 | 26 | 27 | 28 | 29 | 30 | 31 | 250 32 | 0 33 | 34 | 35 | 36 | Filename 37 | 38 | 39 | ? 40 | 41 | 42 | Qt::AlignCenter 43 | 44 | 45 | 46 | 47 | 48 | 49 | Compression Status 50 | 51 | 52 | 0 53 | 54 | 55 | 56 | 57 | 58 | 59 | Encryption Status 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 5 70 | 71 | 72 | 73 | 74 | Filecount 75 | 76 | 77 | 0 / 0 78 | 79 | 80 | 81 | 82 | 83 | 84 | Filesize 85 | 86 | 87 | 0 Byte 88 | 89 | 90 | Qt::AlignCenter 91 | 92 | 93 | 94 | 95 | 96 | 97 | Elapsed Time 98 | 99 | 100 | 00h:00m:00s 101 | 102 | 103 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | QDialogButtonBox::Cancel 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /res/ui/search.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Search 4 | 5 | 6 | 7 | 0 8 | 0 9 | 381 10 | 261 11 | 12 | 13 | 14 | Search Firmware 15 | 16 | 17 | 18 | :/png/png/internet.png:/png/png/internet.png 19 | 20 | 21 | 22 | 23 | 24 | Search Parameter 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Enter Filename (???? will be replaced with version) 33 | 34 | 35 | v11_00????.fullos.pkg 36 | 37 | 38 | Qt::AlignCenter 39 | 40 | 41 | 42 | 43 | 44 | 45 | Select Server 46 | 47 | 48 | 49 | cdn.awsbj0.fds.api.mi-img.com 50 | 51 | 52 | 53 | 54 | cdn.awsde0.fds.api.mi-img.com 55 | 56 | 57 | 58 | 59 | cdn.cnbj0.fds.api.mi-img.com 60 | 61 | 62 | 63 | 64 | cdn.cnbj2.fds.api.mi-img.com 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Select Model 73 | 74 | 75 | 0 76 | 77 | 78 | QComboBox::AdjustToContents 79 | 80 | 81 | 82 | Generation 1 83 | 84 | 85 | 86 | 87 | Generation 2 88 | 89 | 90 | 91 | 92 | Generation 3 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Start Version 103 | 104 | 105 | Qt::AlignCenter 106 | 107 | 108 | 9999 109 | 110 | 111 | 112 | 113 | 114 | 115 | Stop Version 116 | 117 | 118 | Qt::AlignCenter 119 | 120 | 121 | 9999 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | Search Progress 133 | 134 | 135 | 0 136 | 137 | 138 | 0000 | 0 / 0 | %p% 139 | 140 | 141 | 142 | 143 | 144 | 145 | Doubleclick to download selected firmware 146 | 147 | 148 | QAbstractScrollArea::AdjustToContentsOnFirstShow 149 | 150 | 151 | true 152 | 153 | 154 | false 155 | 156 | 157 | true 158 | 159 | 160 | 0 161 | 162 | 163 | 164 | Date 165 | 166 | 167 | AlignCenter 168 | 169 | 170 | 171 | 172 | Time 173 | 174 | 175 | AlignCenter 176 | 177 | 178 | 179 | 180 | Size 181 | 182 | 183 | AlignCenter 184 | 185 | 186 | 187 | 188 | Firmware 189 | 190 | 191 | AlignCenter 192 | 193 | 194 | 195 | 196 | 0000-00-00 197 | 198 | 199 | AlignCenter 200 | 201 | 202 | 00:00:00 203 | 204 | 205 | AlignCenter 206 | 207 | 208 | 000000000 209 | 210 | 211 | AlignCenter 212 | 213 | 214 | v11_000000.fullos.pkg 215 | 216 | 217 | AlignCenter 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | QDialogButtonBox::Apply|QDialogButtonBox::Cancel 229 | 230 | 231 | 232 | 233 | 234 | 235 | comboBox_model 236 | comboBox_server 237 | spinBox_Start 238 | spinBox_Stop 239 | lineEdit 240 | treeWidget 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /res/ui/unpackager.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Unpackager 4 | 5 | 6 | 7 | 0 8 | 0 9 | 292 10 | 185 11 | 12 | 13 | 14 | Voice Unpackager 15 | 16 | 17 | 18 | :/png/png/decompress.png:/png/png/decompress.png 19 | 20 | 21 | 22 | 23 | 24 | Progress 25 | 26 | 27 | 28 | 29 | 30 | 31 | 250 32 | 0 33 | 34 | 35 | 36 | Filename 37 | 38 | 39 | ? 40 | 41 | 42 | Qt::AlignCenter 43 | 44 | 45 | 46 | 47 | 48 | 49 | Decryption Status 50 | 51 | 52 | 0 53 | 54 | 55 | 56 | 57 | 58 | 59 | Decompression Status 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 5 70 | 71 | 72 | 73 | 74 | Filecount 75 | 76 | 77 | 0 78 | 79 | 80 | 81 | 82 | 83 | 84 | Filesize 85 | 86 | 87 | 0 Byte 88 | 89 | 90 | Qt::AlignCenter 91 | 92 | 93 | 94 | 95 | 96 | 97 | Elapsed Time 98 | 99 | 100 | 00h:00m:00s 101 | 102 | 103 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | QDialogButtonBox::Cancel 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /res/ui/update.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Update 4 | 5 | 6 | 7 | 0 8 | 0 9 | 292 10 | 228 11 | 12 | 13 | 14 | Firmware Update 15 | 16 | 17 | 18 | :/png/png/parcel.png:/png/png/parcel.png 19 | 20 | 21 | 22 | 23 | 24 | Progress 25 | 26 | 27 | 28 | 29 | 30 | 31 | 250 32 | 0 33 | 34 | 35 | 36 | Filename 37 | 38 | 39 | ? 40 | 41 | 42 | Qt::AlignCenter 43 | 44 | 45 | 46 | 47 | 48 | 49 | Upload Status 50 | 51 | 52 | 0 53 | 54 | 55 | 56 | 57 | 58 | 59 | Install Status 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 5 70 | 71 | 72 | 73 | 74 | Transfered Bytes 75 | 76 | 77 | 0 / 0 Byte 78 | 79 | 80 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 81 | 82 | 83 | 84 | 85 | 86 | 87 | Elapsed Time 88 | 89 | 90 | 00h:00m:00s 91 | 92 | 93 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Qt::Horizontal 103 | 104 | 105 | 106 | 107 | 108 | 109 | color: rgb(255, 0, 0); 110 | 111 | 112 | Update takes up to 10min! 113 | Be patient and listen to voice output... 114 | 115 | 116 | Qt::AlignCenter 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | QDialogButtonBox::Cancel 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /res/ui/upload.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Upload 4 | 5 | 6 | 7 | 0 8 | 0 9 | 292 10 | 185 11 | 12 | 13 | 14 | Voice Upload 15 | 16 | 17 | 18 | :/png/png/voice.png:/png/png/voice.png 19 | 20 | 21 | 22 | 23 | 24 | Progress 25 | 26 | 27 | 28 | 29 | 30 | 31 | 250 32 | 0 33 | 34 | 35 | 36 | Filename 37 | 38 | 39 | ? 40 | 41 | 42 | Qt::AlignCenter 43 | 44 | 45 | 46 | 47 | 48 | 49 | Upload Status 50 | 51 | 52 | 0 53 | 54 | 55 | 56 | 57 | 58 | 59 | Install Status 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | 68 | 69 | 5 70 | 71 | 72 | 73 | 74 | Transfered Bytes 75 | 76 | 77 | 0 / 0 Byte 78 | 79 | 80 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 81 | 82 | 83 | 84 | 85 | 86 | 87 | Elapsed Time 88 | 89 | 90 | 00h:00m:00s 91 | 92 | 93 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | QDialogButtonBox::Cancel 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /res/ui/zones.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog_Zones 4 | 5 | 6 | 7 | 0 8 | 0 9 | 385 10 | 267 11 | 12 | 13 | 14 | Zone & GoTo Editor 15 | 16 | 17 | 18 | :/png/png/zone.png:/png/png/zone.png 19 | 20 | 21 | 22 | 23 | 24 | 0 25 | 26 | 27 | 28 | 29 | :/png/png/zone.png:/png/png/zone.png 30 | 31 | 32 | Zone 33 | 34 | 35 | 36 | 37 | 38 | Parameter 39 | 40 | 41 | 42 | 43 | 44 | Zone Label 45 | 46 | 47 | true 48 | 49 | 50 | QComboBox::NoInsert 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Starting Position (X1) 60 | 61 | 62 | Qt::AlignCenter 63 | 64 | 65 | false 66 | 67 | 68 | 51200 69 | 70 | 71 | 72 | 73 | 74 | 75 | Ending Position (X2) 76 | 77 | 78 | Qt::AlignCenter 79 | 80 | 81 | false 82 | 83 | 84 | 51200 85 | 86 | 87 | 88 | 89 | 90 | 91 | Starting Position (Y1) 92 | 93 | 94 | Qt::AlignCenter 95 | 96 | 97 | false 98 | 99 | 100 | 51200 101 | 102 | 103 | 104 | 105 | 106 | 107 | Ending Position (Y2) 108 | 109 | 110 | Qt::AlignCenter 111 | 112 | 113 | false 114 | 115 | 116 | 51200 117 | 118 | 119 | 120 | 121 | 122 | 123 | Cleaning Cycles 124 | 125 | 126 | Qt::AlignCenter 127 | 128 | 129 | 1 130 | 131 | 132 | 10 133 | 134 | 135 | 136 | 137 | 138 | 139 | Current Zone 140 | 141 | 142 | QFrame::StyledPanel 143 | 144 | 145 | Zone ? / ? 146 | 147 | 148 | Qt::AlignCenter 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | :/png/png/goto.png:/png/png/goto.png 163 | 164 | 165 | GoTo 166 | 167 | 168 | 169 | 170 | 171 | Parameter 172 | 173 | 174 | 175 | 176 | 177 | GoTo Label 178 | 179 | 180 | true 181 | 182 | 183 | QComboBox::NoInsert 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | Position X 193 | 194 | 195 | Qt::AlignCenter 196 | 197 | 198 | false 199 | 200 | 201 | 51200 202 | 203 | 204 | 205 | 206 | 207 | 208 | Position Y 209 | 210 | 211 | Qt::AlignCenter 212 | 213 | 214 | false 215 | 216 | 217 | 51200 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | QDialogButtonBox::Close|QDialogButtonBox::Discard|QDialogButtonBox::Save 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /rrcc.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets network websockets 2 | 3 | TARGET = rrcc 4 | 5 | SOURCES += mainwindow.cpp history.cpp timer.cpp setup.cpp logger.cpp upload.cpp packager.cpp unpackager.cpp converter.cpp about.cpp zones.cpp installer.cpp uninstaller.cpp update.cpp download.cpp search.cpp onlineupd.cpp 6 | HEADERS += mainwindow.h history.h timer.h setup.h logger.h upload.h packager.h unpackager.h converter.h about.h zones.h installer.h uninstaller.h update.h download.h search.h onlineupd.h 7 | 8 | INCLUDEPATH += ext/qaes 9 | SOURCES += ext/qaes/qaesencryption.cpp 10 | HEADERS += ext/qaes/qaesencryption.h 11 | 12 | QT += concurrent 13 | INCLUDEPATH += ext/qarchive 14 | SOURCES += ext/qarchive/qarchive.cpp 15 | HEADERS += ext/qarchive/qarchive.h 16 | LIBS += -larchive 17 | 18 | INCLUDEPATH += ext/ccrypt 19 | SOURCES += ext/ccrypt/ccryptlib.c ext/ccrypt/rijndael.c ext/ccrypt/tables.c ext/ccrypt/platform.c 20 | HEADERS += ext/ccrypt/ccryptlib.h ext/ccrypt/rijndael.h ext/ccrypt/tables.h ext/ccrypt/platform.h 21 | 22 | INCLUDEPATH += ext/qsshsocket 23 | SOURCES += ext/qsshsocket/qsshsocket.cpp 24 | HEADERS *= ext/qsshsocket/qsshsocket.h 25 | LIBS += -lssh 26 | 27 | FORMS += res/ui/mainwindow.ui res/ui/history.ui res/ui/timer.ui res/ui/setup.ui res/ui/logger.ui res/ui/upload.ui res/ui/packager.ui res/ui/unpackager.ui res/ui/converter.ui res/ui/about.ui res/ui/zones.ui res/ui/installer.ui res/ui/uninstaller.ui res/ui/update.ui res/ui/download.ui res/ui/search.ui res/ui/onlineupd.ui 28 | RESOURCES += res/rrcc.qrc 29 | 30 | TRANSLATIONS+= lng/rrcc_de.ts 31 | 32 | unix:!macx { 33 | QMAKE_LFLAGS+= -Wl,-rpath=. 34 | } 35 | 36 | win32 { 37 | RC_ICONS += res/ico/app.ico 38 | } 39 | 40 | macx { 41 | ICON += res/ico/app.icns 42 | } 43 | -------------------------------------------------------------------------------- /search.cpp: -------------------------------------------------------------------------------- 1 | #include "search.h" 2 | 3 | searchDialog::searchDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Search")); 11 | buttonBox->button(QDialogButtonBox::Apply)->setDefault(true); 12 | 13 | treeWidget->resizeColumnToContents(0); 14 | treeWidget->resizeColumnToContents(1); 15 | treeWidget->resizeColumnToContents(2); 16 | treeWidget->resizeColumnToContents(3); 17 | 18 | comboBox_model->currentIndexChanged(0); 19 | 20 | QTimer::singleShot(1, this, SLOT(calcHeight())); 21 | } 22 | 23 | void searchDialog::calcHeight() 24 | { 25 | treeWidget->setMinimumHeight(10 * treeWidget->visualItemRect(treeWidget->topLevelItem(0)).height() + treeWidget->header()->height() + /*style()->pixelMetric(QStyle::PM_ScrollBarExtent)*/ + 2); 26 | 27 | treeWidget->clear(); 28 | } 29 | 30 | void searchDialog::metaDataChanged() 31 | { 32 | if(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) 33 | { 34 | reply->abort(); 35 | 36 | QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString("yyyy-MM-dd") << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString("HH:mm:ss") << reply->header(QNetworkRequest::ContentLengthHeader).toString() << request.url().fileName()); 37 | 38 | item->setTextAlignment(0, Qt::AlignHCenter); 39 | item->setTextAlignment(1, Qt::AlignHCenter); 40 | item->setTextAlignment(2, Qt::AlignHCenter); 41 | item->setTextAlignment(3, Qt::AlignHCenter); 42 | 43 | treeWidget->addTopLevelItem(item); 44 | } 45 | 46 | progressBar->setFormat(QString("%1 | %2 / %3 | %p%").arg(QString::number(version).rightJustified(4, '0')).arg(version - spinBox_Start->value() + 1).arg(counter)); 47 | progressBar->setValue((version - spinBox_Start->value() + 1)*100 / counter); 48 | } 49 | 50 | void searchDialog::on_comboBox_model_currentIndexChanged(int index) 51 | { 52 | spinBox_Start->setValue(fw.versions[index]); 53 | spinBox_Stop->setValue(spinBox_Start->value() + 99); 54 | 55 | lineEdit->setText(fw.names[index]); 56 | } 57 | 58 | void searchDialog::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, __attribute__((unused)) int column) 59 | { 60 | downloadDialog(this, comboBox_model->currentIndex(), comboBox_server->currentIndex(), item->text(3)).exec(); 61 | } 62 | 63 | void searchDialog::reject() 64 | { 65 | if(reply && reply->isRunning()) 66 | { 67 | if(QMessageBox::question(this, APPNAME, tr("Really abort search?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) 68 | { 69 | abort = true; 70 | } 71 | 72 | return; 73 | } 74 | 75 | accept(); 76 | } 77 | 78 | void searchDialog::on_buttonBox_clicked(QAbstractButton *button) 79 | { 80 | if(buttonBox->standardButton(button) == QDialogButtonBox::Apply) 81 | { 82 | if(!lineEdit->text().contains("????")) 83 | { 84 | QMessageBox::warning(this, APPNAME, tr("Filename must contain \"????\"!")); 85 | 86 | return; 87 | } 88 | 89 | if(spinBox_Start->value() > spinBox_Stop->value()) 90 | { 91 | int val = spinBox_Start->value(); 92 | 93 | spinBox_Start->setValue(spinBox_Stop->value()); 94 | spinBox_Stop->setValue(val); 95 | } 96 | 97 | counter = spinBox_Stop->value() - spinBox_Start->value() + 1; 98 | 99 | if(counter > 100) 100 | { 101 | if(QMessageBox::warning(this, APPNAME, tr("Scanning more than 100 versions takes a long time and is not recommended!\n\nContinue anyway?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No) 102 | { 103 | return; 104 | } 105 | } 106 | 107 | version = spinBox_Start->value(); 108 | 109 | buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); 110 | 111 | treeWidget->clear(); 112 | progressBar->setValue(0); 113 | 114 | QNetworkAccessManager *netmgr = new QNetworkAccessManager(this); 115 | request = QNetworkRequest(QUrl(QString("http://%1/%2/%3").arg(comboBox_server->currentText()).arg(fw.dirs[comboBox_model->currentIndex()]).arg(lineEdit->text().replace("????", QString::number(version).rightJustified(4, '0'))))); 116 | 117 | abort = false; 118 | 119 | do 120 | { 121 | reply = netmgr->get(request); 122 | 123 | connect(reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged())); 124 | 125 | while(reply->isRunning()) 126 | { 127 | QCoreApplication::processEvents(); 128 | } 129 | 130 | version++; 131 | 132 | request.setUrl(QUrl(QString("http://%1/%2/%3").arg(comboBox_server->currentText()).arg(fw.dirs[comboBox_model->currentIndex()]).arg(lineEdit->text().replace("????", QString::number(version).rightJustified(4, '0'))))); 133 | 134 | }while(!abort && version <= spinBox_Stop->value()); 135 | 136 | buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); 137 | 138 | reply = nullptr; 139 | 140 | QMessageBox::information(this, APPNAME, (abort ? tr("Search aborted, %1 firmware versions found so far.") : tr("%1 firmware versions found.")).arg(treeWidget->topLevelItemCount())); 141 | } 142 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Cancel) 143 | { 144 | if(reply && reply->isRunning()) 145 | { 146 | if(QMessageBox::question(this, APPNAME, tr("Really abort search?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) 147 | { 148 | abort = true; 149 | } 150 | } 151 | else 152 | { 153 | close(); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /search.h: -------------------------------------------------------------------------------- 1 | #ifndef SEARCHDLG_H 2 | #define SEARCHDLG_H 3 | 4 | #include "ui_search.h" 5 | #include "mainwindow.h" 6 | 7 | class searchDialog : public QDialog, private Ui::Dialog_Search 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | searchDialog(QWidget*); 14 | 15 | private: 16 | 17 | struct 18 | { 19 | QString names[3] = { FW_NAME_G1, FW_NAME_G2, FW_NAME_G3 }; 20 | QString dirs[3] = { FW_DIR_G1, FW_DIR_G2, FW_DIR_G3 }; 21 | int versions[3] = { FW_VER_G1, FW_VER_G2, FW_VER_G3 }; 22 | 23 | }fw; 24 | 25 | QNetworkRequest request; 26 | QNetworkReply *reply = nullptr; 27 | int counter; 28 | int version; 29 | bool abort; 30 | 31 | private slots: 32 | 33 | void calcHeight(); 34 | void metaDataChanged(); 35 | void on_comboBox_model_currentIndexChanged(int); 36 | void on_treeWidget_itemDoubleClicked(QTreeWidgetItem*, int); 37 | void reject(); 38 | void on_buttonBox_clicked(QAbstractButton*); 39 | }; 40 | 41 | #endif // SEARCHDLG_H 42 | -------------------------------------------------------------------------------- /setup.h: -------------------------------------------------------------------------------- 1 | #ifndef SETUPDLG_H 2 | #define SETUPDLG_H 3 | 4 | #include "ui_setup.h" 5 | #include "mainwindow.h" 6 | 7 | class setupDialog : public QDialog, private Ui::Dialog_Setup 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | setupDialog(QWidget*, QString); 14 | 15 | private: 16 | 17 | QTimer timerVolume; 18 | QSshSocket *ssh; 19 | 20 | private slots: 21 | 22 | void on_horizontalSlider_volume_valueChanged(int); 23 | 24 | void on_toolButton_ssh_clicked(); 25 | void on_toolButton_convert_clicked(); 26 | void on_toolButton_sound_install_clicked(); 27 | void on_toolButton_sound_pack_clicked(); 28 | void on_toolButton_sound_unpack_clicked(); 29 | void on_toolButton_carpet_reset_clicked(); 30 | void on_toolButton_ssh_keyfile_clicked(); 31 | 32 | void on_groupBox_ssh_password_clicked(bool checked); 33 | void on_groupBox_ssh_keyfile_clicked(bool checked); 34 | 35 | void on_buttonBox_clicked(QAbstractButton*); 36 | 37 | void timer_setVolume(); 38 | 39 | void ssh_connected(); 40 | void ssh_disconnected(); 41 | void ssh_error(QSshSocket::SshError); 42 | void ssh_loginSuccessful(); 43 | void ssh_commandExecuted(QString, QString); 44 | 45 | void reject(); 46 | }; 47 | 48 | #endif // SETUPDLG_H 49 | -------------------------------------------------------------------------------- /timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | timerDialog::timerDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | if(reinterpret_cast(parent)->robo.timers.size()) 11 | { 12 | foreach(TIMER val, reinterpret_cast(parent)->robo.timers) 13 | { 14 | comboBox->addItem(QString("ID %1").arg(val.id), val.id); 15 | } 16 | } 17 | else 18 | { 19 | addTimer(); 20 | } 21 | 22 | buttonBox->button(QDialogButtonBox::Yes)->setText(tr("Add Timer")); 23 | buttonBox->button(QDialogButtonBox::No)->setText(tr("Del Timer")); 24 | } 25 | 26 | void timerDialog::addTimer() 27 | { 28 | buttonBox->button(QDialogButtonBox::Yes)->setEnabled(false); 29 | buttonBox->button(QDialogButtonBox::No)->setEnabled(false); 30 | comboBox->setEnabled(false); 31 | 32 | comboBox_fanspeed->setCurrentIndex(1); 33 | 34 | dial_hour->setValue(10); 35 | dial_minute->setValue(0); 36 | dial_day->setValue(0); 37 | dial_month->setValue(0); 38 | 39 | lcdNumber_day->display("-"); 40 | lcdNumber_month->display("-"); 41 | toolButton_mon->setChecked(true); 42 | toolButton_tue->setChecked(false); 43 | toolButton_wed->setChecked(true); 44 | toolButton_thu->setChecked(false); 45 | toolButton_fri->setChecked(true); 46 | toolButton_sat->setChecked(false); 47 | toolButton_sun->setChecked(false); 48 | pushButton_state->setChecked(true); 49 | 50 | comboBox->addItem(QString("ID %1").arg(QDateTime::currentDateTime().toTime_t()), QDateTime::currentDateTime().toTime_t()); 51 | comboBox->setCurrentIndex(comboBox->count() - 1); 52 | } 53 | 54 | void timerDialog::delTimer() 55 | { 56 | if(reinterpret_cast(parent())->sendUDP(QString(MIIO_DEL_TIMER).arg(comboBox->itemData(comboBox->currentIndex()).toLongLong()).arg("%1"))) 57 | { 58 | reinterpret_cast(parent())->robo.timers.remove(comboBox->currentIndex(), 1); 59 | 60 | comboBox->removeItem(comboBox->currentIndex()); 61 | 62 | if(!comboBox->count()) 63 | { 64 | close(); 65 | } 66 | } 67 | } 68 | 69 | void timerDialog::on_comboBox_currentIndexChanged(int index) 70 | { 71 | if(reinterpret_cast(parent())->robo.timers.size() && reinterpret_cast(parent())->robo.timers.size() > index) 72 | { 73 | QStringList entries = reinterpret_cast(parent())->robo.timers.at(index).crontab.split(' '); 74 | 75 | if(reinterpret_cast(parent())->robo.timers.at(index).fanspeed > FANSPEED_TURBO) 76 | { 77 | comboBox_fanspeed->setCurrentIndex(3); 78 | } 79 | else if(reinterpret_cast(parent())->robo.timers.at(index).fanspeed > FANSPEED_BALANCED) 80 | { 81 | comboBox_fanspeed->setCurrentIndex(2); 82 | } 83 | else if(reinterpret_cast(parent())->robo.timers.at(index).fanspeed > FANSPEED_QUIET) 84 | { 85 | comboBox_fanspeed->setCurrentIndex(1); 86 | } 87 | else 88 | { 89 | comboBox_fanspeed->setCurrentIndex(0); 90 | } 91 | 92 | pushButton_state->setChecked(reinterpret_cast(parent())->robo.timers.at(index).state == "off" ? 0 : 1); 93 | 94 | dial_hour->setValue(entries.at(1) == "*" ? -1 : entries.at(1).toInt()); 95 | dial_minute->setValue(entries.at(0) == "*" ? -1 : entries.at(0).toInt()); 96 | dial_day->setValue(entries.at(2) == "*" ? 0 : entries.at(2).toInt()); 97 | dial_month->setValue(entries.at(3) == "*" ? 0 : entries.at(3).toInt()); 98 | 99 | emit on_dial_hour_valueChanged(dial_hour->value()); 100 | emit on_dial_minute_valueChanged(dial_minute->value()); 101 | emit on_dial_day_valueChanged(dial_day->value()); 102 | emit on_dial_month_valueChanged(dial_month->value()); 103 | 104 | toolButton_sun->setChecked(entries.at(4).contains("0")); 105 | toolButton_mon->setChecked(entries.at(4).contains("1")); 106 | toolButton_tue->setChecked(entries.at(4).contains("2")); 107 | toolButton_wed->setChecked(entries.at(4).contains("3")); 108 | toolButton_thu->setChecked(entries.at(4).contains("4")); 109 | toolButton_fri->setChecked(entries.at(4).contains("5")); 110 | toolButton_sat->setChecked(entries.at(4).contains("6")); 111 | } 112 | } 113 | 114 | void timerDialog::on_dial_hour_valueChanged(int value) 115 | { 116 | lcdNumber_hour->display(value < 0 ? "-" : QString::number(value)); 117 | } 118 | 119 | void timerDialog::on_dial_minute_valueChanged(int value) 120 | { 121 | lcdNumber_minute->display(value < 0 ? "-" : QString::number(value)); 122 | } 123 | 124 | void timerDialog::on_dial_day_valueChanged(int value) 125 | { 126 | lcdNumber_day->display(value ? QString::number(value) : "-"); 127 | } 128 | 129 | void timerDialog::on_dial_month_valueChanged(int value) 130 | { 131 | lcdNumber_month->display(value ? QString::number(value) : "-"); 132 | } 133 | 134 | void timerDialog::on_buttonBox_clicked(QAbstractButton *button) 135 | { 136 | if(buttonBox->standardButton(button) == QDialogButtonBox::Apply) 137 | { 138 | int hour = dial_hour->value(); 139 | int minute = dial_minute->value(); 140 | int day = dial_day->value(); 141 | int month = dial_month->value(); 142 | QString weekdays; 143 | int fanspeeds[] = {FANSPEED_QUIET, FANSPEED_BALANCED, FANSPEED_TURBO, FANSPEED_MAXIMUM}; 144 | 145 | if(toolButton_sun->isChecked()) 146 | { 147 | weekdays.append("0,"); 148 | } 149 | if(toolButton_mon->isChecked()) 150 | { 151 | weekdays.append("1,"); 152 | } 153 | if(toolButton_tue->isChecked()) 154 | { 155 | weekdays.append("2,"); 156 | } 157 | if(toolButton_wed->isChecked()) 158 | { 159 | weekdays.append("3,"); 160 | } 161 | if(toolButton_thu->isChecked()) 162 | { 163 | weekdays.append("4,"); 164 | } 165 | if(toolButton_fri->isChecked()) 166 | { 167 | weekdays.append("5,"); 168 | } 169 | if(toolButton_sat->isChecked()) 170 | { 171 | weekdays.append("6,"); 172 | } 173 | 174 | if(weekdays.isEmpty()) 175 | { 176 | weekdays = "*"; 177 | } 178 | else 179 | { 180 | weekdays.chop(1); 181 | } 182 | 183 | QString crontab = QString("%1 %2 %3 %4 %5").arg(minute < 0 ? "*" : QString::number(minute)).arg(hour < 0 ? "*" : QString::number(hour)).arg(day == 0 ? "*" : QString::number(day)).arg(month == 0 ? "*" : QString::number(month)).arg(weekdays); 184 | 185 | if(reinterpret_cast(parent())->sendUDP(QString(MIIO_SET_TIMER).arg(comboBox->itemData(comboBox->currentIndex()).toLongLong()).arg(crontab).arg(fanspeeds[comboBox_fanspeed->currentIndex()]).arg("%1"))) 186 | { 187 | if(!pushButton_state->isChecked()) 188 | { 189 | reinterpret_cast(parent())->sendUDP(QString(MIIO_UPD_TIMER).arg(comboBox->itemData(comboBox->currentIndex()).toLongLong()).arg("off").arg("%1")); 190 | } 191 | 192 | close(); 193 | } 194 | } 195 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Yes) 196 | { 197 | addTimer(); 198 | } 199 | else if(buttonBox->standardButton(button) == QDialogButtonBox::No) 200 | { 201 | delTimer(); 202 | } 203 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Close) 204 | { 205 | close(); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMERDLG_H 2 | #define TIMERDLG_H 3 | 4 | #include "ui_timer.h" 5 | #include "mainwindow.h" 6 | 7 | class timerDialog : public QDialog, private Ui::Dialog_Timer 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | timerDialog(QWidget*); 14 | 15 | private: 16 | 17 | void addTimer(); 18 | void delTimer(); 19 | 20 | private slots: 21 | 22 | void on_comboBox_currentIndexChanged(int); 23 | 24 | void on_dial_hour_valueChanged(int); 25 | void on_dial_minute_valueChanged(int); 26 | void on_dial_day_valueChanged(int); 27 | void on_dial_month_valueChanged(int); 28 | 29 | void on_buttonBox_clicked(QAbstractButton*); 30 | }; 31 | 32 | #endif // TIMERDLG_H 33 | -------------------------------------------------------------------------------- /uninstaller.cpp: -------------------------------------------------------------------------------- 1 | #include "uninstaller.h" 2 | 3 | uninstallerDialog::uninstallerDialog(QWidget *parent) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Uninstall")); 11 | } 12 | 13 | void uninstallerDialog::updateProgress() 14 | { 15 | QPixmap pixmap = failed ? QPixmap(":/png/png/canceled.png") : QPixmap(":/png/png/complete.png"); 16 | 17 | if(step == 1) 18 | { 19 | progressBar_stop_service->setValue(failed ? 50 : 100); 20 | 21 | label_stop_service->setPixmap(pixmap); 22 | } 23 | else if(step == 2) 24 | { 25 | progressBar_remove_bin->setValue(failed ? 50 : 100); 26 | 27 | label_remove_bin->setPixmap(pixmap); 28 | } 29 | else if(step == 3) 30 | { 31 | progressBar_remove_cfg->setValue(failed ? 50 : 100); 32 | 33 | label_remove_cfg->setPixmap(pixmap); 34 | } 35 | else if(step == 4) 36 | { 37 | progressBar_restore_hosts->setValue(failed ? 50 : 100); 38 | 39 | label_restore_hosts->setPixmap(pixmap); 40 | } 41 | else if(step == 5) 42 | { 43 | progressBar_restore_rclocal->setValue(failed ? 50 : 100); 44 | 45 | label_restore_rclocal->setPixmap(pixmap); 46 | } 47 | 48 | step++; 49 | } 50 | 51 | void uninstallerDialog::ssh_connected() 52 | { 53 | plainTextEdit->appendPlainText(tr("SSH: connected\n")); 54 | 55 | if(reinterpret_cast(parent())->cfg.ssh_auth == "PKey") 56 | { 57 | QFile file_key(reinterpret_cast(parent())->cfg.ssh_pkey); 58 | 59 | if(file_key.open(QIODevice::ReadOnly)) 60 | { 61 | QByteArray key = file_key.readAll(); 62 | 63 | file_key.close(); 64 | 65 | ssh->loginKey(reinterpret_cast(parent())->cfg.ssh_user, key, reinterpret_cast(parent())->cfg.ssh_pkpp); 66 | } 67 | } 68 | else 69 | { 70 | ssh->login(reinterpret_cast(parent())->cfg.ssh_user, reinterpret_cast(parent())->cfg.ssh_pass); 71 | } 72 | } 73 | 74 | void uninstallerDialog::ssh_loginSuccessful() 75 | { 76 | plainTextEdit->appendPlainText(tr("SSH: logged in\n")); 77 | 78 | ssh->executeCommand(QString(VALETUDO_CMD_STOP).arg(comboBox_control->currentText().split("<").at(0))); 79 | } 80 | 81 | void uninstallerDialog::ssh_commandExecuted(QString command, QString response) 82 | { 83 | plainTextEdit->appendPlainText(tr("SSH: %1 -> %2\n").arg(command).arg(response.isEmpty() ? QString("OK") : response)); 84 | 85 | QThread::msleep(250); 86 | 87 | if(command == QString(VALETUDO_CMD_STOP).arg(comboBox_control->currentText().split("<").at(0))) 88 | { 89 | updateProgress(); 90 | 91 | ssh->executeCommand(VALETUDO_CMD_RMBIN); 92 | } 93 | else if(command == VALETUDO_CMD_RMBIN) 94 | { 95 | updateProgress(); 96 | 97 | ssh->executeCommand(VALETUDO_CMD_RMCFG); 98 | } 99 | else if(command == VALETUDO_CMD_RMCFG) 100 | { 101 | updateProgress(); 102 | 103 | ssh->executeCommand(VALETUDO_CMD_HOSTS); 104 | } 105 | else if(command == VALETUDO_CMD_HOSTS) 106 | { 107 | updateProgress(); 108 | 109 | ssh->executeCommand(VALETUDO_CMD_RCLOCAL); 110 | } 111 | else if(command == VALETUDO_CMD_RCLOCAL) 112 | { 113 | updateProgress(); 114 | 115 | ssh->disconnectFromHost(); 116 | } 117 | } 118 | 119 | void uninstallerDialog::ssh_disconnected() 120 | { 121 | plainTextEdit->appendPlainText(tr("SSH: disconnected\n")); 122 | 123 | delete ssh; 124 | ssh = nullptr; 125 | 126 | failed ? QMessageBox::warning(this, APPNAME, tr("Valetudo uninstallation failed!")) : QMessageBox::information(this, APPNAME, tr("Valetudo uninstallation finished.\n\nRead log for details.")); 127 | 128 | buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); 129 | } 130 | 131 | void uninstallerDialog::ssh_error(QSshSocket::SshError error) 132 | { 133 | plainTextEdit->appendPlainText(tr("SSH: %1\n").arg(reinterpret_cast(parent())->ssh_error_strings.at(error))); 134 | 135 | failed = true; 136 | 137 | updateProgress(); 138 | 139 | ssh->disconnectFromHost(); 140 | } 141 | 142 | void uninstallerDialog::reject() 143 | { 144 | if(ssh && ssh->isConnected()) 145 | { 146 | QMessageBox::warning(this, APPNAME, tr("Can not abort current operation, please wait...")); 147 | 148 | return; 149 | } 150 | 151 | accept(); 152 | } 153 | 154 | void uninstallerDialog::on_buttonBox_clicked(QAbstractButton *button) 155 | { 156 | if(buttonBox->standardButton(button) == QDialogButtonBox::Cancel) 157 | { 158 | button->setDisabled(true); 159 | 160 | failed = false; 161 | step = 1; 162 | 163 | label_stop_service->setPixmap(QPixmap(":/png/png/question.png")); 164 | label_remove_bin->setPixmap(QPixmap(":/png/png/question.png")); 165 | label_remove_cfg->setPixmap(QPixmap(":/png/png/question.png")); 166 | label_restore_hosts->setPixmap(QPixmap(":/png/png/question.png")); 167 | label_restore_rclocal->setPixmap(QPixmap(":/png/png/question.png")); 168 | 169 | progressBar_stop_service->setValue(0); 170 | progressBar_remove_bin->setValue(0); 171 | progressBar_remove_cfg->setValue(0); 172 | progressBar_restore_hosts->setValue(0); 173 | progressBar_restore_rclocal->setValue(0); 174 | 175 | plainTextEdit->clear(); 176 | 177 | ssh = new QSshSocket(this); 178 | 179 | connect(ssh, SIGNAL(connected()), this, SLOT(ssh_connected())); 180 | connect(ssh, SIGNAL(disconnected()), this, SLOT(ssh_disconnected())); 181 | connect(ssh, SIGNAL(error(QSshSocket::SshError)), this, SLOT(ssh_error(QSshSocket::SshError))); 182 | connect(ssh, SIGNAL(loginSuccessful()), this, SLOT(ssh_loginSuccessful())); 183 | connect(ssh, SIGNAL(commandExecuted(QString, QString)), this, SLOT(ssh_commandExecuted(QString, QString))); 184 | 185 | ssh->connectToHost(reinterpret_cast(parent())->cfg.ip); 186 | } 187 | else if(buttonBox->standardButton(button) == QDialogButtonBox::Close) 188 | { 189 | close(); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /uninstaller.h: -------------------------------------------------------------------------------- 1 | #ifndef UNINSTALLERDLG_H 2 | #define UNINSTALLERDLG_H 3 | 4 | #include "ui_uninstaller.h" 5 | #include "mainwindow.h" 6 | 7 | #define VALETUDO_CMD_RMBIN "rm " VALETUDO_BIN_DST 8 | #define VALETUDO_CMD_RMCFG "rm " VALETUDO_CFG_DST 9 | #define VALETUDO_CMD_HOSTS "sed -i '/### VALETUDO HOSTS INIT ###/,/### VALETUDO HOSTS EXIT ###/d' " VALETUDO_HOSTS_DST 10 | #define VALETUDO_CMD_RCLOCAL "sed -i '/### VALETUDO RC.LOCAL INIT ###/,/### VALETUDO RC.LOCAL EXIT ###/d' " VALETUDO_RCLOCAL_DST 11 | 12 | class uninstallerDialog : public QDialog, private Ui::Dialog_Uninstaller 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | 18 | uninstallerDialog(QWidget*); 19 | 20 | private: 21 | 22 | void updateProgress(); 23 | 24 | QSshSocket *ssh = nullptr; 25 | bool failed; 26 | int step; 27 | 28 | private slots: 29 | 30 | void ssh_connected(); 31 | void ssh_disconnected(); 32 | void ssh_error(QSshSocket::SshError); 33 | void ssh_loginSuccessful(); 34 | void ssh_commandExecuted(QString, QString); 35 | 36 | void reject(); 37 | 38 | void on_buttonBox_clicked(QAbstractButton*); 39 | }; 40 | 41 | #endif // UNINSTALLERDLG_H 42 | -------------------------------------------------------------------------------- /unpackager.cpp: -------------------------------------------------------------------------------- 1 | #include "unpackager.h" 2 | 3 | unpackagerDialog::unpackagerDialog(QWidget *parent, QFile *package, QFile *directory) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | connect(&timer, SIGNAL(timeout()), this, SLOT(timer_refreshTime())); 11 | 12 | file_pkg = package; 13 | file_dir = directory; 14 | 15 | finished = false; 16 | canceled = false; 17 | 18 | time.start(); 19 | timer.start(500); 20 | 21 | label_file->setText(file_pkg->fileName()); 22 | label_size->setText(QString::number(file_pkg->size())); 23 | 24 | QTimer::singleShot(100, this, SLOT(startUnpackaging())); 25 | } 26 | 27 | void unpackagerDialog::startUnpackaging() 28 | { 29 | QFile file_tgz(TGZ); 30 | QByteArray pkg; 31 | QByteArray tgz; 32 | ccrypt_stream_t ccrypt; 33 | short err_code; 34 | QString err_msg; 35 | int count = 0; 36 | 37 | pkg = file_pkg->readAll(); 38 | file_pkg->close(); 39 | 40 | tgz.resize(pkg.size() - 32); 41 | 42 | ccrypt.next_in = pkg.data(); 43 | ccrypt.avail_in = static_cast(pkg.size()); 44 | ccrypt.next_out = tgz.data(); 45 | ccrypt.avail_out = static_cast(tgz.size()); 46 | 47 | progressBar_decrypt->setValue(50); 48 | 49 | ccdecrypt_init(&ccrypt, PKG_KEY, 0); 50 | ccdecrypt(&ccrypt); 51 | ccdecrypt_end(&ccrypt); 52 | 53 | progressBar_decrypt->setValue(100); 54 | 55 | QCoreApplication::processEvents(); 56 | 57 | if(file_tgz.open(QIODevice::WriteOnly)) 58 | { 59 | file_tgz.write(tgz); 60 | 61 | file_tgz.close(); 62 | } 63 | 64 | extractor 65 | .setArchive(file_tgz.fileName(), file_dir->fileName()) 66 | .setFunc(QArchive::EXTRACTED, [&](QString file) 67 | { 68 | label_file->setText(QFileInfo(file).fileName()); 69 | label_count->setText(QString::number(++count)); 70 | label_size->setText(QString("%1 Byte").arg(QFileInfo(file).size())); 71 | }) 72 | .setFunc(QArchive::PROGRESS, [&](int prog) 73 | { 74 | progressBar_decompress->setValue(prog); 75 | }) 76 | .setFunc(QArchive::FINISHED, [&]() 77 | { 78 | finished = true; 79 | }) 80 | .setFunc(QArchive::CANCELED, [&]() 81 | { 82 | canceled = true; 83 | }) 84 | .setFunc([&](short errorCode, QString eMsg) 85 | { 86 | canceled = true; 87 | finished = true; 88 | err_code = errorCode; 89 | err_msg = eMsg; 90 | }) 91 | .start(); 92 | 93 | while(!finished && !canceled) 94 | { 95 | QThread::msleep(10); 96 | QCoreApplication::processEvents(); 97 | } 98 | 99 | timer.stop(); 100 | 101 | if(canceled) 102 | { 103 | if(!finished) 104 | { 105 | QMessageBox::warning(this, APPNAME, tr("Unpackaging aborted by user!")); 106 | } 107 | else 108 | { 109 | QMessageBox::critical(this, APPNAME, tr("Unpackaging error!\n\n%1\n\n%2").arg(reinterpret_cast(parent())->qarchive_error_strings.at(err_code)).arg(err_msg)); 110 | } 111 | 112 | close(); 113 | 114 | return; 115 | } 116 | 117 | QDesktopServices::openUrl(QUrl("file:///" + file_dir->fileName())); 118 | 119 | close(); 120 | } 121 | 122 | void unpackagerDialog::timer_refreshTime() 123 | { 124 | QTime elapsed = QTime::fromMSecsSinceStartOfDay(time.elapsed()); 125 | 126 | label_time->setText(QString("%1h:%2m:%3s").arg(elapsed.hour(), 2, 10, QChar('0')).arg(elapsed.minute(), 2, 10, QChar('0')).arg(elapsed.second(), 2, 10, QChar('0'))); 127 | } 128 | 129 | void unpackagerDialog::reject() 130 | { 131 | if(!finished && !canceled) 132 | { 133 | if(QMessageBox::question(this, APPNAME, tr("Really abort unpackaging?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 134 | { 135 | extractor.cancel(); 136 | } 137 | } 138 | else 139 | { 140 | accept(); 141 | } 142 | } 143 | 144 | void unpackagerDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 145 | { 146 | close(); 147 | } 148 | -------------------------------------------------------------------------------- /unpackager.h: -------------------------------------------------------------------------------- 1 | #ifndef UNPACKAGERDLG_H 2 | #define UNPACKAGERDLG_H 3 | 4 | #include "ui_unpackager.h" 5 | #include "mainwindow.h" 6 | 7 | class unpackagerDialog : public QDialog, private Ui::Dialog_Unpackager 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | unpackagerDialog(QWidget*, QFile*, QFile*); 14 | 15 | private: 16 | 17 | QArchive::Extractor extractor; 18 | QTime time; 19 | QTimer timer; 20 | QFile *file_pkg; 21 | QFile *file_dir; 22 | bool finished; 23 | bool canceled; 24 | 25 | private slots: 26 | 27 | void startUnpackaging(); 28 | void timer_refreshTime(); 29 | 30 | void on_buttonBox_clicked(QAbstractButton*); 31 | 32 | void reject(); 33 | }; 34 | 35 | #endif // UNPACKAGERDLG_H 36 | -------------------------------------------------------------------------------- /update.cpp: -------------------------------------------------------------------------------- 1 | #include "update.h" 2 | 3 | updateDialog::updateDialog(QWidget *parent, QFile *package) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | file = package; 11 | 12 | label_file->setText(QFileInfo(file->fileName()).fileName()); 13 | label_bytes->setText(QString("0 / %1 Byte").arg(file->size())); 14 | 15 | connect(&timer, SIGNAL(timeout()), this, SLOT(timer_refreshTime())); 16 | 17 | time.start(); 18 | timer.start(500); 19 | 20 | QTimer::singleShot(1, this, SLOT(startUpdating())); 21 | } 22 | 23 | void updateDialog::startUpdating() 24 | { 25 | pkg = file->readAll(); 26 | file->close(); 27 | 28 | size = pkg.size(); 29 | 30 | server = new QTcpServer(this); 31 | socket = nullptr; 32 | 33 | connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); 34 | 35 | if(server->listen(QHostAddress(reinterpret_cast(parent())->src_ip), 8080)) 36 | { 37 | if(!reinterpret_cast(parent())->sendUDP(QString(MIIO_OTA).arg(QString("http://%1:8080/%2").arg(reinterpret_cast(parent())->src_ip).arg(QFileInfo(file->fileName()).fileName())).arg(QString(QCryptographicHash::hash(pkg, QCryptographicHash::Md5).toHex())).arg("%1"))) 38 | { 39 | timer.stop(); 40 | 41 | canceled = true; 42 | 43 | QMessageBox::warning(this, APPNAME, tr("Could not start firmware update!")); 44 | 45 | close(); 46 | } 47 | } 48 | else 49 | { 50 | timer.stop(); 51 | 52 | canceled = true; 53 | 54 | QMessageBox::warning(this, APPNAME, tr("Could not start HTTP server!\n\n%1").arg(server->errorString())); 55 | 56 | close(); 57 | } 58 | } 59 | 60 | void updateDialog::timer_refreshTime() 61 | { 62 | QTime elapsed = QTime::fromMSecsSinceStartOfDay(time.elapsed()); 63 | 64 | label_time->setText(QString("%1h:%2m:%3s").arg(elapsed.hour(), 2, 10, QChar('0')).arg(elapsed.minute(), 2, 10, QChar('0')).arg(elapsed.second(), 2, 10, QChar('0'))); 65 | 66 | if(!requested && time.elapsed() >= 15000) 67 | { 68 | timer.stop(); 69 | 70 | canceled = true; 71 | 72 | QMessageBox::warning(this, APPNAME, tr("Robot does not send firmware package request!\n\nMake sure your firewall accepts incoming tcp connections for\n\n %1 -> %2:8080\n\nand try again...").arg(reinterpret_cast(parent())->cfg.ip).arg(reinterpret_cast(parent())->src_ip)); 73 | 74 | close(); 75 | } 76 | } 77 | 78 | void updateDialog::newConnection() 79 | { 80 | socket = server->nextPendingConnection(); 81 | 82 | connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); 83 | connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); 84 | connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64))); 85 | 86 | requested = true; 87 | } 88 | 89 | void updateDialog::readyRead() 90 | { 91 | socket->readAll(); 92 | 93 | header = QString("HTTP/1.1 200 OK\r\nDate: %1 GMT\r\nContent-Type: application/octet-stream\r\nContent-Length: %2\r\nConnection: close\r\n\r\n").arg(QLocale("en_US").toString(QDateTime::currentDateTime().toUTC(), "ddd, dd MMM yyyy hh:mm:ss")).arg(size).toUtf8(); 94 | 95 | total = static_cast(header.size() * -1); 96 | 97 | socket->write(header); 98 | socket->write(pkg); 99 | } 100 | 101 | void updateDialog::bytesWritten(qint64 byte) 102 | { 103 | total += static_cast(byte); 104 | 105 | progressBar_upload->setValue(static_cast((100 * total) / static_cast(size))); 106 | label_bytes->setText(QString("%1 / %2 Byte").arg(total).arg(size)); 107 | } 108 | 109 | void updateDialog::disconnected() 110 | { 111 | finished = true; 112 | 113 | socket->close(); 114 | server->close(); 115 | 116 | if(canceled) 117 | { 118 | timer.stop(); 119 | 120 | QMessageBox::warning(this, APPNAME, tr("Update aborted by user!")); 121 | 122 | close(); 123 | 124 | return; 125 | } 126 | 127 | progressBar_install->setValue(50); 128 | 129 | do 130 | { 131 | reinterpret_cast(parent())->sendUDP(MIIO_GET_OTA_STATE); 132 | 133 | QThread::msleep(1000); 134 | QCoreApplication::processEvents(); 135 | } 136 | while(reinterpret_cast(parent())->robo.ota.state == "downloading" || reinterpret_cast(parent())->robo.ota.state == "installing"); 137 | 138 | progressBar_install->setValue(100); 139 | 140 | timer.stop(); 141 | 142 | reinterpret_cast(parent())->robo.ota.state == "failed" ? QMessageBox::warning(this, APPNAME, tr("Firmware update failed!")) : QMessageBox::information(this, APPNAME, tr("Firmware update successful.")); 143 | 144 | reinterpret_cast(parent())->sendUDP(nullptr); 145 | 146 | close(); 147 | } 148 | 149 | void updateDialog::reject() 150 | { 151 | if(!finished && !canceled) 152 | { 153 | if(QMessageBox::question(this, APPNAME, tr("Really abort update?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 154 | { 155 | canceled = true; 156 | 157 | if(socket) 158 | { 159 | socket->abort(); 160 | 161 | reject(); 162 | } 163 | else 164 | { 165 | server->close(); 166 | 167 | accept(); 168 | } 169 | } 170 | } 171 | else 172 | { 173 | accept(); 174 | } 175 | } 176 | 177 | void updateDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 178 | { 179 | close(); 180 | } 181 | -------------------------------------------------------------------------------- /update.h: -------------------------------------------------------------------------------- 1 | #ifndef UPDATEDLG_H 2 | #define UPDATEDLG_H 3 | 4 | #include "ui_update.h" 5 | #include "mainwindow.h" 6 | 7 | class updateDialog : public QDialog, private Ui::Dialog_Update 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | updateDialog(QWidget*, QFile*); 14 | 15 | private: 16 | 17 | QTcpServer *server; 18 | QTcpSocket *socket; 19 | QByteArray header; 20 | QByteArray pkg; 21 | QTime time; 22 | QTimer timer; 23 | QFile *file; 24 | int size; 25 | quint64 total; 26 | bool finished = false; 27 | bool canceled = false; 28 | bool requested = false; 29 | 30 | private slots: 31 | 32 | void newConnection(); 33 | void readyRead(); 34 | void bytesWritten(qint64); 35 | void disconnected(); 36 | 37 | void startUpdating(); 38 | void timer_refreshTime(); 39 | 40 | void on_buttonBox_clicked(QAbstractButton*); 41 | 42 | void reject(); 43 | }; 44 | 45 | #endif // UPDATEDLG_H 46 | -------------------------------------------------------------------------------- /upload.cpp: -------------------------------------------------------------------------------- 1 | #include "upload.h" 2 | 3 | uploadDialog::uploadDialog(QWidget *parent, QFile *package) : QDialog(parent) 4 | { 5 | setupUi(this); 6 | 7 | layout()->setSizeConstraint(QLayout::SetFixedSize); 8 | setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); 9 | 10 | file = package; 11 | 12 | label_file->setText(QFileInfo(file->fileName()).fileName()); 13 | label_bytes->setText(QString("0 / %1 Byte").arg(file->size())); 14 | 15 | connect(&timer, SIGNAL(timeout()), this, SLOT(timer_refreshTime())); 16 | 17 | time.start(); 18 | timer.start(500); 19 | 20 | QTimer::singleShot(1, this, SLOT(startUploading())); 21 | } 22 | 23 | void uploadDialog::startUploading() 24 | { 25 | pkg = file->readAll(); 26 | file->close(); 27 | 28 | size = pkg.size(); 29 | 30 | server = new QTcpServer(this); 31 | socket = nullptr; 32 | 33 | connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); 34 | 35 | if(server->listen(QHostAddress(reinterpret_cast(parent())->src_ip), 8080)) 36 | { 37 | if(reinterpret_cast(parent())->sendUDP(MIIO_GET_CURRENT_SOUND)) 38 | { 39 | if(!reinterpret_cast(parent())->sendUDP(QString(MIIO_DNLD_INSTALL_SOUND).arg(QString("http://%1:8080/%2").arg(reinterpret_cast(parent())->src_ip).arg(QFileInfo(file->fileName()).fileName())).arg(QString(QCryptographicHash::hash(pkg, QCryptographicHash::Md5).toHex())).arg(reinterpret_cast(parent())->robo.currentsound.sid_in_use).arg("%1"))) 40 | { 41 | timer.stop(); 42 | 43 | canceled = true; 44 | 45 | QMessageBox::warning(this, APPNAME, tr("Could not start voice installation!")); 46 | 47 | close(); 48 | } 49 | } 50 | else 51 | { 52 | timer.stop(); 53 | 54 | canceled = true; 55 | 56 | QMessageBox::warning(this, APPNAME, tr("Could not detect current voice id!")); 57 | 58 | close(); 59 | } 60 | } 61 | else 62 | { 63 | timer.stop(); 64 | 65 | canceled = true; 66 | 67 | QMessageBox::warning(this, APPNAME, tr("Could not start HTTP server!\n\n%1").arg(server->errorString())); 68 | 69 | close(); 70 | } 71 | } 72 | 73 | void uploadDialog::timer_refreshTime() 74 | { 75 | QTime elapsed = QTime::fromMSecsSinceStartOfDay(time.elapsed()); 76 | 77 | label_time->setText(QString("%1h:%2m:%3s").arg(elapsed.hour(), 2, 10, QChar('0')).arg(elapsed.minute(), 2, 10, QChar('0')).arg(elapsed.second(), 2, 10, QChar('0'))); 78 | 79 | if(!requested && time.elapsed() >= 15000) 80 | { 81 | timer.stop(); 82 | 83 | canceled = true; 84 | 85 | QMessageBox::warning(this, APPNAME, tr("Robot does not send voice package request!\n\nMake sure your firewall accepts incoming tcp connections for\n\n %1 -> %2:8080\n\nand try again...").arg(reinterpret_cast(parent())->cfg.ip).arg(reinterpret_cast(parent())->src_ip)); 86 | 87 | close(); 88 | } 89 | } 90 | 91 | void uploadDialog::newConnection() 92 | { 93 | socket = server->nextPendingConnection(); 94 | 95 | connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); 96 | connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); 97 | connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64))); 98 | 99 | requested = true; 100 | } 101 | 102 | void uploadDialog::readyRead() 103 | { 104 | socket->readAll(); 105 | 106 | header = QString("HTTP/1.1 200 OK\r\nDate: %1 GMT\r\nContent-Type: application/octet-stream\r\nContent-Length: %2\r\nConnection: close\r\n\r\n").arg(QLocale("en_US").toString(QDateTime::currentDateTime().toUTC(), "ddd, dd MMM yyyy hh:mm:ss")).arg(size).toUtf8(); 107 | 108 | total = static_cast(header.size() * -1); 109 | 110 | socket->write(header); 111 | socket->write(pkg); 112 | } 113 | 114 | void uploadDialog::bytesWritten(qint64 byte) 115 | { 116 | total += static_cast(byte); 117 | 118 | progressBar_upload->setValue(static_cast((100 * total) / static_cast(size))); 119 | label_bytes->setText(QString("%1 / %2 Byte").arg(total).arg(size)); 120 | } 121 | 122 | void uploadDialog::disconnected() 123 | { 124 | finished = true; 125 | 126 | socket->close(); 127 | server->close(); 128 | 129 | if(canceled) 130 | { 131 | timer.stop(); 132 | 133 | QMessageBox::warning(this, APPNAME, tr("Upload aborted by user!")); 134 | 135 | close(); 136 | 137 | return; 138 | } 139 | 140 | progressBar_install->setValue(50); 141 | 142 | do 143 | { 144 | reinterpret_cast(parent())->sendUDP(MIIO_GET_SOUND_PROGRESS); 145 | 146 | QThread::msleep(1000); 147 | QCoreApplication::processEvents(); 148 | } 149 | while(reinterpret_cast(parent())->robo.soundprogress.sid_in_progress); 150 | 151 | timer.stop(); 152 | 153 | progressBar_install->setValue(100); 154 | 155 | reinterpret_cast(parent())->robo.soundprogress.error ? QMessageBox::warning(this, APPNAME, tr("Voice installation failed!\n\nErrorcode: %1").arg(reinterpret_cast(parent())->robo.soundprogress.error)) : QMessageBox::information(this, APPNAME, tr("Voice installation successful.")); 156 | 157 | close(); 158 | } 159 | 160 | void uploadDialog::reject() 161 | { 162 | if(!finished && !canceled) 163 | { 164 | if(QMessageBox::question(this, APPNAME, tr("Really abort upload?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) 165 | { 166 | canceled = true; 167 | 168 | if(socket) 169 | { 170 | socket->abort(); 171 | 172 | reject(); 173 | } 174 | else 175 | { 176 | server->close(); 177 | 178 | accept(); 179 | } 180 | } 181 | } 182 | else 183 | { 184 | accept(); 185 | } 186 | } 187 | 188 | void uploadDialog::on_buttonBox_clicked(__attribute__((unused)) QAbstractButton *button) 189 | { 190 | close(); 191 | } 192 | -------------------------------------------------------------------------------- /upload.h: -------------------------------------------------------------------------------- 1 | #ifndef UPLOADDLG_H 2 | #define UPLOADDLG_H 3 | 4 | #include "ui_upload.h" 5 | #include "mainwindow.h" 6 | 7 | class uploadDialog : public QDialog, private Ui::Dialog_Upload 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | uploadDialog(QWidget*, QFile*); 14 | 15 | private: 16 | 17 | QTcpServer *server; 18 | QTcpSocket *socket; 19 | QByteArray header; 20 | QByteArray pkg; 21 | QTime time; 22 | QTimer timer; 23 | QFile *file; 24 | int size; 25 | quint64 total; 26 | bool finished = false; 27 | bool canceled = false; 28 | bool requested = false; 29 | 30 | private slots: 31 | 32 | void newConnection(); 33 | void readyRead(); 34 | void bytesWritten(qint64); 35 | void disconnected(); 36 | 37 | void startUploading(); 38 | void timer_refreshTime(); 39 | 40 | void on_buttonBox_clicked(QAbstractButton*); 41 | 42 | void reject(); 43 | }; 44 | 45 | #endif // UPLOADDLG_H 46 | -------------------------------------------------------------------------------- /zones.h: -------------------------------------------------------------------------------- 1 | #ifndef ZONESDLG_H 2 | #define ZONESDLG_H 3 | 4 | #include "ui_zones.h" 5 | #include "mainwindow.h" 6 | 7 | class zonesDialog : public QDialog, private Ui::Dialog_Zones 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | 13 | zonesDialog(QWidget*); 14 | 15 | private slots: 16 | 17 | void on_tabWidget_currentChanged(int); 18 | 19 | void on_comboBox_zone_currentIndexChanged(int); 20 | void on_comboBox_goto_currentIndexChanged(int); 21 | 22 | void zonePositionChanged(); 23 | void on_spinBox_x1_editingFinished(); 24 | void on_spinBox_y1_editingFinished(); 25 | void on_spinBox_x2_editingFinished(); 26 | void on_spinBox_y2_editingFinished(); 27 | void on_spinBox_x1_valueChanged(int); 28 | void on_spinBox_y1_valueChanged(int); 29 | void on_spinBox_x2_valueChanged(int); 30 | void on_spinBox_y2_valueChanged(int); 31 | 32 | void gotoPositionChanged(); 33 | void on_spinBox_x_editingFinished(); 34 | void on_spinBox_y_editingFinished(); 35 | void on_spinBox_x_valueChanged(int); 36 | void on_spinBox_y_valueChanged(int); 37 | 38 | void on_buttonBox_clicked(QAbstractButton*); 39 | 40 | void reject(); 41 | }; 42 | 43 | #endif // ZONESDLG_H 44 | --------------------------------------------------------------------------------