From 67b654a0c63e2ff61f2a90f58467677ca44eeba0 Mon Sep 17 00:00:00 2001 From: "wingo.he" Date: Sun, 11 Dec 2022 13:35:38 +0800 Subject: [PATCH] =?UTF-8?q?v9.25.0=201.=E5=A2=9E=E5=8A=A0=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E4=BF=A1=E6=81=AF=E7=9A=84SFTP=E5=A4=87=E4=BB=BD?= =?UTF-8?q?=E5=92=8C=E5=90=88=E5=B9=B6=E5=8A=9F=E8=83=BD=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E3=80=82=20=E3=80=8B=E3=80=8B=E6=8F=90=E4=BE=9B8=E7=A7=8D?= =?UTF-8?q?=E9=AB=98=E5=BC=BA=E5=BA=A6=E5=8A=A0=E5=AF=86=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E4=BE=9B=E7=94=A8=E6=88=B7=E8=87=AA=E7=94=B1=E9=80=89=E6=8B=A9?= =?UTF-8?q?=EF=BC=8C=E9=99=8D=E4=BD=8E=E6=95=B0=E6=8D=AE=E5=BA=93=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=A2=AB=E6=9A=B4=E5=8A=9B=E7=A0=B4=E8=A7=A3=E7=9A=84?= =?UTF-8?q?=E9=A3=8E=E9=99=A9=E3=80=82=20=E3=80=8B=E3=80=8B=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E5=A4=9A=E7=A7=8D=E5=90=88=E5=B9=B6=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E3=80=82=202.=E4=BF=AE=E5=A4=8DUbuntu=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E4=B8=8B=EF=BC=8C=E7=BB=88=E7=AB=AF=E4=B8=8ESFTP=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E4=B8=8D=E5=90=8C=E6=AD=A5=E7=9A=84=E7=BC=BA=E9=99=B7?= =?UTF-8?q?=E3=80=82=203.SSH=E6=96=AD=E7=BD=91=E5=90=8E=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=81=A2=E5=A4=8D=E8=87=B3=E6=96=AD=E7=BD=91?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E7=9B=AE=E5=BD=95=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kxterm/qkxtermitem.cpp | 28 +- kxutil/CMakeLists.txt | 30 +- kxutil/qkxcipher.cpp | 712 ++++++++++ kxutil/qkxcipher.h | 55 + kxutil/qkxsetting.cpp | 2 +- woterm/CMakeLists.txt | 24 + woterm/language/update.bat | 4 +- woterm/language/woterm_zh.qm | Bin 39590 -> 56398 bytes woterm/language/woterm_zh.ts | 1974 ++++++++++++++++++++++------ woterm/main.cpp | 4 +- woterm/qwoaboutdialog.cpp | 2 +- woterm/qwoadmindialog.cpp | 5 +- woterm/qwodbbackupdialog.cpp | 156 +++ woterm/qwodbbackupdialog.h | 46 + woterm/qwodbbackupdialog.ui | 375 ++++++ woterm/qwodbgroupmergemodel.cpp | 40 + woterm/qwodbgroupmergemodel.h | 26 + woterm/qwodbidentitymergemodel.cpp | 42 + woterm/qwodbidentitymergemodel.h | 27 + woterm/qwodbmergemodel.cpp | 296 +++++ woterm/qwodbmergemodel.h | 46 + woterm/qwodbpowerrestoredialog.cpp | 727 ++++++++++ woterm/qwodbpowerrestoredialog.h | 106 ++ woterm/qwodbpowerrestoredialog.ui | 666 ++++++++++ woterm/qwodbrestoredialog.cpp | 12 +- woterm/qwodbservermergemodel.cpp | 50 + woterm/qwodbservermergemodel.h | 26 + woterm/qwodbsftpdetaildialog.cpp | 307 +++++ woterm/qwodbsftpdetaildialog.h | 57 + woterm/qwodbsftpdetaildialog.ui | 253 ++++ woterm/qwodbsftpdownlistdialog.cpp | 88 ++ woterm/qwodbsftpdownlistdialog.h | 43 + woterm/qwodbsftpdownlistdialog.ui | 75 ++ woterm/qwodbsftpdownsync.cpp | 215 +++ woterm/qwodbsftpdownsync.h | 57 + woterm/qwodbsftpuploadsync.cpp | 286 ++++ woterm/qwodbsftpuploadsync.h | 55 + woterm/qwoglobal.h | 46 + woterm/qwohostinfoedit.cpp | 9 +- woterm/qwohostlistmodel.cpp | 7 + woterm/qwohostlistmodel.h | 2 + woterm/qwohosttreemodel.cpp | 6 + woterm/qwohosttreemodel.h | 2 + woterm/qwoidentify.cpp | 26 + woterm/qwoidentify.h | 1 + woterm/qwoidentifycreatedialog.cpp | 8 +- woterm/qwoidentifydialog.cpp | 26 +- woterm/qwomainwindow.cpp | 52 +- woterm/qwopasswordinput.cpp | 5 +- woterm/qworlogintermwidget.cpp | 4 +- woterm/qwoserialwidgetimpl.cpp | 10 +- woterm/qwosessionlist.cpp | 4 +- woterm/qwosessionmanage.cpp | 38 +- woterm/qwosessionproperty.cpp | 35 +- woterm/qwosetting.cpp | 17 +- woterm/qwosetting.h | 1 + woterm/qwosftplocalmodel.cpp | 220 ++++ woterm/qwosftplocalmodel.h | 60 + woterm/qwosftpqueuemodel.cpp | 277 ++++ woterm/qwosftpqueuemodel.h | 65 + woterm/qwosftpremotemodel.cpp | 316 +++++ woterm/qwosftpremotemodel.h | 62 + woterm/qwosftptask.cpp | 116 ++ woterm/qwosftptask.h | 38 + woterm/qwosftpwidget.cpp | 5 +- woterm/qwosftpwidgetimpl.cpp | 2 +- woterm/qwoshower.cpp | 12 +- woterm/qwossh.cpp | 508 ++++++- woterm/qwossh.h | 27 + woterm/qwosshconf.cpp | 36 +- woterm/qwosshconf.h | 10 +- woterm/qwosshtermwidget.cpp | 71 +- woterm/qwosshtermwidget.h | 6 +- woterm/qwotelnettermwidget.cpp | 4 +- woterm/qwotermwidget.cpp | 4 +- woterm/qwotermwidgetimpl.cpp | 2 +- woterm/qwoutils.cpp | 18 +- woterm/resource/qss/default.qss | 13 + woterm/version.h | 2 +- 79 files changed, 8555 insertions(+), 535 deletions(-) create mode 100644 kxutil/qkxcipher.cpp create mode 100644 kxutil/qkxcipher.h create mode 100644 woterm/qwodbbackupdialog.cpp create mode 100644 woterm/qwodbbackupdialog.h create mode 100644 woterm/qwodbbackupdialog.ui create mode 100644 woterm/qwodbgroupmergemodel.cpp create mode 100644 woterm/qwodbgroupmergemodel.h create mode 100644 woterm/qwodbidentitymergemodel.cpp create mode 100644 woterm/qwodbidentitymergemodel.h create mode 100644 woterm/qwodbmergemodel.cpp create mode 100644 woterm/qwodbmergemodel.h create mode 100644 woterm/qwodbpowerrestoredialog.cpp create mode 100644 woterm/qwodbpowerrestoredialog.h create mode 100644 woterm/qwodbpowerrestoredialog.ui create mode 100644 woterm/qwodbservermergemodel.cpp create mode 100644 woterm/qwodbservermergemodel.h create mode 100644 woterm/qwodbsftpdetaildialog.cpp create mode 100644 woterm/qwodbsftpdetaildialog.h create mode 100644 woterm/qwodbsftpdetaildialog.ui create mode 100644 woterm/qwodbsftpdownlistdialog.cpp create mode 100644 woterm/qwodbsftpdownlistdialog.h create mode 100644 woterm/qwodbsftpdownlistdialog.ui create mode 100644 woterm/qwodbsftpdownsync.cpp create mode 100644 woterm/qwodbsftpdownsync.h create mode 100644 woterm/qwodbsftpuploadsync.cpp create mode 100644 woterm/qwodbsftpuploadsync.h create mode 100644 woterm/qwosftplocalmodel.cpp create mode 100644 woterm/qwosftplocalmodel.h create mode 100644 woterm/qwosftpqueuemodel.cpp create mode 100644 woterm/qwosftpqueuemodel.h create mode 100644 woterm/qwosftpremotemodel.cpp create mode 100644 woterm/qwosftpremotemodel.h create mode 100644 woterm/qwosftptask.cpp create mode 100644 woterm/qwosftptask.h diff --git a/kxterm/qkxtermitem.cpp b/kxterm/qkxtermitem.cpp index 3cd6575..b1a1c06 100644 --- a/kxterm/qkxtermitem.cpp +++ b/kxterm/qkxtermitem.cpp @@ -866,11 +866,37 @@ void QKxTermItem::onGuessActivePathChanged(const QString &path) int idx = path.indexOf(":~"); if(idx < 0) { idx = path.indexOf(":/"); + if(idx < 0) { + idx = path.indexOf(':'); + if(idx < 0) { + return; + } + bool ok = false; + for(int i = idx+1; i < path.length(); i++) { + QChar c = path.at(i); + if(path.at(i) == QChar::Space) { + continue; + } + if(!(c == '/' || c== '~')) { + return; + } + idx = i; + ok = true; + break; + } + if(!ok) { + return; + } + }else{ + idx += 1; + } + }else{ + idx += 1; } if(idx < 0) { return; } - QString pathAct = path.mid(idx+1); + QString pathAct = path.mid(idx); emit activePathArrived(pathAct); } diff --git a/kxutil/CMakeLists.txt b/kxutil/CMakeLists.txt index 1a8c0de..df84c12 100644 --- a/kxutil/CMakeLists.txt +++ b/kxutil/CMakeLists.txt @@ -24,6 +24,9 @@ set(SOURCE_FILES qkxsetting.cpp qkxhttpclient.cpp qkxrc4crypt.cpp + qkxaescrypt.cpp + qkxabstractcrypt.cpp + qkxcipher.cpp qkxsharememory.cpp qkxlocalpeer.cpp qkxmacaddress.cpp @@ -39,6 +42,9 @@ set(HEADER_FILES qkxsetting.h qkxhttpclient.h qkxrc4crypt.h + qkxaescrypt.h + qkxabstractcrypt.h + qkxcipher.h qkxutil_private.h qkxutil_share.h qkxsharememory.h @@ -95,28 +101,48 @@ else() #set(HEADER_FILES qtunixserversocket.h qtunixsocket.h) endif() -include_directories(${ZLIB_ROOT_DIR}/include) +include_directories(${ZLIB_ROOT_DIR}/include + ${OPENSSL_ROOT_DIR}/include +) -link_directories(${ZLIB_ROOT_DIR}/lib) +link_directories(${ZLIB_ROOT_DIR}/lib + ${OPENSSL_ROOT_DIR}/lib +) if(WIN32) link_libraries(ws2_32) + add_library(libz_main STATIC IMPORTED) set_target_properties(libz_main PROPERTIES IMPORTED_LOCATION ${ZLIB_ROOT_DIR}/lib/zlibstatic.lib) link_libraries(libz_main) + + add_library(openssl_crypto STATIC IMPORTED) + set_target_properties(openssl_crypto PROPERTIES IMPORTED_LOCATION + ${OPENSSL_ROOT_DIR}/lib/libcrypto.lib) + link_libraries(openssl_crypto) elseif(APPLE) message("APPLE Here") add_library(libz_main STATIC IMPORTED) set_target_properties(libz_main PROPERTIES IMPORTED_LOCATION ${ZLIB_ROOT_DIR}/lib/libz.a) link_libraries(libz_main) + + add_library(openssl_crypto STATIC IMPORTED) + set_target_properties(openssl_crypto PROPERTIES IMPORTED_LOCATION + ${OPENSSL_ROOT_DIR}/lib/libcrypto.a) + link_libraries(openssl_crypto) else() message("other") add_library(libz_main STATIC IMPORTED) set_target_properties(libz_main PROPERTIES IMPORTED_LOCATION ${ZLIB_ROOT_DIR}/lib/libz.a) link_libraries(libz_main) + + add_library(openssl_crypto STATIC IMPORTED) + set_target_properties(openssl_crypto PROPERTIES IMPORTED_LOCATION + ${OPENSSL_ROOT_DIR}/lib/libcrypto.a) + link_libraries(openssl_crypto) endif() add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES}) diff --git a/kxutil/qkxcipher.cpp b/kxutil/qkxcipher.cpp new file mode 100644 index 0000000..7a7bb98 --- /dev/null +++ b/kxutil/qkxcipher.cpp @@ -0,0 +1,712 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qkxcipher.h" + +#include +#include +#include +#include +#include +#include + +#include + +int QKxCipher::pkcs7PaddedLength(int dataLen, int alignSize) +{ + int remainder = dataLen % alignSize; + int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder); + return (dataLen + paddingSize); +} + +QByteArray QKxCipher::pkcs7Padding(const QByteArray &in, int alignSize) +{ + int remainder = in.size() % alignSize; + int paddingSize = (remainder == 0) ? alignSize : (alignSize - remainder); + + QByteArray temp(in); + temp.append(paddingSize, paddingSize); + return temp; +} + +QByteArray QKxCipher::pkcs7UnPadding(const QByteArray &in) +{ + char paddingSize = in.at(in.size() - 1); + return in.left(in.size() - paddingSize); +} + +QByteArray QKxCipher::makeBytes(const QByteArray &pass, int cnt) +{ + QByteArray tmp = pass; + if(pass.length() < cnt) { + int left = cnt - pass.length(); + tmp.append(left, 0); + }else if(pass.length() > cnt) { + tmp.resize(cnt); + } + return tmp; +} + +bool QKxCipher::aesEcbEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if (enc) { + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + QByteArray inTemp = pkcs7Padding(in, AES_BLOCK_SIZE); + out.resize(inTemp.size()); + for (int i = 0; i < inTemp.size() / AES_BLOCK_SIZE; i++) { + AES_ecb_encrypt((const unsigned char*)inTemp.data() + AES_BLOCK_SIZE * i, + (unsigned char*)out.data() + AES_BLOCK_SIZE * i, + &aes_key, + AES_ENCRYPT); + } + } else { + AES_KEY aes_key; + if (AES_set_decrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + out.resize(in.size()); + for (int i = 0; i < in.size() / AES_BLOCK_SIZE; i++) { + AES_ecb_encrypt((const unsigned char*)in.data() + AES_BLOCK_SIZE * i, + (unsigned char*)out.data() + AES_BLOCK_SIZE * i, + &aes_key, + AES_DECRYPT); + } + out = pkcs7UnPadding(out); + } + return true; +} + +bool QKxCipher::aesCbcEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + if (enc) { + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + QByteArray inTemp = pkcs7Padding(in, AES_BLOCK_SIZE); + QByteArray ivecTemp = ivec; // ivec will be changed. + out.resize(inTemp.size()); + AES_cbc_encrypt((const unsigned char*)inTemp.data(), + (unsigned char*)out.data(), + inTemp.size(), + &aes_key, + (unsigned char*)ivecTemp.data(), + AES_ENCRYPT); + } else { + AES_KEY aes_key; + if (AES_set_decrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + QByteArray ivecTemp = ivec; // ivec will be changed after encrypt. + out.resize(in.size()); + AES_cbc_encrypt((const unsigned char*)in.data(), + (unsigned char*)out.data(), + in.size(), + &aes_key, + (unsigned char*)ivecTemp.data(), + AES_DECRYPT); + + // remove PKCS7Padding + out = pkcs7UnPadding(out); + } + return true; +} + +bool QKxCipher::aesCfb1Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + int num = 0; + QByteArray ivecTemp = ivec; // ivec + int encVal = enc ? AES_ENCRYPT : AES_DECRYPT; + out.resize(in.size()); // + AES_cfb1_encrypt((const unsigned char*)in.data(), + (unsigned char*)out.data(), + in.size() * 8, + &aes_key, + (unsigned char*)ivecTemp.data(), + &num, + encVal); + return true; +} + +bool QKxCipher::aesCfb8Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + int num = 0; + QByteArray ivecTemp = ivec; + int encVal = enc ? AES_ENCRYPT : AES_DECRYPT; + out.resize(in.size()); + AES_cfb8_encrypt((const unsigned char*)in.data(), + (unsigned char*)out.data(), + in.size(), + &aes_key, + (unsigned char*)ivecTemp.data(), + &num, + encVal); + return true; +} + +bool QKxCipher::aesCfb128Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + int num = 0; + QByteArray ivecTemp = ivec; + int encVal = enc ? AES_ENCRYPT : AES_DECRYPT; + out.resize(in.size()); + AES_cfb128_encrypt((const unsigned char*)in.data(), + (unsigned char*)out.data(), + in.size(), + &aes_key, + (unsigned char*)ivecTemp.data(), + &num, + encVal); + return true; +} + +bool QKxCipher::aesOfb128Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + AES_KEY aes_key; + if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0) { + return false; + } + + Q_UNUSED(enc); + + int num = 0; + QByteArray ivecTemp = ivec; + out.resize(in.size()); + AES_ofb128_encrypt((const unsigned char*)in.data(), + (unsigned char*)out.data(), + in.size(), + &aes_key, + (unsigned char*)ivecTemp.data(), + &num); + return true; +} + + + +static bool EvpEncrypt(EVP_CIPHER_CTX *ctx, const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, const EVP_CIPHER *ciper, bool enc){ + if (enc) { + int ret = EVP_EncryptInit_ex(ctx, ciper, NULL, (const unsigned char*)key.data(), (const unsigned char*)ivec.data()); + if(ret != 1) { + return false; + } + + int mlen = 0; + out.resize(in.size() + AES_BLOCK_SIZE); + ret = EVP_EncryptUpdate(ctx, (unsigned char*)out.data(), &mlen, (const unsigned char*)in.data(), in.size()); + if(ret != 1) { + return false; + } + + int flen = 0; + ret = EVP_EncryptFinal_ex(ctx, (unsigned char *)out.data() + mlen, &flen); + if(ret != 1) { + return false; + } + out.resize(mlen + flen); + return true; + } else { + int ret = EVP_DecryptInit_ex(ctx, ciper, NULL, (const unsigned char*)key.data(), (const unsigned char*)ivec.data()); + if(ret != 1) { + return false; + } + int mlen = 0; + out.resize(in.size()); + ret = EVP_DecryptUpdate(ctx, (unsigned char*)out.data(), &mlen, (const unsigned char*)in.data(), in.size()); + if(ret != 1) { + return false; + } + int flen = 0; + ret = EVP_DecryptFinal_ex(ctx, (unsigned char *)out.data() + mlen, &flen); + if(ret != 1) { + return false; + } + out.resize(mlen + flen); + return true; + } +} + +bool QKxCipher::aesCtrEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + const EVP_CIPHER * cipher = nullptr; + if (key.size() == 16) { + cipher = EVP_aes_128_ctr(); + } else if (key.size() == 24) { + cipher = EVP_aes_192_ctr(); + } else { + cipher = EVP_aes_256_ctr(); + } + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + bool on = EvpEncrypt(ctx, in, out, key, ivec, cipher, enc); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return on; +} + +bool QKxCipher::aesGcmEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + const EVP_CIPHER * cipher = nullptr; + if (key.size() == 16) { + cipher = EVP_aes_128_gcm(); + } else if (key.size() == 24) { + cipher = EVP_aes_192_gcm(); + } else { + cipher = EVP_aes_256_gcm(); + } + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + bool on= EvpEncrypt(ctx, in, out, key, ivec, cipher, enc); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return on; +} + +bool QKxCipher::aesXtsEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + const EVP_CIPHER * cipher = nullptr; + if (key.size() == 16) { + cipher = EVP_aes_128_xts(); + } else { + cipher = EVP_aes_256_xts(); + } + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + bool on = EvpEncrypt(ctx, in, out, key, ivec, cipher, enc); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return on; +} + +bool QKxCipher::aesOcbEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(!(key.size() == 16 || key.size() == 24 || key.size() == 32)) { + return false; + } + if(ivec.size() != 16) { + return false; + } + + const EVP_CIPHER * cipher = nullptr; + if (key.size() == 16) { + cipher = EVP_aes_128_ocb(); + } else if (key.size() == 24) { + cipher = EVP_aes_192_ocb(); + } else { + cipher = EVP_aes_256_ocb(); + } + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + bool on = EvpEncrypt(ctx, in, out, key, ivec, cipher, enc); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return on; +} + + +const int DES_BLOCK_SIZE = 8; + +static void setKey(const QByteArray &key, DES_key_schedule &sch1, DES_key_schedule &sch2, DES_key_schedule &sch3) +{ + const_DES_cblock key1, key2, key3; + memcpy(key1, key.data(), 8); + memcpy(key2, key.data() + 8, 8); + memcpy(key3, key.data() + 16, 8); + + DES_set_key_unchecked(&key1, &sch1); + DES_set_key_unchecked(&key2, &sch2); + DES_set_key_unchecked(&key3, &sch3); +} + + + +bool QKxCipher::tripleDesEcbEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, bool enc) +{ + if(key.size() != 24) { + return false; + } + + DES_key_schedule sch1, sch2, sch3; + setKey(key, sch1, sch2, sch3); + if (enc) { + QByteArray inTemp = pkcs7Padding(in, DES_BLOCK_SIZE); + out.resize(inTemp.size()); + for (int i = 0; i < inTemp.size() / DES_BLOCK_SIZE; i++) { + DES_ecb3_encrypt((const_DES_cblock*)(inTemp.constData() + i * DES_BLOCK_SIZE), + (DES_cblock *)(out.data() + i * DES_BLOCK_SIZE), + &sch1, &sch2, &sch3, DES_ENCRYPT); + } + } else { + out.resize(in.size()); + for (int i = 0; i < in.size() / DES_BLOCK_SIZE; i++) { + DES_ecb3_encrypt((const_DES_cblock*)(in.constData() + i * DES_BLOCK_SIZE), + (DES_cblock *)(out.data() + i * DES_BLOCK_SIZE), + &sch1, &sch2, &sch3, DES_DECRYPT); + } + out = pkcs7UnPadding(out); + } + return true; +} + +bool QKxCipher::tripleDesCbcEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(key.size() != 24) { + return false; + } + if(ivec.size() != 8) { + return false; + } + DES_key_schedule sch1, sch2, sch3; + setKey(key, sch1, sch2, sch3); + + QByteArray ivecTemp = ivec; + if (enc) { + QByteArray inTemp = pkcs7Padding(in, DES_BLOCK_SIZE); + out.resize(inTemp.size()); + DES_ede3_cbc_encrypt((const unsigned char *)inTemp.constData(), + (unsigned char *)out.data(), + inTemp.size(), &sch1, &sch2, &sch3, + (DES_cblock *)ivecTemp.data(), DES_ENCRYPT); + } else { + out.resize(in.size()); + DES_ede3_cbc_encrypt((const unsigned char *)in.constData(), + (unsigned char *)out.data(), + in.size(), &sch1, &sch2, &sch3, + (DES_cblock *)ivecTemp.data(), DES_DECRYPT); + + out = pkcs7UnPadding(out); + } + return true; +} + +bool QKxCipher::tripleDesCfb1Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(key.size() != 24) { + return false; + } + if(ivec.size() != 8) { + return false; + } + + DES_key_schedule sch1, sch2, sch3; + setKey(key, sch1, sch2, sch3); + + QByteArray ivecTemp = ivec; + int encVal = enc ? DES_ENCRYPT : DES_DECRYPT; + out.resize(in.size()); + DES_ede3_cfb_encrypt((const unsigned char *)in.constData(), + (unsigned char *)out.data(), + 8, in.size(), &sch1, &sch2, &sch3, + (DES_cblock *)ivecTemp.data(), encVal); + return true; +} + +bool QKxCipher::tripleDesCfb64Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(key.size() != 24) { + return false; + } + if(ivec.size() != 8) { + return false; + } + + DES_key_schedule sch1, sch2, sch3; + setKey(key, sch1, sch2, sch3); + + int num = 0; + QByteArray ivecTemp = ivec; + int encVal = enc ? DES_ENCRYPT : DES_DECRYPT; + out.resize(in.size()); + DES_ede3_cfb64_encrypt((const unsigned char *)in.constData(), + (unsigned char *)out.data(), + in.size(), &sch1, &sch2, &sch3, + (DES_cblock *)ivecTemp.data(), &num, encVal); + return true; +} + +bool QKxCipher::tripleDesOfb64Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc) +{ + if(key.size() != 24) { + return false; + } + if(ivec.size() != 8) { + return false; + } + + Q_UNUSED(enc) + + DES_key_schedule sch1, sch2, sch3; + setKey(key, sch1, sch2, sch3); + + int num = 0; + QByteArray ivecTemp = ivec; + out.resize(in.size()); + DES_ede3_ofb64_encrypt((const unsigned char *)in.constData(), + (unsigned char *)out.data(), + in.size(), &sch1, &sch2, &sch3, + (DES_cblock *)ivecTemp.data(), &num); + return true; +} + +bool QKxCipher::rc4Encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, bool enc) +{ + RC4_KEY enkey; + + RC4_set_key(&enkey, key.length(), (unsigned char*)key.data()); + out.resize(in.length()); + RC4(&enkey, in.length(), (const unsigned char*)in.data(), (unsigned char*)out.data()); + return true; +} + +bool QKxCipher::blowfishEcbEncrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray& ivec, bool enc) { + if(ivec.length() != 8) { + return false; + } + if(enc) { + BF_KEY dekey; + BF_set_key(&dekey, key.length(), (const unsigned char*)key.data()); + out.resize(in.length()); + QByteArray tmpVec = ivec; + BF_cbc_encrypt((const unsigned char*)in.data(), (unsigned char*)out.data(), (long)in.length(), &dekey, (unsigned char*)tmpVec.data(), BF_ENCRYPT); + }else{ + BF_KEY dekey; + BF_set_key(&dekey, key.length(), (const unsigned char*)key.data()); + out.resize(in.length()); + QByteArray tmpVec = ivec; + BF_cbc_encrypt((const unsigned char*)in.data(), (unsigned char*)out.data(), (long)in.length(), &dekey, (unsigned char*)tmpVec.data(), BF_DECRYPT); + } + return true; +} + +void QKxCipher::test() +{ + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesEcbEncrypt(in, out, key, true); + aesEcbEncrypt(out, out2, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesCbcEncrypt(in, out, key, key, true); + aesCbcEncrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesCfb1Encrypt(in, out, key, key, true); + aesCfb1Encrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesCfb8Encrypt(in, out, key, key, true); + aesCfb8Encrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesCfb128Encrypt(in, out, key, key, true); + aesCfb128Encrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesOfb128Encrypt(in, out, key, key, true); + aesOfb128Encrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesCtrEncrypt(in, out, key, key, true); + aesCtrEncrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesGcmEncrypt(in, out, key, key, true); + aesGcmEncrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesXtsEncrypt(in, out, key, key, true); + aesXtsEncrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + aesOcbEncrypt(in, out, key, key, true); + aesOcbEncrypt(out, out2, key, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 24); + tripleDesEcbEncrypt(in, out, key, true); + tripleDesEcbEncrypt(out, out2, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123", ivec="123"; + key = makeBytes(key, 24); + ivec = makeBytes(ivec, 8); + tripleDesCbcEncrypt(in, out, key, ivec, true); + tripleDesCbcEncrypt(out, out2, key, ivec, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123", ivec="123"; + key = makeBytes(key, 24); + ivec = makeBytes(ivec, 8); + tripleDesCfb1Encrypt(in, out, key, ivec, true); + tripleDesCfb1Encrypt(out, out2, key, ivec, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123", ivec="123"; + key = makeBytes(key, 24); + ivec = makeBytes(ivec, 8); + tripleDesCfb64Encrypt(in, out, key, ivec, true); + tripleDesCfb64Encrypt(out, out2, key, ivec, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123", ivec="123"; + key = makeBytes(key, 24); + ivec = makeBytes(ivec, 8); + tripleDesOfb64Encrypt(in, out, key, ivec, true); + tripleDesOfb64Encrypt(out, out2, key, ivec, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123"; + key = makeBytes(key, 16); + rc4Encrypt(in, out, key, true); + rc4Encrypt(out, out2, key, false); + Q_ASSERT(in == out2); + } + { + QByteArray in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + QByteArray out, out2, key = "123", ivec="123"; + key = makeBytes(key, 16); + ivec = makeBytes(ivec, 8); + blowfishEcbEncrypt(in, out, key, ivec, true); + blowfishEcbEncrypt(out, out2, key, ivec, false); + Q_ASSERT(in == out2); + } +} diff --git a/kxutil/qkxcipher.h b/kxutil/qkxcipher.h new file mode 100644 index 0000000..abd261a --- /dev/null +++ b/kxutil/qkxcipher.h @@ -0,0 +1,55 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QKXCIPHER_H +#define QKXCIPHER_H + +#include "qkxutil_private.h" + +#include + +class KXUTIL_EXPORT QKxCipher +{ +public: + static int pkcs7PaddedLength(int dataLen, int alignSize); + static QByteArray pkcs7Padding(const QByteArray &in, int alignSize); + static QByteArray pkcs7UnPadding(const QByteArray &in); + static QByteArray makeBytes(const QByteArray& pass, int cnt); + // aes + // key.size() == 16 || key.size() == 24 || key.size() == 32 + static bool aesEcbEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, bool enc); + static bool aesCbcEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesCfb1Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesCfb8Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesCfb128Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesOfb128Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesCtrEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesGcmEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesXtsEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool aesOcbEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + + // des + static bool tripleDesEcbEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, bool enc); + static bool tripleDesCbcEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool tripleDesCfb1Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool tripleDesCfb64Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + static bool tripleDesOfb64Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + + // rc + static bool rc4Encrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, bool enc); + + //blowfish + static bool blowfishEcbEncrypt(const QByteArray& in, QByteArray& out, const QByteArray &key, const QByteArray& ivec, bool enc); + + static void test(); +}; + +#endif // QKXCIPHER_H diff --git a/kxutil/qkxsetting.cpp b/kxutil/qkxsetting.cpp index 622f7a2..0f28248 100644 --- a/kxutil/qkxsetting.cpp +++ b/kxutil/qkxsetting.cpp @@ -35,7 +35,7 @@ void QKxSetting::setValue(const QString &key, const QVariant &v) QVariant QKxSetting::value(const QString &key, const QVariant &defval) { - qDebug() << "fileName" << gSettings->fileName(); + //qDebug() << "fileName" << gSettings->fileName(); return gSettings->value(key, defval); } diff --git a/woterm/CMakeLists.txt b/woterm/CMakeLists.txt index 8c9c35d..c6efb72 100644 --- a/woterm/CMakeLists.txt +++ b/woterm/CMakeLists.txt @@ -83,7 +83,17 @@ set(SOURCE_FILES qwoloadingwidget.cpp qwoshortcutmodel.cpp qwoshortcutdelegate.cpp + qwodbsftpuploadsync.cpp + qwodbsftpdownsync.cpp + qwodbmergemodel.cpp + qwodbidentitymergemodel.cpp + qwodbgroupmergemodel.cpp + qwodbservermergemodel.cpp + qwodbsftpdownlistdialog.cpp + qwodbbackupdialog.cpp + qwodbsftpdetaildialog.cpp qwodbrestoredialog.cpp + qwodbpowerrestoredialog.cpp qwogroupinputdialog.cpp qwotelnet.cpp qwoutils.cpp @@ -160,7 +170,17 @@ set(HEADER_FILES qwoloadingwidget.h qwoshortcutmodel.h qwoshortcutdelegate.h + qwodbsftpuploadsync.h + qwodbsftpdownsync.h + qwodbmergemodel.h + qwodbidentitymergemodel.h + qwodbgroupmergemodel.h + qwodbservermergemodel.h + qwodbsftpdownlistdialog.h + qwodbbackupdialog.h + qwodbsftpdetaildialog.h qwodbrestoredialog.h + qwodbpowerrestoredialog.h qwogroupinputdialog.h qwotelnet.h qwoutils.h @@ -197,7 +217,11 @@ set(OTHER_FILES qwosftptransferwidget.ui qwovnctoolform.ui qwovncftpwidget.ui + qwodbbackupdialog.ui + qwodbsftpdetaildialog.ui qwodbrestoredialog.ui + qwodbpowerrestoredialog.ui + qwodbsftpdownlistdialog.ui qwogroupinputdialog.ui ) diff --git a/woterm/language/update.bat b/woterm/language/update.bat index 6025eed..84a0259 100644 --- a/woterm/language/update.bat +++ b/woterm/language/update.bat @@ -7,6 +7,6 @@ set path_script=%~dp0 echo %path_script% cd %path_script% -lupdate -recursive %path_script%\..\ -I %path_script%\..\..\kxftp -I %path_script%\..\..\kxvnc -I %path_script%\..\..\kxutil -I %path_script%\..\..\kxterm -ts %path_script%woterm_en.ts -lupdate -recursive %path_script%\..\ -I %path_script%\..\..\kxftp -I %path_script%\..\..\kxvnc -I %path_script%\..\..\kxutil -I %path_script%\..\..\kxterm -ts %path_script%woterm_zh.ts +rem lupdate -recursive %path_script%\..\ -I %path_script%\..\..\kxftp -I %path_script%\..\..\kxvnc -I %path_script%\..\..\kxutil -I %path_script%\..\..\kxterm -ts %path_script%woterm_en.ts +lupdate -recursive %path_script%\..\ %path_script%\..\..\kxftp %path_script%\..\..\kxvnc %path_script%\..\..\kxutil %path_script%\..\..\kxterm -ts %path_script%woterm_zh.ts pause \ No newline at end of file diff --git a/woterm/language/woterm_zh.qm b/woterm/language/woterm_zh.qm index c2b5549058c8dd2d36effc163a084f128c2a4d03..d8d5180aa3f14c7685403806b02cfe61ac546d4d 100644 GIT binary patch literal 56398 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 delta 4927 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 diff --git a/woterm/language/woterm_zh.ts b/woterm/language/woterm_zh.ts index 85bc5b6..20ff8bc 100644 --- a/woterm/language/woterm_zh.ts +++ b/woterm/language/woterm_zh.ts @@ -4,17 +4,35 @@ English - + English 简体中文 + + QDBMergeActionDelegate + + + Add + 增加 + + + + Remove + 移除 + + + + Replace + 替换 + + QKxConfirmWidget Form - + 表单 @@ -33,6 +51,355 @@ + + QKxFtpDownload + + + Failed to open file + + + + + Bad Size + 大小错误 + + + + QKxFtpLocalModel + + + FileName + 文件名 + + + + Size + 大小 + + + + Created Date + 创建日期 + + + + QKxFtpRemoteModel + + + FileName + 文件名 + + + + Size + 大小 + + + + Created Date + 创建日期 + + + + QKxFtpTransferModel + + + Type + 类型 + + + + Status + 状态 + + + + Progress + 进度 + + + + File Size + 文件大小 + + + + Local File + 本地文件 + + + + Remote File + 远程文件 + + + + QKxFtpTransferWidget + + + File Transfer + 文件传输 + + + + Local folder + 本地文件夹 + + + + Remote folder + 远程文件夹 + + + + File transfer + 文件传输 + + + + Upload + 上传 + + + + + Create Directory + 创建文件夹 + + + + Browser Directory + 浏览文件夹 + + + + + Refresh + 刷新 + + + + directory name + 文件夹名 + + + + + Please input a directory name + 请输入一个文件夹名称 + + + + + + + + + information + 信息 + + + + + the new directory name should be empty! + 新文件夹名不能为空! + + + + Please select a remote path to upload + 请选择一个上传的远程路径 + + + + The upload file number can't over 500 + 上传文件数不能超过500个 + + + + tip + 提示 + + + + Please select a directory to open. + 请选择一个可打开的文件夹 + + + + Download + 下载 + + + + Directory name + 文件夹名称 + + + + Please select a local path to save download files + 请选择一个保存路径 + + + + The number of selected files cannot over 500. + 所选文件数量超过500。 + + + + + Start + 开始 + + + + Stop + 停止 + + + + Restart + 重新开始 + + + + Remove + 移除 + + + + Stop all + 停止所有 + + + + Start all + 开始所有 + + + + Remove all + 移除所有 + + + + QKxFtpUpload + + + Failed to open file: + 无法打开文件 + + + + QKxMessageBox + + + Ok + 确定 + + + + Save + 保存 + + + + Save all + 保存所有 + + + + Open + 打开 + + + + Yes + 确认 + + + + Yes to all + 确认所有 + + + + No + + + + + No to all + 否定所有 + + + + Abort + 放弃 + + + + Retry + 重试 + + + + Ignore + 忽略 + + + + Close + 关闭 + + + + Cancel + 取消 + + + + Discard + 放弃 + + + + Help + 帮助 + + + + Apply + 应用 + + + + Reset + 重置 + + + + Restore defaults + 恢复默认 + + + + QKxSearch + + + Form + 表单 + + + + Case + 区分大小写 + + + + Regular + 正则 + + QObject @@ -64,261 +431,1105 @@ 高清模式 - - General clear mode - 普清模式 + + General clear mode + 普清模式 + + + + Fast mode + 流畅模式 + + + + Super fast mode + 极速模式 + + + + Classics 16bit mode + 经典16位模式 + + + + Classics 15bit mode + 经典15位模式 + + + + Classics 8bit mode + 经典8位模式 + + + + Classics pallete mode + 经典调色板模式 + + + + Ultimate version + 旗舰版 + + + + this is the feature of the ultimate version, please upgrade to latest version. + 此为旗舰版功能,请升级至最新版本。 + + + + Password input + 密码输入 + + + + The current records are exactly the same. + 当前记录是完全相同的。 + + + + Number of records that can be added: %1 + 可添加的记录数量:%1 + + + + Number of records that can be removed: %1 + 可移除的记录数量:%1 + + + + Number of records that can be replaced: %1 + 可替换的记录数量:%1 + + + + Number of records that are the same: %1 + 相同的记录数量:%1 + + + + QPowerVNC + + + Password + 密码 + + + + QRLoginPtyClient + + + Failed to bind to local port for no permission, switch to root user and try it again? + 没有权限绑定本地端口,请切换至超级用户权限 + + + + Failed to bind to local port for unknow reasion + 无法绑定本地端口,原因未知。 + + + + Failed to connect to remote server + 无法连接远程服务器 + + + + QSshAuthClient + + Failed to connect server : %1 + 无法连接服务:%1 + + + + QWoAboutDialog + + + About + 关于 + + + + Current version: + 当前版本: + + + + Latest version: + 最新版本: + + + + Offcie website: + 官网: + + + + Check Version + 检查版本 + + + version check + 版本检查 + + + Failed For %1 + 失败原因 + + + Found New Version: %1, Try To Download? + 发现新版本:%1,现在去下载? + + + No New Version + 已经是最新版本 + + + TextLabel + 标签 + + + + QWoAdminDialog + + + Dialog + 对话框 + + + + This password is very important and must be remembered. + 该密码非常重要,需妥善保存。 + + + + Password: + 密码: + + + + save + 保存 + + + + Login when startup + 软件启动时,需要登录。 + + + + Look up password + 查看会话密码。 + + + + Close + 关闭 + + + + Administator + 管理员 + + + + Parameter error + 参数错误 + + + + The password should not empty. + 密码不能为空。 + + + + QWoBaseToolForm + + + Form + + + + + + + + + ... + ... + + + + QWoCommandLineInput + + + Form + Form + + + + QWoDBGroupMergeModel + + + name + 名字 + + + + orderNum + 顺序 + + + + QWoDBIdentityMergeModel + + + name + 名字 + + + + prvKey + 私有密钥 + + + + QWoDBMergeModel + + + Local + 本地 + + + + Remote + 远程 + + + + Operate + 操作 + + + + QWoDBPowerRestoreDialog + + + Dialog + 对话框 + + + + Local file: + 本地文件: + + + + Remote file: + 远程文件: + + + + + + ... + ... + + + + + Decrypt type: + 解密类型: + + + + + Decrypt key: + 解密密钥: + + + + Decrypt again + 重试解密 + + + + Merge mode + 合并模式 + + + + Show diffent record + 显示差异记录 + + + + Show full record + 显示全部记录 + + + + Replace + 替换 + + + + Project name: + 项目名: + + + + Last step + 上一步 + + + + Apply + 应用 + + + + + Cancel + 取消 + + + + Type: + 类型: + + + + File name: + 文件名: + + + + Server: + 服务器: + + + + Next step + 下一步 + + + + Database restore + 数据库恢复 + + + + sftp server + SFTP服务器 + + + + local file + 本地文件 + + + + Session list + 会话列表 + + + + Group list + 分组列表 + + + + Identity file list + 证书文件列表 + + + + + Parameter error + 参数错误 + + + + the server address should not be empty. + 服务器地址不能为空。 + + + + the crypt key should not be empty. + 密钥不能为空。 + + + + Error + 错误 + + + + + Failed to open file:%1 + 文件打开失败:%1 + + + + + + Decryption error + 解密错误 + + + + Failed to decrypt the backup file:%1. + 备份文件解密失败:%1 + + + + Failed to write decrypt result to file:%1 + 解密文件写入失败:%1 + + + + Merge information + 合并信息 + + + + QWoDBRestoreDialog + + + Dialog + 对话框 + + + + Backup file path: + 备份文件路径: + + + + Restore + 恢复 + + + + Database Restore + 数据库恢复 + + + + Restore Session Database + 恢复数据库 + + + + + + + Restore information + 恢复信息 + + + + please select a backup file to restore + 选择一个备份文件去恢复 + + + + The backup file is incorrect or corrupt + 备份文件已经损坏或格式不符合要求。 + + + + success to restore database. + 数据库恢复成功。 + + + + failed to restore database. + 数据库恢复失败。 + + + + QWoDBServerMergeModel + + + groupName + 组名 + + + + host + 主机名 + + + + name + 名字 + + + + port + 端口 + + + + baudRate + 波特率 + + + + dataBits + 数据位 + + + + memo + 备忘 + + + + parity + 校验位 + + + + stopBits + 停止位 + + + + flowControl + 流控协议 + + + + proxyJump + 跳板机 + + + + loginName + 登录名 + + + + identityFile + 证书文件 + + + + type + 类型 + + + + QWoDBSftpDownListDialog + + + Dialog + 对话框 + + + + Reflesh + 刷新 + + + + Select + 选择 + + + + Cancel + 取消 + + + + Backup file list + + + + + Parameter error + 参数错误 + + + + Please select a backup file. + 请选择一个备份文件。 + + + + QWoDBSftpDownSync + + + Ready to fetch the latest version information. + 正获取最新的版本信息。 + + + Failed to upload backup file. + 上传备份文件失败。 + + + + Failed to download the backup file. + 下载备份文件失败。 + + + + No relevant version information was found. + 没有找到相关的版本信息。 + + + + Unknow error was found. + 未知错误。 + + + + Success to download file:%1 + 下载文件成功:%1 + + + + Failed to download file:%1 + 下载文件失败:%1 + + + + Success to list file. + 列举文件成功。 + + + + Failed to list file + 列举文件失败 + + + + + Failed to login remote server. + 登录远程服务器失败。 + + + + Ready to download file:%1 + 正下载文件:%1 + + + + QWoDBSftpRestoreListDialog + + Dialog + 对话框 + + + + QWoDBSftpUploadSync + + + ready to clone the db. + 正备份数据库。 + + + + Failed to clone the backup file:%1. + 备份数据失败。 + + + + Failed to read the backup file:%1. + 读取备份文件失败:%1 + + + + + + + + + + + Failed to encrypt the backup file:%1. + 加密备份文件失败:%1 + + + + Failed to write the encrypt file:%1. + 加密数据写入文件失败:%1 + + + + Ready to check server upload path. + 正检查上传路径。 + + + + Failed to upload backup file. + 上传备份文件失败。 + + + + The path is not exist:%1 + 路径不存在:%1 + + + + Failed to check path:%1. + 检查路径失败:%1。 + + + + Ready to check version information. + 正检查版本信息。 + + + + success to backup file:%1. + 备份文件成功。 + + + + Failed to upload the encrypt file:%1. + 上传加密文件失败:%1。 + + + + + Ready to write version information. + 正写入版本信息。 + + + + + Ready to upload the encrypt file. + 正上传加密文件。 + + + + Failed to write version information. + 写入版本信息失败。 + + + + Failed to login remote server. + 登录远程服务器失败。 + + + + QWoDbBackupDialog + + + Dialog + 对话框 + + + + Type: + 类型: + + + + File name: + 文件名: + + + + + ... + ... + + + + Save + 保存 + + + + Server: + 服务器: + + + + Encrypt type: + 加密类型: + + + + Encrypt key: + 加密密钥: + + + + The encrypted file will be uploaded to the target server. Remember your encryption type and encryption key. + 加密文件将被上传至目标服务器,请务必记住加密类型和密钥。 + + + + Upload + 上传 + + + + Database backup + 数据库备份 + + + + sftp server + sftp服务器 - - Fast mode - 流畅模式 + + local file + 本地文件 - - Super fast mode - 极速模式 + + + Parameter error + 参数错误 - - Classics 16bit mode - 经典16位模式 + + the local file should not be empty. + 本地文件不能为空。 - - Classics 15bit mode - 经典15位模式 + + Failure + 失败 - - Classics 8bit mode - 经典8位模式 + + failed to backup the database. + 备份数据库失败。 - - Classics pallete mode - 经典调色板模式 + + Success + 成功 - - Ultimate version - 旗舰版 + + success to backup the file. + 备份文件失败。 - - this is the feature of the ultimate version, please upgrade to latest version. - 此为旗舰版功能,请升级至最新版本。 + + the encryption key should not be empty. + 加密密钥不能为空。 - - Password input - 密码输入 + + Local file + 本地文件 + + + + SQLite (*.db) + SQLite (*.db) - QRLoginPtyClient + QWoDbSftpDetailDialog - - Failed to bind to local port for no permission, switch to root user and try it again? - 没有权限绑定本地端口,请切换至超级用户权限 + + Dialog + 对话框 - - Failed to bind to local port for unknow reasion - 无法绑定本地端口,原因未知。 + + Host: + 主机名: - - Failed to connect to remote server - 无法连接远程服务器 + + Port: + 端口: - - - QSshAuthClient - Failed to connect server : %1 - 无法连接服务:%1 + + Name: + 名称: - - - QWoAboutDialog - - About - 关于 + + Type: + 类型: - - Current version: - 当前版本: + + Password: + 密码: - - Latest version: - 最新版本: + + Identity file: + 证书文件: - - Offcie website: - 官网: + + ... + ... - - Check Version - 检查版本 + + Path: + 路径: - version check - 版本检查 + + Test connection + 测试连接 - Failed For %1 - 失败原因 + + Apply + 应用 - Found New Version: %1, Try To Download? - 发现新版本:%1,现在去下载? + + Close + 关闭 - No New Version - 已经是最新版本 + + Sftp server + Sftp服务器 - TextLabel - 标签 + + Password + 密码 - - - QWoAdminDialog - - Dialog - 对话框 + + Identity file + 证书文件 - - This password is very important and must be remembered. - 该密码非常重要,需妥善保存。 + + + + + + Parameter error + 参数错误 - - Password: - 密码: + + the host parameter should not be empty + 主机参数不能为空。 - - save - 保存 + + the login name parameter should not be empty + 登录名参数不能为空。 - - Login when startup - 软件启动时,需要登录。 + + the password parameter should not be empty + 密码参数不能为空。 - - Look up password - 查看会话密码。 + + the identity file parameter should not be empty + 证书文件参数不能为空。 - - Close - 关闭 + + the save path parameter should not be empty + 保存路径的参数不能为空。 - - Administator - 管理员 + + Failure report + 失败通知 - - Parameter error - 参数错误 + + Failed + 失败 - - The password should not empty. - 密码不能为空。 + + + Success + 成功 - - - QWoBaseToolForm - - Form - + + Success to create it. + 创建成功。 - - - - - - ... - ... + + + + Failure + 失败 - - - QWoCommandLineInput - - Form - Form + + Failed to create it, please do it by manual. + 创建失败,请手动操作。 - - - QWoDBRestoreDialog - - Dialog - 对话框 + + Failed to open the target path, try to create it? + 无法打开目标路径,请先创建它? - - Backup file path: - 备份文件路径: + + Please check it again and make sure the target path is a directory not a file. + 请再检查一遍并确保目标路径是一个目录而不是一个文件。 - - Restore - 恢复 + + It's a valid path to backup file. + 是一个有效的文件备份路径。 - - Database Restore - 数据库恢复 + + + + Error + 错误 - - Restore Session Database - 恢复数据库 + + Parmeter error + 参数错误 - - - - - Restore information - 恢复信息 + + The account information error + 帐号信息错误。 - - please select a backup file to restore - 选择一个备份文件去恢复 + + Test timeout + 测试超时 - - The backup file is incorrect or corrupt - 备份文件已经损坏或格式不符合要求。 + + Administrator + 管理员 - - success to restore database. - 数据库恢复成功。 + + Please create administrator's password first! + 请先创建管理员密码! - - failed to restore database. - 数据库恢复失败。 + + Please input the administrator's password + 请输入管理员密码 + + + + Password error! + 密码错误! @@ -436,45 +1647,45 @@ QWoHostInfoEdit - - + + Add 增加 - + Modify 修改 - + Password 密码 - - - + + + Info 信息 - + The name can't be empty 名字不能为空 - + The host can't be empty 主机名不能为空 - + The port should be at [10,65535] 端口范围必须在[10,65535]之间 - + Open File 打开文件 @@ -503,22 +1714,22 @@ QWoHostListModel - + Name 名字 - + Host 主机名 - + Type 类型 - + Memo 备忘 @@ -534,22 +1745,22 @@ QWoHostTreeModel - + Name 名字 - + Host 主机名 - + Memo 备忘 - + Type 类型 @@ -838,7 +2049,7 @@ - + Option 选项 @@ -864,7 +2075,7 @@ - + Upgrade to ultimate version 升级至旗舰版 @@ -968,134 +2179,134 @@ WoTerm - + WoTerm ultimate beta WoTerm 旗舰版 beta - + WoTerm free WoTerm 免费版 - + Session Manager 会话管理 - + Confirm 确认 - + Exit Or Not? 退出或取消 - + The current version is free. It is recommended to upgrade to the ultimate version. 当前版本是免费版本,建议升级至旗舰版。 - + Version check 版本检查 - + a new version of %1 is found, do you want to update it? 发现新版本%1,是否需要更新? - + Backup Session Database 备份数据库 - + Failure 失败 - + failed to backup the session list. 备份数据库失败。 - + Language 语言 - + The language has been changed, restart application to take effect right now. 语言已经被修改,需要重启才能生效,是否立即重启? - + Password input 密码输入 - + Login to the configuration of administrator for the first time. Please input password to activate it. 首次使用管理员配置需要输入新密码激活它 - + Please input password to verify. 请输入密码进行验证。 - + Password error 参数错误 - + the password is not right. 密码不正确。 - + Toolbar 工具栏 - + Administrator login 管理员登录 - + Please input password to login application. 请输入密码登录应用 - + Login failure 登录失败 - + The password is wrong, %1 times left to try. 密码错误,剩余%1次尝试机会。 - + New 新建 - + Manage 管理 - + List 列表 @@ -1104,7 +2315,7 @@ Please input password to activate it. 设置 - + Keys 证书管理 @@ -1147,12 +2358,12 @@ Please input password to activate it. 完成 - + Tip 提示 - + The Password is Empty, continue to finish? 当前密码为空,是否继续执行? @@ -1161,60 +2372,60 @@ Please input password to activate it. QWoPowerSftp An attempt to create an already existing file or directory has been made - 已经存在同名的目标文件或目录 + 已经存在同名的目标文件或文件夹 - + The file is already exist 文件已经存在 - + No such file or directory path exists - 目标文件或目录不存在 + 目标文件或文件夹不存在 - + Permission denied 没有权限 - + Generic failure 常规性失败 - + Garbage received from server 接收到与协议不符合的内容 - + No connection has been set up 还没有建立连接 - + There was a connection, but we lost it 连接已经断开 - + Operation not supported by the server 服务端不支持该操作 - + Invalid file handle 无效的文件句柄 - + We are trying to write on a write-protected filesystem 文件已经被系统保护,无法写入任何内容 - + No media in remote drive 远程服务器没有相关文件 @@ -1231,12 +2442,12 @@ Please input password to activate it. 无法打开本地文件 - + Error allocating SFTP session: %1 会话不存在:%1 - + Error initializing SFTP session: %1 会话初始化失败:%1 @@ -1271,7 +2482,7 @@ Please input password to activate it. Open Directory - 打开目录 + 打开文件夹 @@ -2191,8 +3402,8 @@ Please input password to activate it. - - + + Password 密码 @@ -2223,79 +3434,99 @@ Please input password to activate it. 默认:590x - + + Administrator + 管理员 + + + + Please create administrator's password first! + 请先创建管理员密码 + + + Please input the administrator password 请输入管理员密码 - + + Error + 错误 + + + + Password error! + 密码错误! + + + Parameter error 参数错误 - + The group name is already exist. 组名已经存在。 - - - - - - - - - - - + + + + + + + + + + + Info 信息 - + The identity file can't be empty 证书文件不能为空 - + The identify file can't be empty 证书文件不能为空 - + failed to find the identify file 无法查到证书文件 - - + + ProxyJump can't be same with name, change to another one. 名字不能与跳板机名字相同,请更换其它跳板机名字 - - + + The userName can't be empty 登录名不能为空 - + The name can't be empty 会话名不能为空 - + The host can't be empty 主机名不能为空 - + The port should be at [10,65535] 端口取值范围必须是[10,65535]之间 - + The session name had been used, Change to another name. 会话名已经被占用,请更换其它名字 @@ -2413,28 +3644,28 @@ Please input password to activate it. QWoSftpQueueModel - - - - + + + + Local: 本地: - - - - + + + + Remote: 远程: - + Type 类型 - + FileName 文件名 @@ -2559,7 +3790,7 @@ Please input password to activate it. Return to Home Directory - 返回主目录 + 返回主文件夹 @@ -2569,143 +3800,143 @@ Please input password to activate it. Enter the current directory - 进入目录 + 进入文件夹 Refresh the current directory - 刷新目录 + 刷新文件夹 - - + + The directory is out of sync with the terminal. - 目录不与终端同步 + 文件夹不与终端同步 - + Reject request 拒绝请求 - + Please wait until the current task is completed. 请等待当前任务完成。 - - + + Select all 选择所有 - - - - + + + + Back 返回 - - - - + + + + Refresh 刷新 - - + + Home Directory 返回主文件夹 - - + + Create Directory 创建文件夹 - - - + + + Upload 上传 - - + + Deselect all 取消所选 - - + + Remove selections 删除选中项 - - + + Home directory - 主目录 + 主文件夹 - - + + Create directory - 创建目录 + 创建文件夹 - + Try enter 尝试进入 - + directory name - 目录名 + 文件夹名 - + Please input a directory name - 请输入一个目录名称 + 请输入一个文件夹名称 - + information 信息 - + the new directory name should be empty! - 新目录名不能为空! + 新文件夹名不能为空! - - + + SFTP SFTP - - + + No items are currently selected. 当前没有选中项 - + Keep the directory synchronized with the terminal. - 保持目录与终端同步 + 保持文件夹与终端同步 Remove Directory - 删除文件夹 + 删除文件夹 - - + + Enter 进入 @@ -2714,29 +3945,29 @@ Please input password to activate it. 删除文件 - - + + Download 下载 - + Try Enter 尝度进入 - + New Session Multiplex 新建连接复用 - + Close 关闭 - - + + Error 错误 @@ -2745,55 +3976,55 @@ Please input password to activate it. 保存文件 - - + + FileExist 文件存在 - - + + has the same name in the target path. override it? 已经存在同名文件,是否覆盖? - + Upload information 上传信息 - - + + the follow files has exist: 以下文件已经存在: - + Save directory - 保存目录 + 保存文件夹 - + Parameter error 参数错误 - + Please select directory to save it. 请选择文件夹去保存 - + Download information 下载信息 - + Select files 选择文件 - + Select File 选择文件 @@ -2887,93 +4118,98 @@ Continue To Close It? QWoSshTermWidget - + Reconnection confirmation 重连确认 - + Continue to connect to the server? 继续连接远程服务器? - - + + Restore + 恢复 + + + + session list 会话列表 - + Error 错误 - + can't find the session, maybe it had been delete ago 无法找到相应会话信息 - + Select Files 选择文件 - + Open Directory 打开文件夹 - + Copy 复制 - + Paste 粘贴 - + Force Reconnect 强制重新联网 - + Split Vertical 垂直分割 - + Split Horizontal 水平分割 - + Add To Vertical 创建竖直窗口 - + Add To Horizontal 创建水平窗口 - + Sftp Assistant SFTP助手 - + Find... 查找... - + Edit 编辑 - + Zmodem abort Zmodem中止传输 @@ -2982,17 +4218,17 @@ Continue To Close It? 新建连接复用 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 @@ -3009,37 +4245,37 @@ Continue To Close It? ZModem中断传输 - + Close Session 关闭会话 - + Float This Tab 浮动此选项卡 - + Duplicate in new window 在新窗口中打开 - + New session multiplex 新建连接复用 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Files are transfering... 文件正在传输中... @@ -3378,7 +4614,7 @@ Continue To Close It? Enter Directory Name - 进入目录名 + 进入文件夹名 diff --git a/woterm/main.cpp b/woterm/main.cpp index 1aa7ee5..03d7ab1 100644 --- a/woterm/main.cpp +++ b/woterm/main.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include #include +#include "qkxcipher.h" + #include "qwoglobal.h" #include "qwoapplication.h" #include "qwomainwindow.h" @@ -32,6 +33,7 @@ #include "qwoutils.h" #include "qwossh.h" #include "qkxutils.h" +#include "qkxmessagebox.h" void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { diff --git a/woterm/qwoaboutdialog.cpp b/woterm/qwoaboutdialog.cpp index 9ce3d92..6d566f6 100644 --- a/woterm/qwoaboutdialog.cpp +++ b/woterm/qwoaboutdialog.cpp @@ -15,12 +15,12 @@ #include #include #include -#include #include #include #include "version.h" #include "qkxhttpclient.h" +#include "qkxmessagebox.h" #include "qwoutils.h" #include "qkxver.h" diff --git a/woterm/qwoadmindialog.cpp b/woterm/qwoadmindialog.cpp index f918695..ea72188 100644 --- a/woterm/qwoadmindialog.cpp +++ b/woterm/qwoadmindialog.cpp @@ -14,8 +14,7 @@ #include "qwosetting.h" #include "qkxbuttonassist.h" - -#include +#include "qkxmessagebox.h" QWoAdminDialog::QWoAdminDialog(QWidget *parent) : QDialog(parent), @@ -62,7 +61,7 @@ void QWoAdminDialog::onPasswordResetClicked() { QString pass = ui->pass->text(); if(pass.isEmpty()) { - QMessageBox::information(this, tr("Parameter error"), tr("The password should not empty.")); + QKxMessageBox::information(this, tr("Parameter error"), tr("The password should not empty.")); return; } QWoSetting::setAdminPassword(pass); diff --git a/woterm/qwodbbackupdialog.cpp b/woterm/qwodbbackupdialog.cpp new file mode 100644 index 0000000..6645831 --- /dev/null +++ b/woterm/qwodbbackupdialog.cpp @@ -0,0 +1,156 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbbackupdialog.h" +#include "ui_qwodbbackupdialog.h" +#include "qwodbsftpdetaildialog.h" +#include "qwosetting.h" +#include "qkxmessagebox.h" +#include "qwosshconf.h" +#include "qwodbsftpuploadsync.h" + +#include +#include +#include +#include + +QWoDbBackupDialog::QWoDbBackupDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QWoDbBackupDialog) +{ + Qt::WindowFlags flags = windowFlags(); + setWindowFlags(flags &~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); + setWindowTitle(tr("Database backup")); + ui->backupType->setModel(new QStringListModel(QStringList() << tr("sftp server") << tr("local file"), this)); + QObject::connect(ui->backupType, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged())); + onCurrentIndexChanged(); + QStringList crypts; + crypts.append("AES-CBC-256"); + crypts.append("AES-CTR-256"); + crypts.append("AES-GCM-256"); + crypts.append("DES-CBC"); + crypts.append("DES-ECB"); + crypts.append("DES-OFB64"); + crypts.append("RC4"); + crypts.append("Blowfish"); + ui->cryptType->setModel(new QStringListModel(crypts, this)); + ui->sftpServer->setReadOnly(true); + QObject::connect(ui->btnSftpDetail, SIGNAL(clicked()), this, SLOT(onSftpDetailButtonClicked())); + + QString lastFile = QWoSetting::value("DBBackup/lastLocalFile").toString(); + ui->pathLocal->setText(lastFile); + QString cryptType = QWoSetting::value("DBBackup/lastCryptType").toString(); + if(cryptType.isEmpty()) { + ui->cryptType->setCurrentIndex(0); + }else{ + ui->cryptType->setCurrentText(cryptType); + } + QString cryptKey = QWoSetting::value("DBBackup/lastCryptKey").toString(); + ui->cryptKey->setText(cryptKey); + + resetSftpUrl(); + + QObject::connect(ui->btnSave, SIGNAL(clicked()), this, SLOT(onFileSaveClicked())); + QObject::connect(ui->btnBrowser, SIGNAL(clicked()), this, SLOT(onFileBrowserClicked())); + QObject::connect(ui->btnUpload, SIGNAL(clicked()), this, SLOT(onFileUploadClicked())); +} + +QWoDbBackupDialog::~QWoDbBackupDialog() +{ + delete ui; +} + +void QWoDbBackupDialog::onCurrentIndexChanged() +{ + int idx = ui->backupType->currentIndex(); + if(idx == 0) { + // sftp server. + ui->sftpArea->show(); + ui->localArea->hide(); + }else{ + ui->sftpArea->hide(); + ui->localArea->show(); + } + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); +} + +void QWoDbBackupDialog::onAdjustLayout() +{ + adjustSize(); +} + +void QWoDbBackupDialog::onSftpDetailButtonClicked() +{ + QWoDbSftpDetailDialog dlg(this); + if(dlg.exec() != (QDialog::Accepted + 1)) { + resetSftpUrl(); + } +} + +void QWoDbBackupDialog::onFileSaveClicked() +{ + QString lastFile = ui->pathLocal->text(); + if(lastFile.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the local file should not be empty.")); + return; + } + QWoSetting::setValue("DBBackup/lastLocalFile", lastFile); + if(!QWoSshConf::instance()->backup(lastFile)) { + QKxMessageBox::warning(this, tr("Failure"), tr("failed to backup the database.")); + }else{ + QKxMessageBox::information(this, tr("Success"), tr("success to backup the file.")); + } +} + +void QWoDbBackupDialog::onFileUploadClicked() +{ + QString cryptType = ui->cryptType->currentText(); + QString cryptKey = ui->cryptKey->text(); + if(cryptKey.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the encryption key should not be empty.")); + return; + } + QWoSetting::setValue("DBBackup/lastCryptType", cryptType); + QWoSetting::setValue("DBBackup/lastCryptKey", cryptKey); + + if(m_sync == nullptr) { + m_sync = new QWoDBSftpUploadSync(this); + QObject::connect(m_sync, SIGNAL(infoArrived(int,int,QString)), this, SLOT(onInfoArrived(int,int,QString))); + } + m_sync->upload(cryptType, cryptKey); +} + +void QWoDbBackupDialog::onFileBrowserClicked() +{ + QString path = ui->pathLocal->text(); + QString fileName = QFileDialog::getSaveFileName(this, tr("Local file"), path, tr("SQLite (*.db)")); + if(fileName.isEmpty()) { + return; + } + ui->pathLocal->setText(fileName); +} + +void QWoDbBackupDialog::onInfoArrived(int action, int err, const QString &errDesc) +{ + ui->info->setText(errDesc); +} + +void QWoDbBackupDialog::resetSftpUrl() +{ + QVariantMap dm = QWoSetting::value("DBBackup/sftpDetail").toMap(); + QString host = dm.value("host").toString(); + QString name = dm.value("name").toString(); + QString path = dm.value("path", "~/woterm_db_backup").toString(); + QString port = dm.value("port", 22).toString(); + QString url = QString("sftp://%1@%2:%3?port=%4").arg(name, host, path, port); + ui->sftpServer->setText(url); +} diff --git a/woterm/qwodbbackupdialog.h b/woterm/qwodbbackupdialog.h new file mode 100644 index 0000000..70766a9 --- /dev/null +++ b/woterm/qwodbbackupdialog.h @@ -0,0 +1,46 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBBACKUPDIALOG_H +#define QWODBBACKUPDIALOG_H + +#include +#include + +namespace Ui { +class QWoDbBackupDialog; +} + +class QWoDBSftpUploadSync; +class QWoDbBackupDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QWoDbBackupDialog(QWidget *parent = nullptr); + ~QWoDbBackupDialog(); + +private slots: + void onCurrentIndexChanged(); + void onAdjustLayout(); + void onSftpDetailButtonClicked(); + void onFileSaveClicked(); + void onFileUploadClicked(); + void onFileBrowserClicked(); + void onInfoArrived(int action, int err, const QString& errDesc); +private: + void resetSftpUrl(); +private: + Ui::QWoDbBackupDialog *ui; + QPointer m_sync; +}; + +#endif // QWODBBACKUPDIALOG_H diff --git a/woterm/qwodbbackupdialog.ui b/woterm/qwodbbackupdialog.ui new file mode 100644 index 0000000..a726c2d --- /dev/null +++ b/woterm/qwodbbackupdialog.ui @@ -0,0 +1,375 @@ + + + QWoDbBackupDialog + + + + 0 + 0 + 378 + 336 + + + + Dialog + + + + 10 + + + + + 0 + + + + + Type: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + File name: + + + + + + + + + + ... + + + + :/woterm/resource/skin/dirs.png:/woterm/resource/skin/dirs.png + + + + + + + + + + 0 + 0 + + + + + 360 + 0 + + + + Qt::Horizontal + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Server: + + + + + + + + + + ... + + + + :/woterm/resource/skin/sftp.png:/woterm/resource/skin/sftp.png + + + + + + + + + 0 + + + + + Encrypt type: + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Encrypt key: + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + :/woterm/resource/skin/about.png + + + true + + + + + + + The encrypted file will be uploaded to the target server. Remember your encryption type and encryption key. + + + true + + + + + + + + + + + + + + + + + 360 + 0 + + + + Qt::Horizontal + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Upload + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + diff --git a/woterm/qwodbgroupmergemodel.cpp b/woterm/qwodbgroupmergemodel.cpp new file mode 100644 index 0000000..8bc872f --- /dev/null +++ b/woterm/qwodbgroupmergemodel.cpp @@ -0,0 +1,40 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbgroupmergemodel.h" + +QWoDBGroupMergeModel::QWoDBGroupMergeModel(QObject *parent) + : QWoDBMergeModel(parent) +{ + +} + +QString QWoDBGroupMergeModel::toString(const QVariantMap &dm) const +{ + QStringList names = {tr("name"),tr("orderNum")}; + QStringList lines; + for(auto it = dm.begin(); it != dm.end(); it++) { + QString key = it.key(); + QString txt = it.value().toString(); + if(key == "ct" + || key == "id" + || key == "syncFlag") { + continue; + } + if(txt.isEmpty()) { + continue; + } + QString line = tr(key.toUtf8()) + ":" + txt; + lines.append(line); + } + QString out = lines.join("\r\n"); + return out; +} diff --git a/woterm/qwodbgroupmergemodel.h b/woterm/qwodbgroupmergemodel.h new file mode 100644 index 0000000..0a50c33 --- /dev/null +++ b/woterm/qwodbgroupmergemodel.h @@ -0,0 +1,26 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBGROUPMERGEMODEL_H +#define QWODBGROUPMERGEMODEL_H + +#include "qwodbmergemodel.h" + +class QWoDBGroupMergeModel : public QWoDBMergeModel +{ + Q_OBJECT +public: + explicit QWoDBGroupMergeModel(QObject *parent = nullptr); +private: + virtual QString toString(const QVariantMap& data) const; +}; + +#endif // QWODBGROUPMERGEMODEL_H diff --git a/woterm/qwodbidentitymergemodel.cpp b/woterm/qwodbidentitymergemodel.cpp new file mode 100644 index 0000000..dd5543d --- /dev/null +++ b/woterm/qwodbidentitymergemodel.cpp @@ -0,0 +1,42 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbidentitymergemodel.h" + +QWoDBIdentityMergeModel::QWoDBIdentityMergeModel(QObject *parent) + : QWoDBMergeModel(parent) +{ + +} + +QString QWoDBIdentityMergeModel::toString(const QVariantMap &dm) const +{ + QStringList names = {tr("name"),tr("prvKey")}; + QStringList lines; + for(auto it = dm.begin(); it != dm.end(); it++) { + QString key = it.key(); + QString txt = it.value().toString(); + if(key == "ct" + || key == "delFlag" + || key == "dt" + || key == "id" + || key == "syncFlag") { + continue; + } + if(txt.isEmpty()) { + continue; + } + QString line = tr(key.toUtf8()) + ":" + txt; + lines.append(line); + } + QString out = lines.join("\r\n"); + return out; +} diff --git a/woterm/qwodbidentitymergemodel.h b/woterm/qwodbidentitymergemodel.h new file mode 100644 index 0000000..0c6b434 --- /dev/null +++ b/woterm/qwodbidentitymergemodel.h @@ -0,0 +1,27 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBIDENTITYMERGEMODEL_H +#define QWODBIDENTITYMERGEMODEL_H + +#include "qwodbmergemodel.h" + +class QWoDBIdentityMergeModel : public QWoDBMergeModel +{ + Q_OBJECT +public: + explicit QWoDBIdentityMergeModel(QObject *parent = nullptr); +private: + virtual QString toString(const QVariantMap& data) const; + +}; + +#endif // QWODBIDENTITYMERGEMODEL_H diff --git a/woterm/qwodbmergemodel.cpp b/woterm/qwodbmergemodel.cpp new file mode 100644 index 0000000..3f3d361 --- /dev/null +++ b/woterm/qwodbmergemodel.cpp @@ -0,0 +1,296 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbmergemodel.h" +#include "qwosshconf.h" + +#include +#include + +QWoDBMergeModel::QWoDBMergeModel(QObject *parent) + : QAbstractTableModel(parent) +{ + m_font = QGuiApplication::font(); +} + +void QWoDBMergeModel::setFont(const QFont &ft) +{ + m_font = ft; +} + +void QWoDBMergeModel::setData(const MergeInfo &mi) +{ + m_mi = mi; +} + +QString QWoDBMergeModel::reset(bool isFull) +{ + m_isFull = isFull; + QList lsvm = m_mi.result(isFull); + beginResetModel(); + m_datas.clear(); + QFontMetrics fm(m_font); + m_maxWidth = 0; + for(auto it = lsvm.cbegin(); it != lsvm.cend(); it++) { + QVariantMap dm = *it; + QVariantMap local = dm.value("local").toMap(); + QVariantMap remote = dm.value("remote").toMap(); + QString localLabel = toString(local); + QString remoteLabel = toString(remote); + QSize txtSize1 = fm.size(Qt::TextWordWrap, localLabel); + QSize txtSize2 = fm.size(Qt::TextWordWrap, remoteLabel); + int txtWidth = qMax(txtSize1.width(), txtSize2.width()); + int txtHeight = qMax(txtSize1.height(), txtSize2.height()); + if(txtWidth > m_maxWidth) { + m_maxWidth = txtWidth; + } + dm.insert("localLabel", localLabel); + dm.insert("remoteLabel", remoteLabel); + dm.insert("rowHeight", txtHeight); + dm.insert("colWidth", txtWidth); + m_datas.append(dm); + } + endResetModel(); + return m_mi.resultInformation(); +} + +void QWoDBMergeModel::clear() +{ + m_mi.clear(); + reset(false); +} + +/* +std::string sql = "update servers set type=@type,host=@host,port=@port,loginName=@loginName,"; +sql += "loginPassword=@loginPassword,identityFile=@identityFile,scriptFile=@scriptFile,script=@script,"; +sql += "proxyJump=@proxyJump,memo=@memo,property=@property,groupName=@groupName,baudRate=@baudRate,"; +sql += "dataBits=@dataBits,parity=@parity,stopBits=@stopBits,flowControl=@flowControl "; +sql += "where name=@name and delFlag=0"; +*/ + +static void qVariantMapMergeToHostInfo(HostInfo& hi, QVariantMap dm) { + hi.name = dm.value("name").toString(); + hi.type = (EHostType)dm.value("type").toInt(); + hi.host = dm.value("host").toString(); + hi.port = dm.value("port").toInt(); + hi.user = dm.value("loginName").toString(); + hi.password = dm.value("loginPassword").toString(); + hi.identityFile = dm.value("identityFile").toString(); + hi.scriptFile = dm.value("scriptFile").toString(); + hi.script = dm.value("script").toString(); + hi.proxyJump = dm.value("proxyJump").toString(); + hi.memo = dm.value("meno").toString(); + + hi.property = dm.value("property").toString(); + hi.group = dm.value("groupName").toString(); + hi.baudRate = dm.value("baudRate").toString(); + hi.dataBits = dm.value("dataBits").toString(); + hi.parity = dm.value("parity").toString(); + hi.stopBits = dm.value("stopBits").toString(); + hi.flowControl = dm.value("flowControl").toString(); +} + +static HostInfo qVariantMapToHostInfo(QVariantMap dm) { + HostInfo hi; + hi.name = dm.value("name").toString(); + hi.type = (EHostType)dm.value("type").toInt(); + hi.host = dm.value("host").toString(); + hi.port = dm.value("port").toInt(); + hi.user = dm.value("loginName").toString(); + hi.password = dm.value("loginPassword").toString(); + hi.identityFile = dm.value("identityFile").toString(); + hi.scriptFile = dm.value("scriptFile").toString(); + hi.script = dm.value("script").toString(); + hi.proxyJump = dm.value("proxyJump").toString(); + hi.memo = dm.value("meno").toString(); + hi.property = dm.value("property").toString(); + hi.group = dm.value("groupName").toString(); + hi.baudRate = dm.value("baudRate").toString(); + hi.dataBits = dm.value("dataBits").toString(); + hi.parity = dm.value("parity").toString(); + hi.stopBits = dm.value("stopBits").toString(); + hi.flowControl = dm.value("flowControl").toString(); + return hi; +} + +void QWoDBMergeModel::apply() +{ + // adds + for(auto it = m_mi.rhave.begin(); it != m_mi.rhave.end(); it++) { + QVariantMap& dm = *it; + QVariantMap remote = dm.value("remote").toMap(); + QString action = dm.value("mergeAction").toString(); + if(action == "add") { + dm.insert("mergeAction", "done"); + HostInfo hi = qVariantMapToHostInfo(remote); + QWoSshConf::instance()->append(hi); + } + } + + // remove + for(auto it = m_mi.remove.begin(); it != m_mi.remove.end(); ) { + QVariantMap dm = *it; + QVariantMap local = dm.value("local").toMap(); + QString name = local.value("name").toString(); + QWoSshConf::instance()->removeServer(name); + it = m_mi.remove.erase(it); + } + + // replace + for(auto it = m_mi.replace.begin(); it != m_mi.replace.end(); it++) { + QVariantMap& dm = *it; + QVariantMap remote = dm.value("remote").toMap(); + QString action = dm.value("mergeAction").toString(); + if(action == "replace") { + dm.insert("mergeAction", "done"); + HostInfo hi; + qVariantMapMergeToHostInfo(hi, remote); + QWoSshConf::instance()->modify(hi); + } + } +} + +bool QWoDBMergeModel::runAction(int action, const QModelIndex &idx) +{ + int row = idx.row(); + if(row >= m_datas.length()) { + return false; + } + QVariantMap dm = m_datas.at(row); + QVariantMap local = dm.value("local").toMap(); + QVariantMap remote = dm.value("remote").toMap(); + if(action == DB_MERGE_ACTION_ADD) { + QString name = remote.value("name").toString(); + auto it = std::find_if(m_mi.rhave.begin(), m_mi.rhave.end(), [=](const QVariantMap& dm){ + QVariantMap remote = dm.value("remote").toMap(); + QString nameHit = remote.value("name").toString(); + return name == nameHit; + }); + if(it == m_mi.rhave.end()) { + return false; + } + QVariantMap& dm = *it; + dm.insert("local", dm.value("remote")); + dm.insert("mergeAction", "add"); + dm.insert("isSame", true); + }else if(action == DB_MERGE_ACTION_REMOVE) { + QString name = local.value("name").toString(); + auto it = std::find_if(m_mi.lhave.begin(), m_mi.lhave.end(), [=](const QVariantMap& dm){ + QVariantMap local = dm.value("local").toMap(); + QString nameHit = local.value("name").toString(); + return name == nameHit; + }); + if(it == m_mi.lhave.end()) { + return false; + } + m_mi.remove.append(*it); + m_mi.lhave.erase(it); + }else if(action == DB_MERGE_ACTION_REPLACE) { + QString name = remote.value("name").toString(); + auto it = std::find_if(m_mi.replace.begin(), m_mi.replace.end(), [=](const QVariantMap& dm){ + QVariantMap remote = dm.value("remote").toMap(); + QString nameHit = remote.value("name").toString(); + return name == nameHit; + }); + if(it == m_mi.replace.end()) { + return false; + } + QVariantMap& dm = *it; + dm.insert("local", dm.value("remote")); + dm.insert("mergeAction", "replace"); + dm.insert("isSame", true); + } + reset(m_isFull); + return true; +} + +int QWoDBMergeModel::rowCount(const QModelIndex &parent) const +{ + return m_datas.length(); +} + +int QWoDBMergeModel::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QVariant QWoDBMergeModel::data(const QModelIndex &idx, int role) const +{ + int row = idx.row(); + int col = idx.column(); + if(!idx.isValid() || row >= m_datas.length() || col >= 3) { + return QVariant(); + } + QVariantMap dm = m_datas.at(row); + QString localLabel = dm.value("localLabel").toString(); + QString remoteLabel = dm.value("remoteLabel").toString(); + int colWidth = dm.value("colWidth").toInt(); + int rowHeight = dm.value("rowHeight").toInt(); + bool isSame = dm.value("isSame").toBool(); + colWidth = m_maxWidth + 10; + + if(role == Qt::FontRole){ + return m_font; + }else if(role == Qt::ToolTipRole) { + if(col == 0) { + return localLabel; + }else if(col == 1) { + return remoteLabel; + } + return QVariant(); + }else if(role == Qt::SizeHintRole) { + if(colWidth > 240) { + colWidth = 240; + } + if(rowHeight > 180) { + rowHeight = 180; + } + return QSize(colWidth, rowHeight); + }else if(role == Qt::DisplayRole) { + if(col == 0) { + return localLabel; + }else if(col == 1) { + return remoteLabel; + }else if(col == 2) { + if(localLabel.isEmpty()) { + return "add"; + }else if(remoteLabel.isEmpty()) { + return "remove"; + }else if(!isSame) { + return "replace"; + } + return QVariant(); + } + } + return QVariant(); +} + +QVariant QWoDBMergeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) { + if(role == Qt::DisplayRole) { + if(section == 0) { + return tr("Local"); + }else if(section == 1) { + return tr("Remote"); + }else if(section == 2){ + return tr("Operate"); + } + } + }else{ + if(role == Qt::DisplayRole) { + if(section < m_datas.length()) { + return QString::number(section); + } + } + } + return QVariant(); +} diff --git a/woterm/qwodbmergemodel.h b/woterm/qwodbmergemodel.h new file mode 100644 index 0000000..c09d6de --- /dev/null +++ b/woterm/qwodbmergemodel.h @@ -0,0 +1,46 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBMERGEMODEL_H +#define QWODBMERGEMODEL_H + +#include "qwoglobal.h" + +#include +#include +#include + +class QWoDBMergeModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit QWoDBMergeModel(QObject *parent = nullptr); + void setFont(const QFont& ft); + void setData(const MergeInfo& mi); + QString reset(bool isFull); + void clear(); + void apply(); + bool runAction(int action, const QModelIndex& idx); +protected: + virtual QString toString(const QVariantMap& data) const = 0; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; +protected: + MergeInfo m_mi; + QList m_datas; + QFont m_font; + int m_maxWidth; + bool m_isFull; +}; + +#endif // QWODBMERGEMODEL_H diff --git a/woterm/qwodbpowerrestoredialog.cpp b/woterm/qwodbpowerrestoredialog.cpp new file mode 100644 index 0000000..b8cf8fd --- /dev/null +++ b/woterm/qwodbpowerrestoredialog.cpp @@ -0,0 +1,727 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbpowerrestoredialog.h" +#include "ui_qwodbpowerrestoredialog.h" + +#include "qwodbsftpdetaildialog.h" +#include "qwosetting.h" +#include "qkxmessagebox.h" +#include "qwosshconf.h" +#include "qwodbsftpdownsync.h" +#include "qwodbservermergemodel.h" +#include "qwodbgroupmergemodel.h" +#include "qwodbidentitymergemodel.h" +#include "qwodbsftpdownlistdialog.h" +#include "qkxcipher.h" +#include "qwoutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DB_UNCRYPT_NAME ("woterm_restore_decryption.db") +QDBMergeActionDelegate::QDBMergeActionDelegate(QTableView *tblView, QWidget *parent) + : QStyledItemDelegate(parent) + , m_parent(parent) + , m_tblView(tblView) { + m_btnAdd = new QPushButton(QIcon(":/woterm/resource/skin/add.png"), tr("Add"), parent); + m_btnRemove = new QPushButton(QIcon(":/woterm/resource/skin/close.png"), tr("Remove"), parent); + m_btnReplace = new QPushButton(QIcon(":/woterm/resource/skin/ftp.png"), tr("Replace"), parent); + m_btnAdd->setObjectName("restoreButton"); + m_btnRemove->setObjectName("restoreButton"); + m_btnReplace->setObjectName("restoreButton"); + QSize sz(50,20); + m_btnAdd->resize(sz); + m_btnRemove->resize(sz); + m_btnReplace->resize(sz); + m_btnAdd->hide(); + m_btnRemove->hide(); + m_btnReplace->hide(); +} + +// make sure return true, or else it could not update. +bool QDBMergeActionDelegate::editorEvent(QEvent *ev, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &idx) +{ + _editorEvent(ev, model, option, idx); + return true; +} + +bool QDBMergeActionDelegate::_editorEvent(QEvent *ev, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &idx) { + QMouseEvent *me = static_cast (ev); + m_ptMouse = me->pos(); + QGuiApplication::restoreOverrideCursor(); + m_event = QEvent::None; + QRect itemRt = option.rect; + QString label = idx.data(Qt::DisplayRole).toString(); + QPushButton *btn = nullptr; + QEvent::Type t = me->type(); + if(label == "replace") { + btn = m_btnReplace; + }else if(label == "add") { + btn = m_btnAdd; + }else if(label == "remove") { + btn = m_btnRemove; + }else{ + if(t == QEvent::MouseButtonPress) { + m_tblView->setCurrentIndex(idx); + } + return false; + } + QRect rt = btn->rect(); + rt.adjust(-3, -3, 3, 3); + rt.moveCenter(itemRt.center()); + QApplication::restoreOverrideCursor(); + if(!rt.contains(m_ptMouse)) { + if(t == QEvent::MouseButtonPress) { + m_tblView->setCurrentIndex(idx); + } + return false; + } + + if(t == QEvent::MouseMove + || t == QEvent::MouseButtonPress + || t == QEvent::MouseButtonRelease) { + // QApplication::setOverrideCursor(Qt::PointingHandCursor); + m_event = t; + } + return true; +} + +void QDBMergeActionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const{ + QString label = idx.data(Qt::DisplayRole).toString(); + if(label.isEmpty()) { + QStyledItemDelegate::paint(painter, option, idx); + }else{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + QPalette pal = option.palette; + painter->setBrush(Qt::NoBrush); + if (option.state & QStyle::State_Selected){ + painter->fillRect(option.rect, pal.highlight()); + painter->setPen(Qt::white); + }else{ + painter->setPen(Qt::black); + } + QStyleOptionButton button; + button.state |= QStyle::State_Enabled; + QPushButton *btn = nullptr; + if(label == "replace") { + btn = m_btnReplace; + }else if(label == "add") { + btn = m_btnAdd; + }else if(label == "remove") { + btn = m_btnRemove; + } + if(m_event == QEvent::MouseButtonPress) { + button.state |= QStyle::State_Sunken; + }else if(m_event == QEvent::MouseButtonRelease) { + if(btn == m_btnAdd) { + emit actionArrived(DB_MERGE_ACTION_ADD, idx); + }else if(btn == m_btnRemove) { + emit actionArrived(DB_MERGE_ACTION_REMOVE, idx); + }else if(btn == m_btnReplace) { + emit actionArrived(DB_MERGE_ACTION_REPLACE, idx); + } + + }else if(m_event == QEvent::MouseMove) { + button.state |= QStyle::State_MouseOver; + } + *((QEvent::Type*)&m_event) = QEvent::None; + if(btn) { + QRect itemRt = option.rect; + QRect rt = btn->rect(); + rt.adjust(-3, -3, 3, 3); + rt.moveCenter(itemRt.center()); + button.rect = rt; + button.text = btn->text(); + button.icon = btn->icon(); + button.iconSize = btn->iconSize(); + QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter, btn); + } + painter->restore(); + } +} + + +QWoDBPowerRestoreDialog::QWoDBPowerRestoreDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::QWoDBPowerRestoreDialog) + , m_sftpMode(false) +{ + Qt::WindowFlags flags = windowFlags(); + setWindowFlags(flags &~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); + + m_pathTemp = QWoSetting::tempPath(); + + setWindowTitle(tr("Database restore")); + + ui->btnApply->setEnabled(false); + ui->backupType->setModel(new QStringListModel(QStringList() << tr("sftp server") << tr("local file"), this)); + QObject::connect(ui->backupType, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentBackupIndexChanged())); + onCurrentBackupIndexChanged(); + QStringList crypts; + crypts.append("AES-CBC-256"); + crypts.append("AES-CTR-256"); + crypts.append("AES-GCM-256"); + crypts.append("DES-CBC"); + crypts.append("DES-ECB"); + crypts.append("DES-OFB64"); + crypts.append("RC4"); + crypts.append("Blowfish"); + ui->cryptType->setModel(new QStringListModel(crypts, ui->cryptType)); + ui->cryptType2->setModel(new QStringListModel(crypts, ui->cryptType2)); + ui->sftpServer->setReadOnly(true); + + QStringList items; + items.append(tr("Session list")); + items.append(tr("Group list")); + items.append(tr("Identity file list")); + ui->projectBox->setModel(new QStringListModel(items, ui->projectBox)); + QObject::connect(ui->projectBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentProjectIndexChanged())); + + QObject::connect(ui->btnSftpDetail, SIGNAL(clicked()), this, SLOT(onSftpDetailButtonClicked())); + + QString lastFile = QWoSetting::value("DBBackup/lastLocalFile").toString(); + ui->pathLocal->setText(lastFile); + QString cryptType = QWoSetting::value("DBBackup/lastCryptType").toString(); + if(cryptType.isEmpty()) { + ui->cryptType->setCurrentIndex(0); + ui->cryptType2->setCurrentIndex(0); + }else{ + ui->cryptType->setCurrentText(cryptType); + ui->cryptType2->setCurrentText(cryptType); + } + QString cryptKey = QWoSetting::value("DBBackup/lastCryptKey").toString(); + ui->cryptKey->setText(cryptKey); + ui->cryptKey2->setText(cryptKey); + + resetSftpUrl(); + + QObject::connect(ui->btnNext, SIGNAL(clicked()), this, SLOT(onMergeWidgetShow())); + QObject::connect(ui->btnLast, SIGNAL(clicked()), this, SLOT(onTypeWidgetShow())); + QObject::connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(close())); + QObject::connect(ui->btnExit, SIGNAL(clicked()), this, SLOT(close())); + + ui->localFilePath->setReadOnly(true); + ui->sftpFilePath->setReadOnly(true); + QObject::connect(ui->btnBackupList, SIGNAL(clicked()), this, SLOT(onBackupListClicked())); + QObject::connect(ui->btnDecryptAgain, SIGNAL(clicked()), this, SLOT(onDecryptAgainClicked())); + ui->mergeWidget->hide(); + ui->typeWidget->show(); + adjustSize(); + + QObject::connect(ui->chkDiff, SIGNAL(clicked()), this, SLOT(onGroupModeClicked())); + QObject::connect(ui->chkFull, SIGNAL(clicked()), this, SLOT(onGroupModeClicked())); + QObject::connect(ui->chkReplace, SIGNAL(clicked()), this, SLOT(onGroupModeClicked())); + + + m_modelServer = new QWoDBServerMergeModel(this); + m_modelGroup = new QWoDBGroupMergeModel(this); + m_modelIdentity = new QWoDBIdentityMergeModel(this); + + ui->tblMerge->setTabletTracking(true); + QWidget *viewPort = ui->tblMerge->viewport(); + if(viewPort) { + viewPort->setAttribute(Qt::WA_Hover,true); + viewPort->setTabletTracking(true); + viewPort->setMouseTracking(true); + } + QFont ft = ui->tblMerge->font(); + m_modelServer->setFont(ft); + m_modelGroup->setFont(ui->tblMerge->font()); + m_modelIdentity->setFont(ui->tblMerge->font()); + + ui->tblMerge->setModel(m_modelServer); + ui->tblMerge->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->tblMerge->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->tblMerge->setSelectionMode(QAbstractItemView::SingleSelection); + QDBMergeActionDelegate *delegate = new QDBMergeActionDelegate(ui->tblMerge, ui->tblMerge); + QObject::connect(delegate, SIGNAL(actionArrived(int,QModelIndex)), this, SLOT(onActionArrived(int,QModelIndex))); + ui->tblMerge->setItemDelegateForColumn(2, delegate); + QHeaderView *hdrView = ui->tblMerge->horizontalHeader(); + if(hdrView != nullptr) { + hdrView->setStretchLastSection(true); + //hdrView->setSectionResizeMode(QHeaderView::Stretch); + } + QObject::connect(ui->btnApply, SIGNAL(clicked()), this, SLOT(onButtonApplyClicked())); + setMinimumWidth(600); + ui->tblMerge->setMinimumHeight(350); + QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); +} + +QWoDBPowerRestoreDialog::~QWoDBPowerRestoreDialog() +{ + delete ui; +} + +void QWoDBPowerRestoreDialog::onCurrentBackupIndexChanged() +{ + int idx = ui->backupType->currentIndex(); + if(idx == 0) { + // sftp server. + ui->sftpArea->show(); + ui->localArea->hide(); + }else{ + ui->sftpArea->hide(); + ui->localArea->show(); + } + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); +} + +void QWoDBPowerRestoreDialog::onCurrentProjectIndexChanged() +{ + showMergeResult(); +} + +void QWoDBPowerRestoreDialog::onAdjustLayout() +{ + adjustSize(); +} + +void QWoDBPowerRestoreDialog::onSftpDetailButtonClicked() +{ + QWoDbSftpDetailDialog dlg(this); + if(dlg.exec() != (QDialog::Accepted + 1)) { + resetSftpUrl(); + } +} + +void QWoDBPowerRestoreDialog::onDecryptAgainClicked() +{ + m_cryptKey = ui->cryptKey2->text(); + m_cryptType = ui->cryptType2->currentText(); + QString fileName = ui->sftpFilePath->text(); + if(decryptFile(fileName, DB_UNCRYPT_NAME)) { + QString uncryptFile = m_pathTemp + "/" + DB_UNCRYPT_NAME; + runMerge(uncryptFile); + } +} + +void QWoDBPowerRestoreDialog::onButtonApplyClicked() +{ + if(ui->chkReplace->isChecked()) { + if(m_sftpMode) { + QString dbFile = m_pathTemp + "/" + DB_UNCRYPT_NAME; + QWoSshConf::instance()->restore(dbFile); + }else{ + QString dbFile = ui->pathLocal->text(); + QWoSshConf::instance()->restore(dbFile); + } + }else{ + m_modelGroup->apply(); + m_modelIdentity->apply(); + m_modelServer->apply(); + } + done(QDialog::Accepted); +} + +void QWoDBPowerRestoreDialog::onMergeWidgetShow() +{ + m_sftpMode = ui->backupType->currentIndex() == 0; + if(m_sftpMode) { + QString server = ui->sftpServer->text(); + if(server.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the server address should not be empty.")); + return; + } + QString key = ui->cryptKey->text(); + if(key.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the crypt key should not be empty.")); + return; + } + m_cryptType = ui->cryptType->currentText(); + m_cryptKey = ui->cryptKey->text(); + ui->cryptType2->setCurrentText(m_cryptType); + ui->cryptKey2->setText(m_cryptKey); + ui->decryptArea->hide(); + ui->localFileArea->hide(); + ui->sftpFileArea->show(); + if(m_sync == nullptr){ + m_sync = new QWoDBSftpDownSync(this); + QObject::connect(m_sync, SIGNAL(infoArrived(int,int,QString)), this, SLOT(onSyncInfoArrived(int,int,QString))); + QObject::connect(m_sync, SIGNAL(fetchFinished(int,QString)), this, SLOT(onSyncFetchFinished(int,QString))); + } + m_sync->fetchLatest(); + }else{ + ui->decryptArea->hide(); + ui->localFileArea->show(); + ui->sftpFileArea->hide(); + ui->localFilePath->setText(ui->pathLocal->text()); + QMetaObject::invokeMethod(this, "fetchLocal", Qt::QueuedConnection); + } + ui->mergeWidget->show(); + ui->typeWidget->hide(); + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); +} + +void QWoDBPowerRestoreDialog::onTypeWidgetShow() +{ + ui->mergeWidget->hide(); + ui->typeWidget->show(); + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); +} + +void QWoDBPowerRestoreDialog::onBackupListClicked() +{ + if(m_sync) { + QString fileName = QWoDBSftpDownListDialog::result(m_sync, this); + if(fileName.isEmpty()) { + return; + } + ui->sftpFilePath->setText(fileName); + m_sync->fetch(fileName); + } +} + +void QWoDBPowerRestoreDialog::onGroupModeClicked() +{ + showMergeResult(); +} + +void QWoDBPowerRestoreDialog::onActionArrived(int action, const QModelIndex &idx) +{ + m_modelCurrent->runAction(action, idx); +} + +#define DB_SFTP_DOWNLOAD_CHECK_VERSION (1) +#define DB_SFTP_DOWNLOAD_DOWNLOAD_FILE (2) + +void QWoDBPowerRestoreDialog::onSyncInfoArrived(int action, int err, const QString &errDesc) +{ + ui->infoMsg->setText(errDesc); +} + +void QWoDBPowerRestoreDialog::onSyncFetchFinished(int err, const QString &fileName) +{ + m_modelServer->clear(); + m_modelGroup->clear(); + m_modelIdentity->clear(); + if(err == 0) { + ui->sftpFilePath->setText(fileName); + if(decryptFile(fileName, DB_UNCRYPT_NAME)) { + QString uncryptFile = m_pathTemp + "/" + DB_UNCRYPT_NAME; + runMerge(uncryptFile); + } + }else{ + ui->sftpFilePath->setText(""); + } +} + +void QWoDBPowerRestoreDialog::fetchLocal() +{ + QString dbFile = ui->pathLocal->text(); + if(!QWoSshConf::databaseValid(dbFile)) { + QKxMessageBox::information(this, tr("Error"), tr("Failed to open file:%1").arg(dbFile)); + return; + } + runMerge(dbFile); +} + +void QWoDBPowerRestoreDialog::resetSftpUrl() +{ + QVariantMap dm = QWoSetting::value("DBBackup/sftpDetail").toMap(); + QString host = dm.value("host").toString(); + QString name = dm.value("name").toString(); + QString path = dm.value("path", "~/woterm_db_backup").toString(); + QString port = dm.value("port", 22).toString(); + QString url = QString("sftp://%1@%2:%3?port=%4").arg(name, host, path, port); + ui->sftpServer->setText(url); +} + +bool QWoDBPowerRestoreDialog::decryptFile(const QString &fileName, const QString& fileNameDst) +{ + QString path = m_pathTemp + "/" + fileName; + QFile lf(path); + if(!lf.open(QIODevice::ReadOnly)) { + QKxMessageBox::information(this, tr("Decryption error"), tr("Failed to open file:%1").arg(fileName)); + return false; + } + QByteArray all = lf.readAll(); + lf.close(); + QByteArray out; + if(!decrypt(all, m_cryptType.toUtf8(), m_cryptKey.toUtf8(), out)) { + QKxMessageBox::information(this, tr("Decryption error"), tr("Failed to decrypt the backup file:%1.").arg(fileName)); + if(!ui->decryptArea->isVisible()){ + ui->decryptArea->show(); + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); + } + return false; + } + qDebug() << "ready to do more"; + QString fileDecrypt = m_pathTemp + "/" + fileNameDst; + QFile::remove(fileDecrypt); + QFile df(fileDecrypt); + if(!df.open(QIODevice::WriteOnly)) { + QKxMessageBox::information(this, tr("Decryption error"), tr("Failed to write decrypt result to file:%1").arg(fileDecrypt)); + return false; + } + df.write(out); + df.close(); + + if(ui->decryptArea->isVisible()){ + ui->decryptArea->hide(); + QTimer::singleShot(0, this, SLOT(onAdjustLayout())); + } + return true; +} + +bool QWoDBPowerRestoreDialog::decrypt(const QByteArray& in, const QByteArray &type, const QByteArray &key, QByteArray &out) +{ + if(type == "AES-CBC-256") { + QByteArray pass = QKxCipher::makeBytes(key, 32); + QByteArray ivec = QKxCipher::makeBytes(key, 16); + if(!QKxCipher::aesCbcEncrypt(in, out, pass, ivec, false)) { + return false; + } + }else if(type == "AES-CTR-256") { + QByteArray pass = QKxCipher::makeBytes(key, 32); + QByteArray ivec = QKxCipher::makeBytes(key, 16); + if(!QKxCipher::aesCtrEncrypt(in, out, pass, ivec, false)) { + return false; + } + }else if(type == "AES-GCM-256") { + QByteArray pass = QKxCipher::makeBytes(key, 32); + QByteArray ivec = QKxCipher::makeBytes(key, 16); + if(!QKxCipher::aesGcmEncrypt(in, out, pass, ivec, false)) { + return false; + } + }else if(type == "DES-CBC") { + QByteArray pass = QKxCipher::makeBytes(key, 24); + QByteArray ivec = QKxCipher::makeBytes(key, 8); + if(!QKxCipher::tripleDesCbcEncrypt(in, out, pass, ivec, false)) { + return false; + } + }else if(type == "DES-ECB") { + QByteArray pass = QKxCipher::makeBytes(key, 24); + if(!QKxCipher::tripleDesEcbEncrypt(in, out, pass, false)) { + return false; + } + }else if(type == "DES-OFB64") { + QByteArray pass = QKxCipher::makeBytes(key, 24); + QByteArray ivec = QKxCipher::makeBytes(key, 8); + if(!QKxCipher::tripleDesOfb64Encrypt(in, out, pass, ivec, false)) { + return false; + } + }else if(type == "RC4") { + QByteArray pass = key; + if(!QKxCipher::rc4Encrypt(in, out, pass, false)) { + return false; + } + }else if(type == "Blowfish") { + QByteArray pass = key; + QByteArray ivec = QKxCipher::makeBytes(key, 8); + if(!QKxCipher::blowfishEcbEncrypt(in, out, pass, ivec, false)) { + return false; + } + } + // strncmp(header, "SQLite format 3\000", 16) + QByteArray header = out.left(16); + if(!header.startsWith("SQLite format 3")) { + return false; + } + return true; +} + + +bool QWoDBPowerRestoreDialog::runMerge(const QString &dbFile) +{ + m_modelGroup->clear(); + m_modelIdentity->clear(); + m_modelServer->clear(); + ui->btnApply->setEnabled(false); + + m_local.clear(); + m_remote.clear(); + QString file = QWoSetting::sshServerDbPath(); + if(!dbToMap(file, m_local)) { + return false; + } + if(!dbToMap(dbFile, m_remote)) { + return false; + } + QList names = m_remote.keys(); + for(auto it = names.begin(); it != names.end(); it++) { + QString name = *it; + QList local = m_local.value(name); + QList remote = m_remote.value(name); + if(name == "servers") { + MergeInfo mi = runMergeList(local, remote); + m_modelServer->setData(mi); + }else if(name == "groups") { + MergeInfo mi = runMergeList(local, remote); + m_modelGroup->setData(mi); + }else if(name == "identities") { + MergeInfo mi = runMergeList(local, remote); + m_modelIdentity->setData(mi); + } + } + showMergeResult(); + ui->btnApply->setEnabled(true); + return true; +} + +MergeInfo QWoDBPowerRestoreDialog::runMergeList(const QList &_local, const QList &_remote) const +{ + QList local = _local; + QList remote = _remote; + QList same, replace, lhave, rhave; + for(auto lt = local.begin(); lt != local.end(); lt++) { + QVariantMap lm = *lt; + QVariantMap item; + item.insert("local", lm); + QString name = lm.value("name").toString(); + bool hasFind = false; + bool isSame = true; + for(auto rt = remote.begin(); rt != remote.end(); rt++) { + QVariantMap rm = *rt; + QString nameHit = rm.value("name").toString(); + if(nameHit == name) { + if(name == "kxtry.portmap8") { + qDebug() << lm << rm; + } + item.insert("remote", rm); + isSame = lm == rm; + item.insert("isSame", isSame); + remote.erase(rt); + hasFind = true; + break; + } + } + if(hasFind) { + if(isSame) { + same.append(item); + }else{ + replace.append(item); + } + }else{ + lhave.append(item); + } + } + for(auto rt = remote.begin(); rt != remote.end(); rt++) { + QVariantMap rm = *rt; + QVariantMap item; + item.insert("remote", rm); + rhave.append(item); + } + MergeInfo mi; + mi.same = same; + mi.lhave = lhave; + mi.rhave = rhave; + mi.replace = replace; + return mi; +} + +void QWoDBPowerRestoreDialog::showMergeResult() +{ + int idx = ui->projectBox->currentIndex(); + bool isReplace = ui->chkReplace->isChecked(); + ui->mergeArea->setVisible(!isReplace); + if(!isReplace) { + bool isFull = ui->chkFull->isChecked(); + QString msg; + if(idx == 0) { + // Session list + msg = m_modelServer->reset(isFull); + m_modelCurrent = m_modelServer; + ui->tblMerge->setModel(m_modelServer); + }else if(idx == 1) { + // Group list + msg = m_modelGroup->reset(isFull); + m_modelCurrent = m_modelGroup; + ui->tblMerge->setModel(m_modelGroup); + }else if(idx == 2) { + // Identity file list + msg = m_modelIdentity->reset(isFull); + m_modelCurrent = m_modelIdentity; + ui->tblMerge->setModel(m_modelIdentity); + } + QMetaObject::invokeMethod(ui->tblMerge, "resizeRowsToContents", Qt::QueuedConnection); + QMetaObject::invokeMethod(ui->tblMerge, "resizeColumnsToContents", Qt::QueuedConnection); + + QKxMessageBox::information(this, tr("Merge information"), msg); + } + QMetaObject::invokeMethod(this, "onAdjustLayout", Qt::QueuedConnection); +} + +// Only relevant information affecting the connection network is synchronized, +// and configuration information is not synchronized. +#define PASSWORD_ENCRYPT ("WoTerm@2022-11-6") +QList fetchList(SQLite::Database& db, const QString& tblName) { + QList all; + QString sql = QString("select * from %1").arg(tblName); + SQLite::Statement query(db, sql.toUtf8()); + while(query.executeStep()) { + QVariantMap dm; + for(int i = 0; i < query.getColumnCount(); i++) { + QString name = query.getColumnName(i); + if(name == "id" + ||name == "ct" + ||name == "dt" + || name == "syncFlag" + || name == "delFlag" + || name == "version" + || name == "property") { + continue; + } + SQLite::Column col = query.getColumn(i); + if(col.isInteger()) { + dm.insert(name, col.getInt()); + }else if(col.isText()) { + QByteArray txt = col.getText(); + if(txt.isEmpty()) { + continue; + } + if(name == "loginPassword") { + if(txt == "WoTerm:") { + continue; + } + txt = QWoUtils::aesEncrypt(txt, PASSWORD_ENCRYPT); + } + dm.insert(name, txt); + } + } + all.append(dm); + } + return all; +} + +bool QWoDBPowerRestoreDialog::dbToMap(const QString &dbFile, QMap > &all) +{ + try{ + SQLite::Database db(dbFile.toUtf8(), SQLite::OPEN_READONLY); + QList groups = fetchList(db, "groups"); + QList servers = fetchList(db, "servers"); + QList identities = fetchList(db, "identities"); + all.insert("groups", groups); + all.insert("servers", servers); + all.insert("identities", identities); + }catch(std::exception& e) { + QByteArray err = e.what(); + qDebug() << "dbToMap" << err; + return false; + } + return true; +} + +void QWoDBPowerRestoreDialog::init() +{ + +} diff --git a/woterm/qwodbpowerrestoredialog.h b/woterm/qwodbpowerrestoredialog.h new file mode 100644 index 0000000..03103f0 --- /dev/null +++ b/woterm/qwodbpowerrestoredialog.h @@ -0,0 +1,106 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBPOWERRESTOREDIALOG_H +#define QWODBPOWERRESTOREDIALOG_H + +#include "qwoglobal.h" + +#include +#include +#include +#include +#include +#include + +namespace Ui { +class QWoDBPowerRestoreDialog; +} + +class QTableView; + +class QDBMergeActionDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit QDBMergeActionDelegate(QTableView *tblView, QWidget *parent = 0); + bool editorEvent(QEvent *ev, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &idx); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const; + +signals: + void actionArrived(int action, const QModelIndex& idx) const; +private: + bool _editorEvent(QEvent *ev, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &idx); + +private: + QPointer m_parent; + QPointer m_tblView; + QPushButton *m_btnReplace; + QPushButton *m_btnAdd; + QPushButton *m_btnRemove; + QPoint m_ptMouse; + QEvent::Type m_event; +}; + +class QWoDBSftpDownSync; +class QWoDBMergeModel; +class QWoDBPowerRestoreDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QWoDBPowerRestoreDialog(QWidget *parent = nullptr); + ~QWoDBPowerRestoreDialog(); + + +private slots: + void onCurrentBackupIndexChanged(); + void onCurrentProjectIndexChanged(); + void onAdjustLayout(); + void onSftpDetailButtonClicked(); + void onDecryptAgainClicked(); + void onButtonApplyClicked(); + + void onMergeWidgetShow(); + void onTypeWidgetShow(); + void onBackupListClicked(); + void onGroupModeClicked(); + + + void onActionArrived(int action, const QModelIndex& idx); + + void onSyncInfoArrived(int action, int err, const QString& errDesc); + void onSyncFetchFinished(int err, const QString& fileName); +private: + Q_INVOKABLE void fetchLocal(); + void resetSftpUrl(); + bool decryptFile(const QString &fileNameSrc, const QString& fileNameDst); + bool decrypt(const QByteArray& in, const QByteArray& type, const QByteArray& key, QByteArray& out); + bool runMerge(const QString& fileName); + MergeInfo runMergeList(const QList& local, const QList& remote) const; + void showMergeResult(); + bool dbToMap(const QString& filePath, QMap>& all); + Q_INVOKABLE void init(); +private: + Ui::QWoDBPowerRestoreDialog *ui; + QPointer m_sync; + QPointer m_modelServer, m_modelGroup, m_modelIdentity, m_modelCurrent; + bool m_sftpMode; + QString m_pathTemp; + QString m_cryptType, m_cryptKey; + + QMap> m_local, m_remote; +}; + +#endif // QWODBPOWERRESTOREDIALOG_H diff --git a/woterm/qwodbpowerrestoredialog.ui b/woterm/qwodbpowerrestoredialog.ui new file mode 100644 index 0000000..01fc88e --- /dev/null +++ b/woterm/qwodbpowerrestoredialog.ui @@ -0,0 +1,666 @@ + + + QWoDBPowerRestoreDialog + + + + 0 + 0 + 485 + 655 + + + + + 450 + 0 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Local file: + + + + + + + true + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Remote file: + + + + + + + + + + ... + + + + :/woterm/resource/skin/download2.png:/woterm/resource/skin/download2.png + + + + + + + + + + + + color: rgb(255, 85, 127); + + + + + + + + + + + 0 + 0 + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Decrypt type: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Decrypt key: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 80 + 20 + + + + + + + + Decrypt again + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + Merge mode + + + + 6 + + + 3 + + + 6 + + + 3 + + + + + Show diffent record + + + true + + + + + + + Show full record + + + + + + + Replace + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Project name: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + 360 + 0 + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Last step + + + + + + + Apply + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + 0 + + + + + Type: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + File name: + + + + + + + + + + ... + + + + :/woterm/resource/skin/dirs.png:/woterm/resource/skin/dirs.png + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Server: + + + + + + + + + + ... + + + + :/woterm/resource/skin/sftp.png:/woterm/resource/skin/sftp.png + + + + + + + + + + + Decrypt type: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Decrypt key: + + + + + + + + + + + + + + + + 360 + 0 + + + + Qt::Horizontal + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Next step + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + diff --git a/woterm/qwodbrestoredialog.cpp b/woterm/qwodbrestoredialog.cpp index f429d31..3e4ec38 100644 --- a/woterm/qwodbrestoredialog.cpp +++ b/woterm/qwodbrestoredialog.cpp @@ -16,11 +16,11 @@ #include "qwosshconf.h" #include "qwohostlistmodel.h" #include "qwohosttreemodel.h" +#include "qkxmessagebox.h" #include #include #include -#include QWoDBRestoreDialog::QWoDBRestoreDialog(QWidget *parent) : @@ -57,18 +57,16 @@ void QWoDBRestoreDialog::onRestoreButtonClicked() { QString path = ui->filePath->text(); if(path.isEmpty()) { - QMessageBox::information(this, tr("Restore information"), tr("please select a backup file to restore")); + QKxMessageBox::information(this, tr("Restore information"), tr("please select a backup file to restore")); return; } if(!QWoSshConf::databaseValid(path)) { - QMessageBox::information(this, tr("Restore information"), tr("The backup file is incorrect or corrupt")); + QKxMessageBox::information(this, tr("Restore information"), tr("The backup file is incorrect or corrupt")); return; } if(QWoSshConf::instance()->restore(path)) { - QWoHostListModel::instance()->refreshList(); - QWoHostTreeModel::instance()->refreshList(); - QMessageBox::information(this, tr("Restore information"), tr("success to restore database.")); + QKxMessageBox::information(this, tr("Restore information"), tr("success to restore database.")); }else{ - QMessageBox::information(this, tr("Restore information"), tr("failed to restore database.")); + QKxMessageBox::information(this, tr("Restore information"), tr("failed to restore database.")); } } diff --git a/woterm/qwodbservermergemodel.cpp b/woterm/qwodbservermergemodel.cpp new file mode 100644 index 0000000..c324515 --- /dev/null +++ b/woterm/qwodbservermergemodel.cpp @@ -0,0 +1,50 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbservermergemodel.h" + +#include + +QWoDBServerMergeModel::QWoDBServerMergeModel(QObject *parent) + : QWoDBMergeModel(parent) +{ + +} + +QString QWoDBServerMergeModel::toString(const QVariantMap &dm) const +{ + QStringList names = {tr("groupName"),tr("host"),tr("name"),tr("port"),tr("baudRate"),tr("dataBits"),tr("memo"), + tr("parity"),tr("stopBits"),tr("flowControl"),tr("proxyJump"),tr("loginName"),tr("identityFile"),tr("type")}; + QStringList lines; + for(auto it = dm.begin(); it != dm.end(); it++) { + QString key = it.key(); + QString txt = it.value().toString(); + if(key == "ct" + || key == "delFlag" + || key == "dt" + || key == "id" + || key == "version" + || key == "syncFlag" + || key == "property" + || key == "loginPassword" + || key == "scriptFile" + || key == "script") { + continue; + } + if(txt.isEmpty()) { + continue; + } + QString line = tr(key.toUtf8()) + ":" + txt; + lines.append(line); + } + QString out = lines.join("\r\n"); + return out; +} diff --git a/woterm/qwodbservermergemodel.h b/woterm/qwodbservermergemodel.h new file mode 100644 index 0000000..bf8b274 --- /dev/null +++ b/woterm/qwodbservermergemodel.h @@ -0,0 +1,26 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBSERVERMERGEMODEL_H +#define QWODBSERVERMERGEMODEL_H + +#include "qwodbmergemodel.h" + +class QWoDBServerMergeModel : public QWoDBMergeModel +{ + Q_OBJECT +public: + explicit QWoDBServerMergeModel(QObject *parent = nullptr); +private: + virtual QString toString(const QVariantMap& data) const; +}; + +#endif // QWODBSERVERMERGEMODEL_H diff --git a/woterm/qwodbsftpdetaildialog.cpp b/woterm/qwodbsftpdetaildialog.cpp new file mode 100644 index 0000000..811dcdc --- /dev/null +++ b/woterm/qwodbsftpdetaildialog.cpp @@ -0,0 +1,307 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbsftpdetaildialog.h" +#include "ui_qwodbsftpdetaildialog.h" +#include "qwoidentifydialog.h" +#include "qwosetting.h" +#include "qwossh.h" +#include "qwoglobal.h" +#include "qwoidentify.h" +#include "qkxmessagebox.h" +#include "qkxbuttonassist.h" +#include "qwoutils.h" + +#include +#include +#include +#include + +QWoDbSftpDetailDialog::QWoDbSftpDetailDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QWoDbSftpDetailDialog) +{ + Qt::WindowFlags flags = windowFlags(); + setWindowFlags(flags &~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); + setWindowTitle(tr("Sftp server")); + ui->identity->setReadOnly(true); + ui->password->setEchoMode(QLineEdit::Password); + QKxButtonAssist *assist = new QKxButtonAssist(":/woterm/resource/skin/eye.png", ui->password); + QObject::connect(assist, SIGNAL(clicked(int)), this, SLOT(onAssistButtonClicked(int))); + ui->type->setModel(new QStringListModel(QStringList() << tr("Password") << tr("Identity file"), ui->type)); + QObject::connect(ui->type, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentTextChanged())); + QObject::connect(ui->btnIdentify, SIGNAL(clicked()), this, SLOT(onIdentifyButtonClicked())); + QObject::connect(ui->btnApply, SIGNAL(clicked()), this, SLOT(onApplyButtonClicked())); + QObject::connect(ui->btnTest, SIGNAL(clicked()), this, SLOT(onTestButtonClicked())); + QObject::connect(ui->btnClose, SIGNAL(clicked()), this, SLOT(close())); + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + QObject::connect(m_timer, SIGNAL(timeout()), this, SLOT(onTestTimeout())); + + QVariantMap dm = QWoSetting::value("DBBackup/sftpDetail").toMap(); + QString host = dm.value("host").toString(); + QString name = dm.value("name").toString(); + QString password = dm.value("password").toString(); + QString identity = dm.value("identity").toString(); + QString path = dm.value("path", "~/woterm_db_backup").toString(); + QString port = dm.value("port", 22).toString(); + if(!identity.isEmpty()) { + ui->type->setCurrentIndex(1); + ui->identity->setText(identity); + }else{ + ui->type->setCurrentIndex(0); + ui->password->setText(password); + } + ui->host->setText(host); + ui->name->setText(name); + ui->path->setText(path); + ui->port->setText(port); + ui->port->setValidator(new QIntValidator(1, 65535)); + onCurrentTextChanged(); +} + +QWoDbSftpDetailDialog::~QWoDbSftpDetailDialog() +{ + release(); + delete ui; +} + +void QWoDbSftpDetailDialog::onCurrentTextChanged() +{ + int idx = ui->type->currentIndex(); + if(idx == 0) { + ui->passArea->show(); + ui->identityArea->hide(); + }else{ + ui->passArea->hide(); + ui->identityArea->show(); + } + adjustSize(); +} + +void QWoDbSftpDetailDialog::onIdentifyButtonClicked() +{ + QString name = QWoIdentifyDialog::open(false, this); + if(name.isEmpty()) { + return; + } + ui->identity->setText(name); +} + +void QWoDbSftpDetailDialog::onTestButtonClicked() +{ + HostInfo hi; + hi.host = ui->host->text(); + hi.user = ui->name->text(); + hi.port = ui->port->text().toInt(); + if(hi.host.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the host parameter should not be empty")); + return; + } + + if(hi.user.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the login name parameter should not be empty")); + return; + } + + if(ui->type->currentIndex() == 0) { + hi.password = ui->password->text(); + if(hi.password.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the password parameter should not be empty")); + return; + } + }else{ + hi.identityFile = ui->identity->text(); + if(hi.identityFile.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the identity file parameter should not be empty")); + return; + } + IdentifyInfo info; + if(!QWoIdentify::infomation(hi.identityFile.toUtf8(), &info)) { + return; + } + hi.identityContent = info.prvKey; + } + + QString path = ui->path->text(); + if(path.isEmpty()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("the save path parameter should not be empty")); + return; + } + + release(); + + m_sftp = QWoSshFactory::instance()->createSftp(); + QObject::connect(m_sftp, SIGNAL(connectionFinished(bool)), this, SLOT(onConnectionFinished(bool))); + QObject::connect(m_sftp, SIGNAL(connectionStart()), this, SLOT(onConnectionStart())); + QObject::connect(m_sftp, SIGNAL(errorArrived(QString,QVariantMap)), this, SLOT(onErrorArrived(QString,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(finishArrived(int)), this, SLOT(onFinishArrived(int))); + QObject::connect(m_sftp, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); + QObject::connect(m_sftp, SIGNAL(commandStart(int,QVariantMap)), this, SLOT(onCommandStart(int,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(commandFinish(int,QVariantMap)), this, SLOT(onCommandFinish(int,QVariantMap))); + + m_sftp->start(hi, 11); + m_sftp->fileInfo(path); + ui->btnTest->setEnabled(false); + m_timer->start(1000 * 10); + + saveDetail(); +} + +void QWoDbSftpDetailDialog::onApplyButtonClicked() +{ + saveDetail(); + done(Accepted + 1); +} + +void QWoDbSftpDetailDialog::onConnectionStart() +{ + +} + +void QWoDbSftpDetailDialog::onConnectionFinished(bool ok) +{ + if(!ok) { + release(); + QKxMessageBox::information(this, tr("Failure report"), tr("Failed")); + } +} + +void QWoDbSftpDetailDialog::onCommandStart(int type, const QVariantMap &userData) +{ + +} + +void QWoDbSftpDetailDialog::onCommandFinish(int type, const QVariantMap &userData) +{ + QString reason = userData.value("reason").toString(); + if(type == 22) { + // #define MT_FTP_MKPATH (22) + release(); + if(reason == "ok") { + QKxMessageBox::information(this, tr("Success"), tr("Success to create it.")); + }else{ + QKxMessageBox::information(this, tr("Failure"), tr("Failed to create it, please do it by manual.")); + } + }else if(type == 21) { + // #define MT_FTP_FILE_INFO (21) + QVariantMap dm = userData.value("fileInfo").toMap(); + if(dm.isEmpty()) { + if(reason != "fatal") { + if(QKxMessageBox::information(this, + tr("Failure"), + tr("Failed to open the target path, try to create it?"), + QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) { + QString path = ui->path->text(); + m_sftp->mkPath(path, 0x1C0); + }else{ + release(); + } + }else{ + release(); + } + }else{ + release(); + QString type = dm.value("type").toString(); + if(type != "d") { + QKxMessageBox::information(this, tr("Failure"), tr("Please check it again and make sure the target path is a directory not a file.")); + }else{ + QKxMessageBox::information(this, tr("Success"), tr("It's a valid path to backup file.")); + } + } + }else{ + release(); + } +} + +void QWoDbSftpDetailDialog::onErrorArrived(const QString &err, const QVariantMap &userData) +{ + QKxMessageBox::information(this, tr("Error"), err); + release(); +} + +void QWoDbSftpDetailDialog::onFinishArrived(int code) +{ + release(); +} + +void QWoDbSftpDetailDialog::onInputArrived(const QString &title, const QString &prompt, bool visible) +{ + release(); + QKxMessageBox::information(this, tr("Parmeter error"), tr("The account information error")); +} + +void QWoDbSftpDetailDialog::onTestTimeout() +{ + release(); + QKxMessageBox::information(this, tr("Error"), tr("Test timeout")); +} + +void QWoDbSftpDetailDialog::onAssistButtonClicked(int i) +{ + QLineEdit::EchoMode mode = ui->password->echoMode(); + if(mode == QLineEdit::Normal) { + ui->password->setEchoMode(QLineEdit::Password); + }else{ + QString pwdAdmin = QWoSetting::adminPassword(); + if(pwdAdmin.isEmpty()) { + QKxMessageBox::information(this, tr("Administrator"), tr("Please create administrator's password first!")); + return; + } + QString pass = QWoUtils::getPassword(this, tr("Please input the administrator's password")); + if(pass.isEmpty()) { + return; + } + if(pass != pwdAdmin) { + QKxMessageBox::information(this, tr("Error"), tr("Password error!")); + return; + } + ui->password->setEchoMode(QLineEdit::Normal); + QTimer::singleShot(1000*5, this, SLOT(onSetEditToPasswordMode())); + } +} + +void QWoDbSftpDetailDialog::onSetEditToPasswordMode() +{ + ui->password->setEchoMode(QLineEdit::Password); +} + +void QWoDbSftpDetailDialog::release() +{ + ui->btnTest->setEnabled(true); + m_timer->stop(); + if(m_sftp) { + m_sftp->stop(); + QWoSshFactory::instance()->release(m_sftp); + } + m_sftp = nullptr; +} + +void QWoDbSftpDetailDialog::saveDetail() +{ + HostInfo hi; + hi.host = ui->host->text(); + hi.user = ui->name->text(); + hi.port = ui->port->text().toInt(); + + QVariantMap dm; + dm.insert("host", ui->host->text()); + dm.insert("port", ui->port->text()); + dm.insert("name", ui->name->text()); + if(ui->type->currentIndex() == 0) { + dm.insert("password", ui->password->text()); + }else{ + dm.insert("identity", ui->identity->text()); + } + dm.insert("path", ui->path->text()); + QWoSetting::setValue("DBBackup/sftpDetail", dm); +} diff --git a/woterm/qwodbsftpdetaildialog.h b/woterm/qwodbsftpdetaildialog.h new file mode 100644 index 0000000..2042e2f --- /dev/null +++ b/woterm/qwodbsftpdetaildialog.h @@ -0,0 +1,57 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBSFTPDETAILDIALOG_H +#define QWODBSFTPDETAILDIALOG_H + +#include +#include + +namespace Ui { +class QWoDbSftpDetailDialog; +} + +class QWoSshFtp; +class QTimer; + +class QWoDbSftpDetailDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QWoDbSftpDetailDialog(QWidget *parent = nullptr); + ~QWoDbSftpDetailDialog(); + +private slots: + void onCurrentTextChanged(); + void onIdentifyButtonClicked(); + void onTestButtonClicked(); + void onApplyButtonClicked(); + void onConnectionStart(); + void onConnectionFinished(bool ok); + void onCommandStart(int type, const QVariantMap& userData); + void onCommandFinish(int type, const QVariantMap& userData); + void onErrorArrived(const QString& err, const QVariantMap& userData); + void onFinishArrived(int code); + void onInputArrived(const QString &title, const QString &prompt, bool visible); + void onTestTimeout(); + void onAssistButtonClicked(int i); + void onSetEditToPasswordMode(); +private: + void release(); + void saveDetail(); +private: + Ui::QWoDbSftpDetailDialog *ui; + QPointer m_sftp; + QPointer m_timer; +}; + +#endif // QWODBSFTPDETAILDIALOG_H diff --git a/woterm/qwodbsftpdetaildialog.ui b/woterm/qwodbsftpdetaildialog.ui new file mode 100644 index 0000000..768f785 --- /dev/null +++ b/woterm/qwodbsftpdetaildialog.ui @@ -0,0 +1,253 @@ + + + QWoDbSftpDetailDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + + + Host: + + + + + + + + + + + + + + Port: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Name: + + + + + + + + + + + + + + Type: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Password: + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Identity file: + + + + + + + + + + ... + + + + :/woterm/resource/skin/proxy.png:/woterm/resource/skin/proxy.png + + + + + + + + + + + + Path: + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Test connection + + + + + + + Apply + + + true + + + + + + + Close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/woterm/qwodbsftpdownlistdialog.cpp b/woterm/qwodbsftpdownlistdialog.cpp new file mode 100644 index 0000000..483374f --- /dev/null +++ b/woterm/qwodbsftpdownlistdialog.cpp @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbsftpdownlistdialog.h" +#include "ui_qwodbsftpdownlistdialog.h" + +#include "qwodbsftpdownsync.h" +#include "qkxmessagebox.h" + +#include + +QString QWoDBSftpDownListDialog::result(QWoDBSftpDownSync *sync, QWidget *parent) +{ + QWoDBSftpDownListDialog dlg(sync, parent); + if(dlg.exec() == QDialog::Accepted) { + return dlg.m_fileName; + } + return ""; +} + +QWoDBSftpDownListDialog::QWoDBSftpDownListDialog(QWoDBSftpDownSync *sync, QWidget *parent) + : QDialog(parent) + , ui(new Ui::QWoDBSftpDownListDialog) + , m_sync(sync) +{ + Qt::WindowFlags flags = windowFlags(); + setWindowFlags(flags &~Qt::WindowContextHelpButtonHint); + ui->setupUi(this); + + setWindowTitle(tr("Backup file list")); + + QObject::connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onListItemDoubleClicked(QModelIndex))); + QObject::connect(ui->btnReflesh, SIGNAL(clicked()), this, SLOT(onButtonRefleshClicked())); + QObject::connect(ui->btnSelect, SIGNAL(clicked()), this, SLOT(onButtonSelectClicked())); + QObject::connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(close())); + QObject::connect(m_sync, SIGNAL(listArrived(QStringList)), this, SLOT(onSyncListArrived(QStringList))); + m_sync->listAll(); + adjustSize(); +} + +QWoDBSftpDownListDialog::~QWoDBSftpDownListDialog() +{ + delete ui; +} + +void QWoDBSftpDownListDialog::onButtonRefleshClicked() +{ + m_sync->listAll(); +} + +void QWoDBSftpDownListDialog::onButtonSelectClicked() +{ + QModelIndex idx = ui->listView->currentIndex(); + if(!idx.isValid()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("Please select a backup file.")); + return; + } + m_fileName = idx.data().toString(); + done(QDialog::Accepted); +} + +void QWoDBSftpDownListDialog::onSyncListArrived(const QStringList &fileNames) +{ + QAbstractItemModel *model = ui->listView->model(); + if(model) { + model->deleteLater(); + } + model = new QStringListModel(fileNames, this); + ui->listView->setModel(model); +} + +void QWoDBSftpDownListDialog::onListItemDoubleClicked(const QModelIndex &idx) +{ + QString name = idx.data().toString(); + if(name.isEmpty()) { + return; + } + m_fileName = name; + done(QDialog::Accepted); +} diff --git a/woterm/qwodbsftpdownlistdialog.h b/woterm/qwodbsftpdownlistdialog.h new file mode 100644 index 0000000..f90d761 --- /dev/null +++ b/woterm/qwodbsftpdownlistdialog.h @@ -0,0 +1,43 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBSFTPDOWNLISTDIALOG_H +#define QWODBSFTPDOWNLISTDIALOG_H + +#include +#include + +namespace Ui { +class QWoDBSftpDownListDialog; +} + +class QWoDBSftpDownSync; +class QWoDBSftpDownListDialog : public QDialog +{ + Q_OBJECT + +public: + static QString result(QWoDBSftpDownSync *sync, QWidget *parent = nullptr); +private: + explicit QWoDBSftpDownListDialog(QWoDBSftpDownSync *sync, QWidget *parent = nullptr); + ~QWoDBSftpDownListDialog(); +private slots: + void onButtonRefleshClicked(); + void onButtonSelectClicked(); + void onSyncListArrived(const QStringList& fileNames); + void onListItemDoubleClicked(const QModelIndex& idx); +private: + Ui::QWoDBSftpDownListDialog *ui; + QPointer m_sync; + QString m_fileName; +}; + +#endif // QWODBSFTPDOWNLISTDIALOG_H diff --git a/woterm/qwodbsftpdownlistdialog.ui b/woterm/qwodbsftpdownlistdialog.ui new file mode 100644 index 0000000..78495d0 --- /dev/null +++ b/woterm/qwodbsftpdownlistdialog.ui @@ -0,0 +1,75 @@ + + + QWoDBSftpDownListDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reflesh + + + + + + + Select + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/woterm/qwodbsftpdownsync.cpp b/woterm/qwodbsftpdownsync.cpp new file mode 100644 index 0000000..08717c2 --- /dev/null +++ b/woterm/qwodbsftpdownsync.cpp @@ -0,0 +1,215 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbsftpdownsync.h" +#include "qwossh.h" +#include "qwosshconf.h" +#include "qwosetting.h" +#include "qwoutils.h" +#include "qkxcipher.h" + +#include + +QWoDBSftpDownSync::QWoDBSftpDownSync(QObject *parent) + : QObject(parent) +{ + m_pathTemp = QWoSetting::tempPath(); +} + +QWoDBSftpDownSync::~QWoDBSftpDownSync() +{ + +} + +void QWoDBSftpDownSync::listAll() +{ + if(m_sftp == nullptr || !m_sftp->hasRunning()) { + reconnect(); + } + m_sftp->openDir(m_pathUpload); +} + +void QWoDBSftpDownSync::fetchLatest() +{ + if(m_sftp == nullptr || !m_sftp->hasRunning()) { + reconnect(); + } + QString fileName = m_pathUpload + "/" + ".ver"; + runAction(DB_SFTP_DOWNLOAD_CHECK_VERSION, tr("Ready to fetch the latest version information.")); + m_sftp->fileContent(fileName, 0, 512); +} + +void QWoDBSftpDownSync::fetch(const QString &fileName) +{ + if(m_sftp == nullptr || !m_sftp->hasRunning()) { + reconnect(); + } + download(fileName); +} + +void QWoDBSftpDownSync::onConnectionStart() +{ + +} + +void QWoDBSftpDownSync::onConnectionFinished(bool ok) +{ + if(!ok) { + release(); + } +} + +void QWoDBSftpDownSync::onCommandStart(int type, const QVariantMap &userData) +{ + +} + +void QWoDBSftpDownSync::onCommandFinish(int type, const QVariantMap &userData) +{ + QString reason = userData.value("reason").toString(); + if(reason == "fatal") { + finishAction(-9, tr("Failed to download the backup file.")); + return; + } + int code = userData.value("code").toInt(); + if(type == MT_FTP_READ_FILE_CONTENT) { + QVariantMap file = userData.value("fileContent").toMap(); + if(file.isEmpty()) { + if(code == 2) { + finishAction(-1, tr("No relevant version information was found.")); + }else{ + finishAction(-2, tr("Unknow error was found.")); + } + }else{ + QString fileName = file.value("content").toString(); + download(fileName); + } + }else if(type == MT_FTP_DOWNLOAD) { + QString fileName = userData.value("fileName").toString(); + if(reason == "ok") { + finishAction(0, tr("Success to download file:%1").arg(fileName)); + emit fetchFinished(0, fileName); + }else{ + finishAction(-1, tr("Failed to download file:%1").arg(fileName)); + emit fetchFinished(-1, fileName); + } + }else if(type == MT_FTP_LISTFILE) { + if(reason == "ok") { + finishAction(0, tr("Success to list file.")); + }else{ + finishAction(-1, tr("Failed to list file")); + } + } +} + +void QWoDBSftpDownSync::onErrorArrived(const QString &err, const QVariantMap &userData) +{ + finishAction(-11, err); + release(); +} + +void QWoDBSftpDownSync::onFinishArrived(int code) +{ + release(); + finishAction(-9, tr("Failed to login remote server.")); +} + +void QWoDBSftpDownSync::onInputArrived(const QString &title, const QString &prompt, bool visible) +{ + release(); + finishAction(-9, tr("Failed to login remote server.")); +} + +void QWoDBSftpDownSync::onDirOpen(const QString &path, const QVariantList &data, const QVariantMap &user) +{ + QVariantList lsfile = data; + std::sort(lsfile.begin(), lsfile.end(),[](const QVariant& v1, const QVariant& v2){ + QVariantMap vm1 = v1.toMap(); + QVariantMap vm2 = v2.toMap(); + QDateTime t1 = QDateTime::fromString(vm1.value("date").toString(), "dd.MM.yyyy hh:mm:ss"); + QDateTime t2 = QDateTime::fromString(vm2.value("date").toString(), "dd.MM.yyyy hh:mm:ss"); + return t1 > t2; + }); + QStringList fileNames; + for(int i = 0; i < lsfile.length(); i++) { + QMap mdata = lsfile.at(i).toMap(); + QString name = mdata.value("name").toString(); + if(name == ".ver" || name == "." || name == "..") { + continue; + } + fileNames.append(name); + } + emit listArrived(fileNames); +} + +void QWoDBSftpDownSync::release() +{ + if(m_sftp) { + m_sftp->abort(); + QWoSshFactory::instance()->release(m_sftp); + } +} + +void QWoDBSftpDownSync::reconnect() +{ + release(); + m_sftp = QWoSshFactory::instance()->createSftp(); + QObject::connect(m_sftp, SIGNAL(connectionFinished(bool)), this, SLOT(onConnectionFinished(bool))); + QObject::connect(m_sftp, SIGNAL(connectionStart()), this, SLOT(onConnectionStart())); + QObject::connect(m_sftp, SIGNAL(errorArrived(QString,QVariantMap)), this, SLOT(onErrorArrived(QString,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(finishArrived(int)), this, SLOT(onFinishArrived(int))); + QObject::connect(m_sftp, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); + QObject::connect(m_sftp, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); + QObject::connect(m_sftp, SIGNAL(commandStart(int,QVariantMap)), this, SLOT(onCommandStart(int,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(commandFinish(int,QVariantMap)), this, SLOT(onCommandFinish(int,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(dirOpen(QString,QVariantList,QVariantMap)), this, SLOT(onDirOpen(QString,QVariantList,QVariantMap))); + + HostInfo hi; + QVariantMap dm = QWoSetting::value("DBBackup/sftpDetail").toMap(); + hi.host = dm.value("host").toString(); + hi.user = dm.value("name").toString(); + hi.password = dm.value("password").toString(); + hi.identityFile = dm.value("identity").toString(); + hi.port = dm.value("port", 22).toInt(); + m_pathUpload = dm.value("path", "~/woterm_db_backup").toString(); + m_sftp->start(hi, QDateTime::currentSecsSinceEpoch()); +} + +void QWoDBSftpDownSync::download(const QString &fileName) +{ + QString fileRemote = m_pathUpload + "/" + fileName; + QString fileLocal = m_pathTemp + "/" + fileName; + QVariantMap dm; + dm.insert("remote", fileRemote); + dm.insert("local", fileLocal); + dm.insert("fileName", fileName); + dm.insert("command", "download"); + runAction(DB_SFTP_DOWNLOAD_DOWNLOAD_FILE, tr("Ready to download file:%1").arg(QString(fileName))); + m_sftp->download(fileRemote, fileLocal, QWoSshFtp::TP_Append, dm); + +} + +void QWoDBSftpDownSync::removeTempFile() +{ + +} + +void QWoDBSftpDownSync::runAction(int action, const QString &tip) +{ + m_action = action; + emit infoArrived(action, 0, tip); +} + +void QWoDBSftpDownSync::finishAction(int err, const QString &errDesc) +{ + emit infoArrived(m_action, err, errDesc); + removeTempFile(); +} diff --git a/woterm/qwodbsftpdownsync.h b/woterm/qwodbsftpdownsync.h new file mode 100644 index 0000000..e767466 --- /dev/null +++ b/woterm/qwodbsftpdownsync.h @@ -0,0 +1,57 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBSFTPDOWNSYNC_H +#define QWODBSFTPDOWNSYNC_H + +#include +#include + +#define DB_SFTP_DOWNLOAD_CHECK_VERSION (1) +#define DB_SFTP_DOWNLOAD_DOWNLOAD_FILE (2) + +class QWoSshFtp; +class QWoDBSftpDownSync : public QObject +{ + Q_OBJECT +public: + explicit QWoDBSftpDownSync(QObject *parent = nullptr); + ~QWoDBSftpDownSync(); + void listAll(); + void fetchLatest(); + void fetch(const QString& fileName); +signals: + void infoArrived(int action, int err, const QString& errDesc); + void listArrived(const QStringList& fileNames); + void fetchFinished(int err, const QString& fileName); +private slots: + void onConnectionStart(); + void onConnectionFinished(bool ok); + void onCommandStart(int type, const QVariantMap& userData); + void onCommandFinish(int type, const QVariantMap& userData); + void onErrorArrived(const QString& err, const QVariantMap& userData); + void onFinishArrived(int code); + void onInputArrived(const QString &title, const QString &prompt, bool visible); + void onDirOpen(const QString& path, const QVariantList& data, const QVariantMap& user); +private: + void release(); + void reconnect(); + void download(const QString& fileName); + void removeTempFile(); + void runAction(int action, const QString& tip); + void finishAction(int err, const QString& errDesc); +private: + QPointer m_sftp; + QString m_pathUpload, m_pathTemp, m_nameCrypt; + int m_action; +}; + +#endif // QWODBSFTPDOWNSYNC_H diff --git a/woterm/qwodbsftpuploadsync.cpp b/woterm/qwodbsftpuploadsync.cpp new file mode 100644 index 0000000..c5fd239 --- /dev/null +++ b/woterm/qwodbsftpuploadsync.cpp @@ -0,0 +1,286 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwodbsftpuploadsync.h" +#include "qwossh.h" +#include "qwosshconf.h" +#include "qwosetting.h" +#include "qwoutils.h" +#include "qkxcipher.h" + + +#include +#include +#include + +QWoDBSftpUploadSync::QWoDBSftpUploadSync(QObject *parent) + : QObject(parent) +{ + m_pathTemp = QWoSetting::tempPath(); +} + +QWoDBSftpUploadSync::~QWoDBSftpUploadSync() +{ + removeTempFile(); + release(); +} + +void QWoDBSftpUploadSync::upload(const QString &type, const QString &key) +{ + removeTempFile(); + + QString fileClone = m_pathTemp + "/woterm_backup.db"; + runAction(DB_SFTP_UPLOAD_ENCRYPT_FILE, tr("ready to clone the db.")); + if(!QWoSshConf::instance()->backup(fileClone)) { + finishAction(-1, tr("Failed to clone the backup file:%1.").arg(fileClone)); + return; + } + QFile bf(fileClone); + if(!bf.open(QIODevice::ReadOnly)) { + finishAction(-2, tr("Failed to read the backup file:%1.").arg(fileClone)); + return; + } + QByteArray all = bf.readAll(); + bf.close(); + + QByteArray out; + if(type == "AES-CBC-256") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 32); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 16); + if(!QKxCipher::aesCbcEncrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "AES-CTR-256") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 32); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 16); + if(!QKxCipher::aesCtrEncrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "AES-GCM-256") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 32); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 16); + if(!QKxCipher::aesGcmEncrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "DES-CBC") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 24); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 8); + if(!QKxCipher::tripleDesCbcEncrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "DES-ECB") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 24); + if(!QKxCipher::tripleDesEcbEncrypt(all, out, pass, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "DES-OFB64") { + QByteArray pass = QKxCipher::makeBytes(key.toUtf8(), 24); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 8); + if(!QKxCipher::tripleDesOfb64Encrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "RC4") { + QByteArray pass = key.toUtf8(); + if(!QKxCipher::rc4Encrypt(all, out, pass, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + }else if(type == "Blowfish") { + QByteArray pass = key.toUtf8(); + QByteArray ivec = QKxCipher::makeBytes(key.toUtf8(), 8); + if(!QKxCipher::blowfishEcbEncrypt(all, out, pass, ivec, true)) { + finishAction(-3, tr("Failed to encrypt the backup file:%1.").arg(fileClone)); + return; + } + } + + QByteArray sha1 = QCryptographicHash::hash(out, QCryptographicHash::Sha1); + QString hexFull = sha1.toHex(); + QString hex = hexFull.left(12); + QDate dt = QDate::currentDate(); + m_nameCrypt = QString("woterm_%1%2%3_%4.db").arg(dt.year()).arg(dt.month(),2,10,QLatin1Char('0')).arg(dt.day(),2,10,QLatin1Char('0')).arg(hex); + QString fileCrypt = m_pathTemp + "/" + m_nameCrypt; + QFile uf(fileCrypt); + if(!uf.open(QIODevice::WriteOnly)) { + finishAction(-3, tr("Failed to write the encrypt file:%1.").arg(fileCrypt)); + return; + } + uf.write(out); + uf.close(); + + + reconnect(); + + runAction(DB_SFTP_UPLOAD_CHECK_UPLOAD_PATH, tr("Ready to check server upload path.")); + QVariantMap dm; + dm.insert("pathUpload", m_pathUpload); + m_sftp->fileInfo(m_pathUpload, dm); +} + +void QWoDBSftpUploadSync::release() +{ + if(m_sftp) { + m_sftp->abort(); + QWoSshFactory::instance()->release(m_sftp); + } +} + +void QWoDBSftpUploadSync::removeTempFile() +{ + if(!m_nameCrypt.isEmpty()) { + QFile::remove(m_pathTemp + "/" + m_nameCrypt); + } +} + +void QWoDBSftpUploadSync::runAction(int action, const QString &tip) +{ + m_action = action; + emit infoArrived(action, 0, tip); +} + +void QWoDBSftpUploadSync::finishAction(int err, const QString &errDesc) +{ + emit infoArrived(m_action, err, errDesc); + removeTempFile(); +} + +void QWoDBSftpUploadSync::onConnectionStart() +{ + +} + +void QWoDBSftpUploadSync::onConnectionFinished(bool ok) +{ + if(!ok) { + release(); + } +} + +void QWoDBSftpUploadSync::onCommandStart(int type, const QVariantMap &userData) +{ + +} + +void QWoDBSftpUploadSync::onCommandFinish(int type, const QVariantMap &userData) +{ + QString reason = userData.value("reason").toString(); + if(reason == "fatal") { + finishAction(-9, tr("Failed to upload backup file.")); + return; + } + int code = userData.value("code").toInt(); + if(type == MT_FTP_FILE_INFO) { + QString path = userData.value("pathUpload").toString(); + QVariantMap fi = userData.value("fileInfo").toMap(); + if(fi.isEmpty()) { + if(code == 2) { + finishAction(-1, tr("The path is not exist:%1").arg(path)); + }else{ + finishAction(-2, tr("Failed to check path:%1.").arg(path)); + } + }else{ + QString fileRemote = m_pathUpload + "/" + ".ver"; + QVariantMap dm; + dm.insert("remote", fileRemote); + dm.insert("command", "fileContent"); + m_sftp->fileContent(fileRemote, 0, 512, dm); + runAction(DB_SFTP_UPLOAD_CHECK_VERSION, tr("Ready to check version information.")); + } + }else if(type == MT_FTP_UPLOAD) { + QString file = userData.value("remote").toString(); + if(reason == "ok") { + finishAction(0, tr("success to backup file:%1.").arg(file)); + }else{ + finishAction(-1, tr("Failed to upload the encrypt file:%1.").arg(file)); + } + }else if(type == MT_FTP_READ_FILE_CONTENT) { + QVariantMap file = userData.value("fileContent").toMap(); + if(file.isEmpty()) { + runAction(DB_SFTP_UPLOAD_WRITE_VERSION, tr("Ready to write version information.")); + QString fileRemote = m_pathUpload + "/" + ".ver"; + m_sftp->writeFileContent(fileRemote, m_nameCrypt.toUtf8()); + }else{ + QByteArray fileName = file.value("content").toByteArray(); + QByteArray hex = fileName.mid(fileName.lastIndexOf('_')); + int idx = m_nameCrypt.lastIndexOf(hex); + if(idx > 0) { + // The server already has the same backup, check it's integrity + QString fileLocal = m_pathTemp + "/" + m_nameCrypt; + QString fileRemote = m_pathUpload + "/" + fileName; + runAction(DB_SFTP_UPLOAD_UPLOAD_FILE, tr("Ready to upload the encrypt file.")); + QVariantMap dm; + dm.insert("remote", fileName); + m_sftp->upload(fileLocal, fileRemote, QWoSshFtp::TP_Append, dm); + }else{ + runAction(DB_SFTP_UPLOAD_WRITE_VERSION, tr("Ready to write version information.")); + QString fileRemote = m_pathUpload + "/" + ".ver"; + m_sftp->writeFileContent(fileRemote, m_nameCrypt.toUtf8()); + } + } + }else if(type == MT_FTP_WRITE_FILE_CONTENT) { + if(reason == "error") { + finishAction(-1, tr("Failed to write version information.")); + }else{ + runAction(DB_SFTP_UPLOAD_UPLOAD_FILE, tr("Ready to upload the encrypt file.")); + QString fileLocal = m_pathTemp + "/" + m_nameCrypt; + QString fileRemote = m_pathUpload + "/" + m_nameCrypt; + QVariantMap dm; + dm.insert("remote", m_nameCrypt); + m_sftp->upload(fileLocal, fileRemote, QWoSshFtp::TP_Append, dm); + } + } +} + +void QWoDBSftpUploadSync::onErrorArrived(const QString &err, const QVariantMap &userData) +{ + release(); +} + +void QWoDBSftpUploadSync::onFinishArrived(int code) +{ + release(); +} + +void QWoDBSftpUploadSync::onInputArrived(const QString &title, const QString &prompt, bool visible) +{ + release(); + finishAction(-9, tr("Failed to login remote server.")); +} + +void QWoDBSftpUploadSync::reconnect() +{ + release(); + m_sftp = QWoSshFactory::instance()->createSftp(); + QObject::connect(m_sftp, SIGNAL(connectionFinished(bool)), this, SLOT(onConnectionFinished(bool))); + QObject::connect(m_sftp, SIGNAL(connectionStart()), this, SLOT(onConnectionStart())); + QObject::connect(m_sftp, SIGNAL(errorArrived(QString,QVariantMap)), this, SLOT(onErrorArrived(QString,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(finishArrived(int)), this, SLOT(onFinishArrived(int))); + QObject::connect(m_sftp, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); + QObject::connect(m_sftp, SIGNAL(commandStart(int,QVariantMap)), this, SLOT(onCommandStart(int,QVariantMap))); + QObject::connect(m_sftp, SIGNAL(commandFinish(int,QVariantMap)), this, SLOT(onCommandFinish(int,QVariantMap))); + + HostInfo hi; + QVariantMap dm = QWoSetting::value("DBBackup/sftpDetail").toMap(); + hi.host = dm.value("host").toString(); + hi.user = dm.value("name").toString(); + hi.password = dm.value("password").toString(); + hi.identityFile = dm.value("identity").toString(); + hi.port = dm.value("port", 22).toInt(); + m_pathUpload = dm.value("path", "~/woterm_db_backup").toString(); + m_sftp->start(hi, QDateTime::currentSecsSinceEpoch()); +} + diff --git a/woterm/qwodbsftpuploadsync.h b/woterm/qwodbsftpuploadsync.h new file mode 100644 index 0000000..eb63d43 --- /dev/null +++ b/woterm/qwodbsftpuploadsync.h @@ -0,0 +1,55 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWODBSFTPUPLOADSYNC_H +#define QWODBSFTPUPLOADSYNC_H + +#include +#include + +#define DB_SFTP_UPLOAD_ENCRYPT_FILE (1) +#define DB_SFTP_UPLOAD_CHECK_UPLOAD_PATH (2) +#define DB_SFTP_UPLOAD_CHECK_VERSION (3) +#define DB_SFTP_UPLOAD_WRITE_VERSION (4) +#define DB_SFTP_UPLOAD_UPLOAD_FILE (5) +#define DB_SFTP_UPLOAD_FATAL (99) + +class QWoSshFtp; +class QWoDBSftpUploadSync : public QObject +{ + Q_OBJECT +public: + explicit QWoDBSftpUploadSync(QObject *parent = nullptr); + virtual ~QWoDBSftpUploadSync(); + void upload(const QString& type, const QString& key); +signals: + void infoArrived(int action, int err, const QString& errDesc); +private slots: + void onConnectionStart(); + void onConnectionFinished(bool ok); + void onCommandStart(int type, const QVariantMap& userData); + void onCommandFinish(int type, const QVariantMap& userData); + void onErrorArrived(const QString& err, const QVariantMap& userData); + void onFinishArrived(int code); + void onInputArrived(const QString &title, const QString &prompt, bool visible); +private: + void reconnect(); + void release(); + void removeTempFile(); + void runAction(int action, const QString& tip); + void finishAction(int err, const QString& errDesc); +private: + QPointer m_sftp; + QString m_pathUpload, m_pathTemp, m_nameCrypt; + int m_action; +}; + +#endif // QWODBSFTPUPLOADSYNC_H diff --git a/woterm/qwoglobal.h b/woterm/qwoglobal.h index cc25e0c..1622b9a 100644 --- a/woterm/qwoglobal.h +++ b/woterm/qwoglobal.h @@ -187,6 +187,52 @@ struct TaskInfo { } }; +#define DB_MERGE_ACTION_ADD (1) +#define DB_MERGE_ACTION_REMOVE (2) +#define DB_MERGE_ACTION_REPLACE (3) +struct MergeInfo { + QList same; // + QList lhave; // only local has but remote. + QList rhave; // only remote has but local. + QList replace; // different, can replace by remote. + QList remove; // only hidden. + + void clear() { + same.clear(); + lhave.clear(); + rhave.clear(); + replace.clear(); + remove.clear(); + } + + QList result(bool isFull) const { + if(isFull) { + return rhave + lhave + replace + same; + } + return rhave + lhave + replace; + } + QString resultInformation() const { + if(rhave.isEmpty() && lhave.isEmpty() && replace.isEmpty()) { + return QObject::tr("The current records are exactly the same."); + } + QStringList summarys; + if(!rhave.isEmpty()) { + summarys.append(QObject::tr("Number of records that can be added: %1").arg(rhave.length())); + } + if(!lhave.isEmpty()) { + summarys.append(QObject::tr("Number of records that can be removed: %1").arg(lhave.length())); + } + if(!replace.isEmpty()) { + summarys.append(QObject::tr("Number of records that can be replaced: %1").arg(replace.length())); + } + if(!same.isEmpty()) { + summarys.append(QObject::tr("Number of records that are the same: %1").arg(same.length())); + } + return summarys.join("\r\n"); + } +}; + +Q_DECLARE_METATYPE(MergeInfo) Q_DECLARE_METATYPE(TaskInfo) Q_DECLARE_METATYPE(GroupInfo) Q_DECLARE_METATYPE(HostInfo) diff --git a/woterm/qwohostinfoedit.cpp b/woterm/qwohostinfoedit.cpp index 87c2761..45282d9 100644 --- a/woterm/qwohostinfoedit.cpp +++ b/woterm/qwohostinfoedit.cpp @@ -15,8 +15,9 @@ #include "qwosshconf.h" #include "qwohostsimplelist.h" #include "qwosetting.h" +#include "qkxmessagebox.h" + -#include #include #include #include @@ -79,15 +80,15 @@ void QWoHostInfoEdit::onButtonSaveClicked() hi.proxyJump = ui->jump->currentText(); if(hi.name.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The name can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The name can't be empty")); return; } if(hi.host.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The host can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The host can't be empty")); return; } if(hi.port < 10 || hi.port > 65535) { - QMessageBox::warning(this, tr("Info"), tr("The port should be at [10,65535]")); + QKxMessageBox::warning(this, tr("Info"), tr("The port should be at [10,65535]")); return; } close(); diff --git a/woterm/qwohostlistmodel.cpp b/woterm/qwohostlistmodel.cpp index 0add31e..aff37fb 100644 --- a/woterm/qwohostlistmodel.cpp +++ b/woterm/qwohostlistmodel.cpp @@ -30,6 +30,8 @@ QWoHostListModel::QWoHostListModel(QObject *parent) m_vncIcon = QIcon(QPixmap(":/woterm/resource/skin/vnc2.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); m_serialIcon = QIcon(QPixmap(":/woterm/resource/skin/serialport.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); QMetaObject::invokeMethod(this, "refreshList", Qt::QueuedConnection); + + QObject::connect(QWoSshConf::instance(), SIGNAL(dataReset()), this, SLOT(onDataReset())); } QWoHostListModel::~QWoHostListModel() @@ -83,6 +85,11 @@ void QWoHostListModel::modifyOrAppend(const HostInfo &hi) } } +void QWoHostListModel::onDataReset() +{ + refreshList(); +} + int QWoHostListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()){ diff --git a/woterm/qwohostlistmodel.h b/woterm/qwohostlistmodel.h index e09ff85..875733b 100644 --- a/woterm/qwohostlistmodel.h +++ b/woterm/qwohostlistmodel.h @@ -33,6 +33,8 @@ class QWoHostListModel : public QAbstractListModel bool exists(const QString &name); void resetAllProperty(QString v); void modifyOrAppend(const HostInfo& hi); +private slots: + void onDataReset(); private: int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex sibling(int row, int column, const QModelIndex &idx) const override; diff --git a/woterm/qwohosttreemodel.cpp b/woterm/qwohosttreemodel.cpp index 41ef1c9..8e88635 100644 --- a/woterm/qwohosttreemodel.cpp +++ b/woterm/qwohosttreemodel.cpp @@ -28,6 +28,7 @@ QWoHostTreeModel::QWoHostTreeModel(QObject *parent) m_folderCloseIcon = QIcon(QPixmap(":/woterm/resource/skin/dir_close.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); QMetaObject::invokeMethod(this, "refreshList", Qt::QueuedConnection); + QObject::connect(QWoSshConf::instance(), SIGNAL(dataReset()), this, SLOT(onDataReset())); } QWoHostTreeModel::~QWoHostTreeModel() @@ -117,6 +118,11 @@ void QWoHostTreeModel::refreshList() setHorizontalHeaderLabels(labels); } +void QWoHostTreeModel::onDataReset() +{ + refreshList(); +} + QVariant QWoHostTreeModel::data(const QModelIndex &index, int role) const { if(role == Qt::SizeHintRole) { diff --git a/woterm/qwohosttreemodel.h b/woterm/qwohosttreemodel.h index da21b27..49aff72 100644 --- a/woterm/qwohosttreemodel.h +++ b/woterm/qwohosttreemodel.h @@ -23,6 +23,8 @@ class QWoHostTreeModel : public QStandardItemModel static QWoHostTreeModel *instance(); Q_INVOKABLE void refreshList(); +private slots: + void onDataReset(); private: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: diff --git a/woterm/qwoidentify.cpp b/woterm/qwoidentify.cpp index 453ed95..2eb32a7 100644 --- a/woterm/qwoidentify.cpp +++ b/woterm/qwoidentify.cpp @@ -169,6 +169,32 @@ bool QWoIdentify::create(const QString &name, const QByteArray &prvKey) return false; } +bool QWoIdentify::infomation(const QByteArray &name, IdentifyInfo *pinfo) +{ + QString dbFile = QWoSetting::sshServerDbPath(); + try{ + SQLite::Database db(dbFile.toUtf8(), SQLite::OPEN_READONLY); + if(!db.tableExists("identities")) { + return false; + } + SQLite::Statement query(db, "select * from identities where delFlag=0 and where name=@name"); + query.bind("@name", name.toStdString()); + while(query.executeStep()) { + IdentifyInfo info; + QByteArray rsa = QByteArray::fromStdString(query.getColumn("prvKey").getString()); + if(!infomationByPrivateKey(rsa, &info)) { + continue; + } + info.name = QString::fromStdString(query.getColumn("name").getString()); + *pinfo = info; + return true; + } + }catch(std::exception& e) { + qDebug() << "Identify-information" << e.what(); + } + return false; +} + bool QWoIdentify::rename(const QString &nameOld, const QString &nameNew) { QString dbFile = QWoSetting::sshServerDbPath(); diff --git a/woterm/qwoidentify.h b/woterm/qwoidentify.h index 690b4af..3de1039 100644 --- a/woterm/qwoidentify.h +++ b/woterm/qwoidentify.h @@ -28,6 +28,7 @@ class QWoIdentify : public QObject static bool import(const QString& file, IdentifyInfo *pinfo); static bool create(const QString& file); static bool create(const QString& name, const QByteArray& prvKey); + static bool infomation(const QByteArray& name, IdentifyInfo *pinfo); static bool rename(const QString& nameOld, const QString& nameNew); static bool remove(const QString& name); static QMap loadFromSqlite(); diff --git a/woterm/qwoidentifycreatedialog.cpp b/woterm/qwoidentifycreatedialog.cpp index 1dfe2a6..0e20730 100644 --- a/woterm/qwoidentifycreatedialog.cpp +++ b/woterm/qwoidentifycreatedialog.cpp @@ -15,9 +15,9 @@ #include "qwoutils.h" #include "qwosetting.h" #include "qwoidentify.h" +#include "qkxmessagebox.h" #include -#include #include #include @@ -49,7 +49,7 @@ void QWoIdentifyCreateDialog::onButtonCreateClicked() { QString name = ui->name->text(); if(name.isEmpty()) { - QMessageBox::warning(this, "Warning", "The name should not be empty", QMessageBox::Ok); + QKxMessageBox::warning(this, "Warning", "The name should not be empty", QMessageBox::Ok); return; } ssh_keytypes_e type = ui->typED25519->isChecked() ? SSH_KEYTYPE_ED25519 : SSH_KEYTYPE_RSA; @@ -57,7 +57,7 @@ void QWoIdentifyCreateDialog::onButtonCreateClicked() ssh_key key = nullptr; int err = ssh_pki_generate(type, bits, &key); if(err != SSH_OK) { - QMessageBox::information(this, tr("info"), QString(tr("failed to create identify:[%1]")).arg(name)); + QKxMessageBox::information(this, tr("info"), QString(tr("failed to create identify:[%1]")).arg(name)); return ; } char *b64 = nullptr; @@ -68,7 +68,7 @@ void QWoIdentifyCreateDialog::onButtonCreateClicked() ssh_string_free_char(b64); } if(!QWoIdentify::create(name, content)) { - QMessageBox::information(this, tr("info"), QString(tr("failed to save identity for name already exist:[%1]")).arg(name)); + QKxMessageBox::information(this, tr("info"), QString(tr("failed to save identity for name already exist:[%1]")).arg(name)); return ; } done(QDialog::Accepted); diff --git a/woterm/qwoidentifydialog.cpp b/woterm/qwoidentifydialog.cpp index fc2ee56..dd22ddd 100644 --- a/woterm/qwoidentifydialog.cpp +++ b/woterm/qwoidentifydialog.cpp @@ -17,11 +17,11 @@ #include "qwoidentifypublickeydialog.h" #include "qwoidentifycreatedialog.h" #include "qwoidentify.h" +#include "qkxmessagebox.h" #include #include #include -#include #include #include #include @@ -96,16 +96,16 @@ void QWoIdentifyDialog::onButtonImportClicked() } fileName = QDir::toNativeSeparators(fileName); if(QWoIdentify::isPublicKey(fileName)) { - QMessageBox::information(this, tr("info"), tr("Public key file import is not supported. Please select a valid private key file.")); + QKxMessageBox::information(this, tr("info"), tr("Public key file import is not supported. Please select a valid private key file.")); return; } if(!QWoIdentify::isPrivateKey(fileName)) { - QMessageBox::information(this, tr("info"), tr("Invalid private key file. Please select a valid private key file.")); + QKxMessageBox::information(this, tr("info"), tr("Invalid private key file. Please select a valid private key file.")); return; } IdentifyInfo info; if(!QWoIdentify::import(fileName, &info)) { - QMessageBox::information(this, tr("info"), tr("the identify's file is bad")); + QKxMessageBox::information(this, tr("info"), tr("the identify's file is bad")); return; } @@ -116,7 +116,7 @@ void QWoIdentifyDialog::onButtonImportClicked() QString figure = idx.data(ROLE_IDENTIFY_FIGURE).toString(); if(figure == info.fingureprint) { ui->identify->setCurrentIndex(idx); - QMessageBox::information(this, tr("info"), tr("the same identify had been exist by name: %1").arg(name)); + QKxMessageBox::information(this, tr("info"), tr("the same identify had been exist by name: %1").arg(name)); return; } } @@ -146,7 +146,7 @@ void QWoIdentifyDialog::onButtonExportClicked() { QModelIndex idx = ui->identify->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QAbstractItemModel *model = ui->identify->model(); @@ -177,14 +177,14 @@ void QWoIdentifyDialog::onButtonDeleteClicked() { QModelIndex idx = ui->identify->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QAbstractItemModel *model = ui->identify->model(); QModelIndex idx2 = model->index(idx.row(), 0); QString name = idx2.data().toString(); if(!QWoIdentify::remove(name)) { - QMessageBox::warning(this, tr("Warning"), tr("failed to delete %1").arg(name)); + QKxMessageBox::warning(this, tr("Warning"), tr("failed to delete %1").arg(name)); return; } model->removeRow(idx.row()); @@ -194,7 +194,7 @@ void QWoIdentifyDialog::onButtonSelectClicked() { QModelIndex idx = ui->identify->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QAbstractItemModel *model = ui->identify->model(); @@ -207,7 +207,7 @@ void QWoIdentifyDialog::onButtonRenameClicked() { QModelIndex idx = ui->identify->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QAbstractItemModel *model = ui->identify->model(); @@ -218,7 +218,7 @@ void QWoIdentifyDialog::onButtonRenameClicked() return; } if(!QWoIdentify::rename(name, nameNew)) { - QMessageBox::warning(this, tr("Warning"), tr("failed to rename '%1' to '%2'").arg(name).arg(nameNew)); + QKxMessageBox::warning(this, tr("Warning"), tr("failed to rename '%1' to '%2'").arg(name).arg(nameNew)); return; } reload(); @@ -228,14 +228,14 @@ void QWoIdentifyDialog::onButtonViewClicked() { QModelIndex idx = ui->identify->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QAbstractItemModel *model = ui->identify->model(); QModelIndex idx2 = model->index(idx.row(), 0); QString key = idx2.data(ROLE_IDENTIFY_PUBKEY).toString(); if(key.isEmpty()) { - QMessageBox::information(this, tr("info"), tr("no selection")); + QKxMessageBox::information(this, tr("info"), tr("no selection")); return; } QString name = idx2.data().toString(); diff --git a/woterm/qwomainwindow.cpp b/woterm/qwomainwindow.cpp index 009f0a1..10f9964 100644 --- a/woterm/qwomainwindow.cpp +++ b/woterm/qwomainwindow.cpp @@ -30,13 +30,15 @@ #include "qwosessionproperty.h" #include "qwosessionmoreproperty.h" #include "qwosshconf.h" +#include "qwodbbackupdialog.h" #include "qwodbrestoredialog.h" +#include "qwodbpowerrestoredialog.h" #include "qkxprocesslaunch.h" +#include "qkxmessagebox.h" #include "qkxver.h" #include "version.h" #include -#include #include #include #include @@ -123,7 +125,7 @@ void QWoMainWindow::closeEvent(QCloseEvent *event) { saveLastState(); - QMessageBox::StandardButton btn = QMessageBox::warning(this, tr("Confirm"), tr("Exit Or Not?"), QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, tr("Confirm"), tr("Exit Or Not?"), QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { event->setAccepted(false); return ; @@ -256,7 +258,7 @@ void QWoMainWindow::onAppStart() { if(!QKxVer::isUltimate()) { if(QWoSetting::shouldPopupUpgradeUltimate()) { - int ret = QMessageBox::question(this, tr("Upgrade to ultimate version"), tr("The current version is free. It is recommended to upgrade to the ultimate version."), QMessageBox::Yes|QMessageBox::No); + int ret = QKxMessageBox::question(this, tr("Upgrade to ultimate version"), tr("The current version is free. It is recommended to upgrade to the ultimate version."), QMessageBox::Yes|QMessageBox::No); if(ret == QMessageBox::Yes) { QDesktopServices::openUrl(QUrl("http://woterm.com")); } @@ -285,7 +287,7 @@ void QWoMainWindow::onVersionCheck(int code, const QByteArray &body) return; } QWoSetting::setIgnoreTodayUpgradeVersion(verBody); - int ret = QMessageBox::question(this, tr("Version check"), tr("a new version of %1 is found, do you want to update it?").arg(verBody), QMessageBox::Yes|QMessageBox::No); + int ret = QKxMessageBox::question(this, tr("Version check"), tr("a new version of %1 is found, do you want to update it?").arg(verBody), QMessageBox::Yes|QMessageBox::No); if(ret == QMessageBox::Yes) { QDesktopServices::openUrl(QUrl("http://woterm.com")); } @@ -313,24 +315,34 @@ void QWoMainWindow::onActionOpenTriggered() void QWoMainWindow::onActionBackupTriggered() { - QString path = QWoSetting::lastBackupPath(); - QString fileName = QFileDialog::getSaveFileName(this, tr("Backup Session Database"), path, "SQLite3 (*.db *.bak)"); - qDebug() << "fileName" << fileName; - if(fileName.isEmpty()) { - return; - } - QFileInfo fi(fileName); - QString last = fi.absolutePath(); - QWoSetting::setLastBackupPath(last); - if(!QWoSshConf::instance()->backup(fileName)) { - QMessageBox::warning(this, tr("Failure"), tr("failed to backup the session list.")); + if(QKxVer::isUltimate()) { + QWoDbBackupDialog dlg(this); + dlg.exec(); + }else{ + QString path = QWoSetting::lastBackupPath(); + QString fileName = QFileDialog::getSaveFileName(this, tr("Backup Session Database"), path, "SQLite3 (*.db *.bak)"); + qDebug() << "fileName" << fileName; + if(fileName.isEmpty()) { + return; + } + QFileInfo fi(fileName); + QString last = fi.absolutePath(); + QWoSetting::setLastBackupPath(last); + if(!QWoSshConf::instance()->backup(fileName)) { + QKxMessageBox::warning(this, tr("Failure"), tr("failed to backup the session list.")); + } } } void QWoMainWindow::onActionRestoreTriggered() { - QWoDBRestoreDialog dlg(this); - dlg.exec(); + if(QKxVer::isUltimate()) { + QWoDBPowerRestoreDialog dlg(this); + dlg.exec(); + }else{ + QWoDBRestoreDialog dlg(this); + dlg.exec(); + } } void QWoMainWindow::onActionExitTriggered() @@ -357,7 +369,7 @@ void QWoMainWindow::onActionConfigDefaultTriggered() QString langNow = QWoSetting::languageFile(); if(lang != langNow) { QWoSetting::setLanguageFile(lang); - if(QMessageBox::warning(this, tr("Language"), tr("The language has been changed, restart application to take effect right now."), QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) { + if(QKxMessageBox::warning(this, tr("Language"), tr("The language has been changed, restart application to take effect right now."), QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) { QString path = QCoreApplication::instance()->applicationFilePath(); if(::QKxProcessLaunch::startDetached(path)) { QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); @@ -423,7 +435,7 @@ void QWoMainWindow::onActionAdminTriggered() QString hitTxt = input.textValue(); if(!pass.isEmpty()) { if(pass != hitTxt) { - QMessageBox::information(this, tr("Password error"), tr("the password is not right.")); + QKxMessageBox::information(this, tr("Password error"), tr("the password is not right.")); return; } }else{ @@ -536,7 +548,7 @@ bool QWoMainWindow::checkAdminLogin() if(pass == hitTxt) { return true; } - QMessageBox::information(this, tr("Login failure"), tr("The password is wrong, %1 times left to try.").arg(i)); + QKxMessageBox::information(this, tr("Login failure"), tr("The password is wrong, %1 times left to try.").arg(i)); } return false; } diff --git a/woterm/qwopasswordinput.cpp b/woterm/qwopasswordinput.cpp index 6b1b3e8..34bf72c 100644 --- a/woterm/qwopasswordinput.cpp +++ b/woterm/qwopasswordinput.cpp @@ -12,8 +12,9 @@ #include "qwopasswordinput.h" #include "ui_qwopasswordinput.h" +#include "qkxmessagebox.h" + #include -#include #include QWoPasswordInput::QWoPasswordInput(QWidget *parent) @@ -62,7 +63,7 @@ void QWoPasswordInput::onClose() { QString pass = ui->password->text(); if(pass.isEmpty()) { - QMessageBox::StandardButton btn = QMessageBox::warning(this, tr("Tip"), tr("The Password is Empty, continue to finish?"), QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, tr("Tip"), tr("The Password is Empty, continue to finish?"), QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return ; } diff --git a/woterm/qworlogintermwidget.cpp b/woterm/qworlogintermwidget.cpp index 91e1968..bdd4694 100644 --- a/woterm/qworlogintermwidget.cpp +++ b/woterm/qworlogintermwidget.cpp @@ -28,6 +28,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxmessagebox.h" #include @@ -35,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -235,7 +235,7 @@ void QWoRLoginTermWidget::onDuplicateInNewWindow() void QWoRLoginTermWidget::onModifyThisSession() { if(!QWoSshConf::instance()->exists(m_target)){ - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); return; } QWoSessionProperty dlg(this); diff --git a/woterm/qwoserialwidgetimpl.cpp b/woterm/qwoserialwidgetimpl.cpp index 2c151a7..d4b3e28 100644 --- a/woterm/qwoserialwidgetimpl.cpp +++ b/woterm/qwoserialwidgetimpl.cpp @@ -19,10 +19,10 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" #include "qwosessionproperty.h" +#include "qkxmessagebox.h" #include #include -#include #include #include #include @@ -129,7 +129,7 @@ void QWoSerialWidgetImpl::onConnectReady(const QString &target) } if(!QWoSshConf::instance()->find(m_target, &hi)) { m_input->reset(); - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete")); return; } @@ -173,7 +173,7 @@ void QWoSerialWidgetImpl::onConnectReady(const QString &target) } if(!m_serial->open(QIODevice::ReadWrite)) { m_input->reset(); - QMessageBox::warning(this, tr("Error"), tr("Failed to open device.")); + QKxMessageBox::warning(this, tr("Error"), tr("Failed to open device.")); return; } QString params; @@ -201,7 +201,7 @@ void QWoSerialWidgetImpl::onSendText(const QString &txt) void QWoSerialWidgetImpl::onMoreReady() { if(!QWoSshConf::instance()->exists(m_target)){ - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete")); return; } QWoSessionProperty dlg(this); @@ -360,7 +360,7 @@ void QWoSerialTermWidget::onCopyToClipboard() void QWoSerialTermWidget::onModifyThisSession() { if(!QWoSshConf::instance()->exists(m_target)){ - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); return; } QWoSessionProperty dlg(this); diff --git a/woterm/qwosessionlist.cpp b/woterm/qwosessionlist.cpp index 268ddb3..6d34dfe 100644 --- a/woterm/qwosessionlist.cpp +++ b/woterm/qwosessionlist.cpp @@ -19,6 +19,7 @@ #include "qwosessionproperty.h" #include "qwogroupinputdialog.h" #include "qwosortfilterproxymodel.h" +#include "qkxmessagebox.h" #include "qkxver.h" #include "qwoutils.h" @@ -40,7 +41,6 @@ #include #include #include -#include #define MAX_TRY_LEFT (10) @@ -365,7 +365,7 @@ void QWoSessionList::onListViewItemAdd() void QWoSessionList::onListViewItemDelete() { - QMessageBox::StandardButton btn = QMessageBox::warning(this, "delete", "delete all the selective items?", QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, "delete", "delete all the selective items?", QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return ; } diff --git a/woterm/qwosessionmanage.cpp b/woterm/qwosessionmanage.cpp index f7476ee..86d8c38 100644 --- a/woterm/qwosessionmanage.cpp +++ b/woterm/qwosessionmanage.cpp @@ -24,6 +24,7 @@ #include "qwohosttreemodel.h" #include "qwohostlistmodel.h" #include "qwogroupinputdialog.h" +#include "qkxmessagebox.h" #include "qkxver.h" #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -133,7 +133,7 @@ void QWoSessionManage::onSshConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_SSH); @@ -152,7 +152,7 @@ void QWoSessionManage::onSftpConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_SFTP); @@ -171,7 +171,7 @@ void QWoSessionManage::onTelnetConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_TELNET); @@ -190,7 +190,7 @@ void QWoSessionManage::onRLoginConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_RLOGIN); @@ -209,7 +209,7 @@ void QWoSessionManage::onMstscConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_MSTSC); @@ -228,7 +228,7 @@ void QWoSessionManage::onVncConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_VNC); @@ -247,7 +247,7 @@ void QWoSessionManage::onSerialPortConnectReady() break; } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets.at(0), EOT_SERIALPORT); @@ -259,7 +259,7 @@ void QWoSessionManage::onOpenReady() QItemSelectionModel *model = m_tree->selectionModel(); QModelIndexList idxs = model->selectedRows(); if(idxs.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } QModelIndex idx = idxs.at(0); @@ -272,10 +272,10 @@ void QWoSessionManage::onDeleteReady() QItemSelectionModel *model = m_tree->selectionModel(); QModelIndexList idxs = model->selectedRows(); if(idxs.isEmpty()) { - QMessageBox::warning(this, "delete", "no selection?", QMessageBox::Ok|QMessageBox::No); + QKxMessageBox::warning(this, "delete", "no selection?", QMessageBox::Ok|QMessageBox::No); return; } - QMessageBox::StandardButton btn = QMessageBox::warning(this, "delete", "delete all the selective items?", QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, "delete", "delete all the selective items?", QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return ; } @@ -298,7 +298,7 @@ void QWoSessionManage::onModifyReady() { QModelIndex idx = m_tree->currentIndex(); if(!idx.isValid()) { - QMessageBox::information(this, tr("Modify"), tr("No Selection")); + QKxMessageBox::information(this, tr("Modify"), tr("No Selection")); return; } QVariant group = idx.data(ROLE_GROUP); @@ -357,7 +357,7 @@ void QWoSessionManage::onImportReady() conf.refresh(); QList hosts = conf.hostList(); if(hosts.isEmpty()) { - QMessageBox::warning(this, tr("warning"), tr("it's not a ssh config format: %1").arg(fileName)); + QKxMessageBox::warning(this, tr("warning"), tr("it's not a ssh config format: %1").arg(fileName)); return; } QMap all = QWoIdentify::all(); @@ -373,12 +373,12 @@ void QWoSessionManage::onImportReady() } path = QDir::cleanPath(path); if(!QFile::exists(path)) { - QMessageBox::warning(this, tr("warning"), tr("failed to find the identify file list in config file for bad path: %1").arg(path)); + QKxMessageBox::warning(this, tr("warning"), tr("failed to find the identify file list in config file for bad path: %1").arg(path)); return; } IdentifyInfo info; if(!QWoIdentify::infomation(path, &info)) { - QMessageBox::warning(this, tr("warning"), tr("bad identify file: %1").arg(path)); + QKxMessageBox::warning(this, tr("warning"), tr("bad identify file: %1").arg(path)); return; } if(!all.contains(info.fingureprint)){ @@ -389,7 +389,7 @@ void QWoSessionManage::onImportReady() } if(!needsImport.isEmpty()) { QString items = needsImport.keys().join("\r\n"); - QMessageBox::warning(this, tr("warning"), tr("The config file contain the follow identify's files, Please export them before do this action.")+QString("%1").arg(items)); + QKxMessageBox::warning(this, tr("warning"), tr("The config file contain the follow identify's files, Please export them before do this action.")+QString("%1").arg(items)); return; } //qDebug() << his; @@ -424,7 +424,7 @@ void QWoSessionManage::onTreeViewOpenInSamePage() QItemSelectionModel *model = m_tree->selectionModel(); QModelIndexList idxs = model->selectedRows(); if(idxs.length() > 6) { - QMessageBox::information(this, tr("Info"), tr("can't open over 6 session in same page.")); + QKxMessageBox::information(this, tr("Info"), tr("can't open over 6 session in same page.")); return; } QStringList targets; @@ -434,7 +434,7 @@ void QWoSessionManage::onTreeViewOpenInSamePage() targets.append(name); } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets, true); @@ -452,7 +452,7 @@ void QWoSessionManage::onTreeViewOpenInDifferentPage() targets.append(name); } if(targets.isEmpty()){ - QMessageBox::warning(this, tr("Info"), tr("no selection")); + QKxMessageBox::warning(this, tr("Info"), tr("no selection")); return; } emit readyToConnect(targets, false); diff --git a/woterm/qwosessionproperty.cpp b/woterm/qwosessionproperty.cpp index 20e73ab..67d7dfc 100644 --- a/woterm/qwosessionproperty.cpp +++ b/woterm/qwosessionproperty.cpp @@ -22,11 +22,11 @@ #include "qwogroupinputdialog.h" #include "qkxbuttonassist.h" #include "qkxver.h" +#include "qkxmessagebox.h" #include #include #include -#include #include #include #include @@ -439,10 +439,19 @@ void QWoSessionProperty::onAssistButtonClicked(int idx) if(mode == QLineEdit::Normal) { ui->password->setEchoMode(QLineEdit::Password); }else{ + QString pwdAdmin = QWoSetting::adminPassword(); + if(pwdAdmin.isEmpty()) { + QKxMessageBox::information(this, tr("Administrator"), tr("Please create administrator's password first!")); + return; + } QString pass = QWoUtils::getPassword(this, tr("Please input the administrator password")); if(pass.isEmpty()) { return; } + if(pass != QWoSetting::adminPassword()) { + QKxMessageBox::information(this, tr("Error"), tr("Password error!")); + return; + } ui->password->setEchoMode(QLineEdit::Normal); QTimer::singleShot(1000*5, this, SLOT(onSetEditToPasswordMode())); } @@ -501,7 +510,7 @@ void QWoSessionProperty::onGroupAddCliecked() QPointer dlgPtr(&dlg); QObject::connect(&dlg, &QWoGroupInputDialog::apply, this, [=](const QString& name, int order){ if(names.contains(name)) { - QMessageBox::information(dlgPtr, tr("Parameter error"), tr("The group name is already exist.")); + QKxMessageBox::information(dlgPtr, tr("Parameter error"), tr("The group name is already exist.")); return; } QStringList mynames = names; @@ -534,7 +543,7 @@ bool QWoSessionProperty::saveConfig() }else{ hi.identityFile = ui->identify->text(); if(hi.identityFile.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The identity file can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The identity file can't be empty")); return false; } } @@ -542,11 +551,11 @@ bool QWoSessionProperty::saveConfig() hi.memo = ui->memo->toPlainText(); hi.script = ui->command->text(); if(!hi.name.isEmpty() && hi.name == hi.proxyJump) { - QMessageBox::warning(this, tr("Info"), tr("ProxyJump can't be same with name, change to another one.")); + QKxMessageBox::warning(this, tr("Info"), tr("ProxyJump can't be same with name, change to another one.")); return false; } if(hi.user.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The userName can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The userName can't be empty")); return false; } }else if(type == "SftpOnly") { @@ -560,12 +569,12 @@ bool QWoSessionProperty::saveConfig() }else{ hi.identityFile = QWoUtils::nameToPath(ui->identify->text()); if(hi.identityFile.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The identify file can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The identify file can't be empty")); return false; } QString identify = QWoSetting::identityFilePath() + "/" + hi.identityFile; if(!QFileInfo::exists(identify)) { - QMessageBox::warning(this, tr("Info"), tr("failed to find the identify file")); + QKxMessageBox::warning(this, tr("Info"), tr("failed to find the identify file")); return false; } hi.identityFile.insert(0, "woterm:"); @@ -573,11 +582,11 @@ bool QWoSessionProperty::saveConfig() hi.proxyJump = ui->proxyJump->text(); hi.memo = ui->memo->toPlainText(); if(!hi.name.isEmpty() && hi.name == hi.proxyJump) { - QMessageBox::warning(this, tr("Info"), tr("ProxyJump can't be same with name, change to another one.")); + QKxMessageBox::warning(this, tr("Info"), tr("ProxyJump can't be same with name, change to another one.")); return false; } if(hi.user.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The userName can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The userName can't be empty")); return false; } }else if(type == "Telnet") { @@ -629,23 +638,23 @@ bool QWoSessionProperty::saveConfig() } } if(hi.name.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The name can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The name can't be empty")); return false; } if(hi.type != SerialPort) { if(hi.host.isEmpty()) { - QMessageBox::warning(this, tr("Info"), tr("The host can't be empty")); + QKxMessageBox::warning(this, tr("Info"), tr("The host can't be empty")); return false; } if(hi.port < 10 || hi.port > 65535) { - QMessageBox::warning(this, tr("Info"), tr("The port should be at [10,65535]")); + QKxMessageBox::warning(this, tr("Info"), tr("The port should be at [10,65535]")); return false; } } if(m_name != hi.name) { if(!m_name.isEmpty() && QWoHostListModel::instance()->exists(hi.name)) { - QMessageBox::warning(this, tr("Info"), tr("The session name had been used, Change to another name.")); + QKxMessageBox::warning(this, tr("Info"), tr("The session name had been used, Change to another name.")); return false; } } diff --git a/woterm/qwosetting.cpp b/woterm/qwosetting.cpp index 95cb390..9970049 100644 --- a/woterm/qwosetting.cpp +++ b/woterm/qwosetting.cpp @@ -10,11 +10,11 @@ *******************************************************************************************/ #include "qwosetting.h" +#include "qkxmessagebox.h" #include #include #include -#include #include #include #include @@ -130,6 +130,19 @@ QString QWoSetting::sftpTaskLogPath() return path; } +QString QWoSetting::tempPath() +{ + QString path; + path = QWoSetting::value("woterm/temp", "").toString(); + if(!QFile::exists(path)) { + path = QWoSetting::ensurePath("temp"); + path = QDir::cleanPath(path); + path = QDir::toNativeSeparators(path); + return path; + } + return path; +} + QString QWoSetting::lastJsLoadPath() { QString path; @@ -176,7 +189,7 @@ QMap QWoSetting::allLanguages() if(!type.isEmpty()){ langs.insert(type, path); }else{ - QMessageBox::warning(nullptr, "warning", QString("The language file has no name:%1").arg(path)); + QKxMessageBox::warning(nullptr, "warning", QString("The language file has no name:%1").arg(path)); } } return langs; diff --git a/woterm/qwosetting.h b/woterm/qwosetting.h index 3ce6fcc..b86a02b 100644 --- a/woterm/qwosetting.h +++ b/woterm/qwosetting.h @@ -40,6 +40,7 @@ class QWoSetting : public QKxSetting static QString sshServerDbPath(); static QString sftpTaskDbPath(); static QString sftpTaskLogPath(); + static QString tempPath(); static QString lastBackupPath(); static void setLastBackupPath(const QString& path); diff --git a/woterm/qwosftplocalmodel.cpp b/woterm/qwosftplocalmodel.cpp new file mode 100644 index 0000000..3031872 --- /dev/null +++ b/woterm/qwosftplocalmodel.cpp @@ -0,0 +1,220 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwosftplocalmodel.h" +#include "qkxutils.h" + +#include +#include +#include +#include +#include + +QWoSftpLocalModel::QWoSftpLocalModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_font = QGuiApplication::font(); + m_font.setFamily(QKxUtils::suggestFamily()); +} + +QFileInfo QWoSftpLocalModel::fileInfo(const QModelIndex &idx) const +{ + int row = idx.row(); + if(row < 0 || row >= m_fileInfos.length()) { + return QFileInfo(); + } + return m_fileInfos.at(row); +} + +QModelIndex QWoSftpLocalModel::mkdir(const QModelIndex &parent, const QString &name) +{ + return QModelIndex(); +} + +int QWoSftpLocalModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()){ + return 0; + } + return m_fileInfos.length(); +} + +QModelIndex QWoSftpLocalModel::sibling(int row, int column, const QModelIndex &idx) const +{ + return QAbstractListModel::sibling(row, column, idx); +} + +QVariant QWoSftpLocalModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + int col = index.column(); + if (row < 0 || row > m_fileInfos.length()) { + return QVariant(); + } + if(role == Qt::FontRole) { + return m_font; + } + QFileInfo fi = m_fileInfos.at(row); + if(role == ROLE_INDEX) { + return index.row(); + } + if(role == ROLE_FILEINFO) { + QVariant v; + v.setValue(fi); + return v; + } + if(role == Qt::DecorationRole) { + if(col == 0) { + return m_iconProvider.icon(fi); + } + return QVariant(); + }else if(role == Qt::SizeHintRole) { + QFontMetrics fm(m_font); + if(col == 0) { + QString name = fi.fileName(); + if(fi.isRoot()) { + name = fi.filePath(); + } + int w = fm.width(name) + 50; + return QSize(w, 24); + } + if(col == 1) { + int w = fm.width(QString::number(fi.size())) + 10; + return QSize(w, 24); + } + if(col == 2) { + int w = fm.width(fi.created().toString()) + 10; + return QSize(w, 24); + } + }else if(role == Qt::DisplayRole) { + if(col == 0) { + if(fi.isRoot()) { + return fi.filePath(); + } + return fi.fileName(); + }else if(col == 1) { + if(fi.isFile()) { + return fi.size(); + } + return QVariant(); + }else if(col == 2) { + return fi.created(); + } + return QVariant(); + }else if(role == Qt::SizeHintRole) { + return QSize(-1, 25); + } + return QVariant(); +} + +bool QWoSftpLocalModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return false; +} + +Qt::ItemFlags QWoSftpLocalModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +bool QWoSftpLocalModel::insertRows(int row, int count, const QModelIndex &parent) +{ + qDebug() << "insertRow"; + return false; +} + +bool QWoSftpLocalModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (count <= 0 || row < 0 || (row + count) > rowCount(parent)){ + return false; + } + + beginRemoveRows(QModelIndex(), row, row + count - 1); + + + endRemoveRows(); + + return true; +} + +void QWoSftpLocalModel::sort(int column, Qt::SortOrder order) +{ + return QAbstractListModel::sort(column, order); +} + +int QWoSftpLocalModel::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +Qt::DropActions QWoSftpLocalModel::supportedDropActions() const +{ + return QAbstractListModel::supportedDropActions(); +} + +QVariant QWoSftpLocalModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role != Qt::DisplayRole) { + return QVariant(); + } + if(section == 0) { + return tr("FileName"); + }else if(section == 1) { + return tr("Size"); + }else if(section == 2) { + return tr("Created Date"); + } + return QVariant(); +} + +void QWoSftpLocalModel::setPath(const QString &path) +{ + if(path.isEmpty()) { + setHome(); + }else{ + QDir d(path); + QFileInfoList fis = d.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot, QDir::DirsFirst|QDir::Name); + onFileListActive(fis); + m_path = path; + } + emit pathChanged(m_path); +} + +QString QWoSftpLocalModel::path() const +{ + return m_path; +} + +void QWoSftpLocalModel::reload() +{ + setPath(m_path); +} + +void QWoSftpLocalModel::onFileListActive(const QList &fis) +{ + beginResetModel(); + m_fileInfos = fis; + endResetModel(); +} + +void QWoSftpLocalModel::setHome() +{ + m_path.clear(); +#if defined(Q_OS_WIN) + QFileInfoList fis = QDir::drives(); + onFileListActive(fis); +#elif defined(Q_OS_UNIX) + QDir d = QDir::home(); + QFileInfoList fis = d.entryInfoList(); + onFileListActive(fis); +#endif + emit pathChanged(m_path); +} diff --git a/woterm/qwosftplocalmodel.h b/woterm/qwosftplocalmodel.h new file mode 100644 index 0000000..c2c1969 --- /dev/null +++ b/woterm/qwosftplocalmodel.h @@ -0,0 +1,60 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWOSFTPLOCALMODEL_H +#define QWOSFTPLOCALMODEL_H + +#include "qwoglobal.h" + +#include +#include +#include +#include +#include +#include + +class QWoSftpLocalModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit QWoSftpLocalModel(QObject *parent = nullptr); + QFileInfo fileInfo(const QModelIndex& idx) const; + QModelIndex mkdir(const QModelIndex &parent, const QString &name); + void setPath(const QString& path); + QString path() const; + void reload(); + + void setHome(); +signals: + void pathChanged(const QString& path); +protected: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QModelIndex sibling(int row, int column, const QModelIndex &idx) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + Qt::DropActions supportedDropActions() const override; + // header + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; +private slots: + void onFileListActive(const QList& fis); +private: + QString m_path; + QList m_fileInfos; + QFileIconProvider m_iconProvider; + QFont m_font; +}; + +#endif // QWOSFTPLOCALMODEL_H diff --git a/woterm/qwosftpqueuemodel.cpp b/woterm/qwosftpqueuemodel.cpp new file mode 100644 index 0000000..c4e415f --- /dev/null +++ b/woterm/qwosftpqueuemodel.cpp @@ -0,0 +1,277 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwosftpqueuemodel.h" + +#include +#include +#include +#include + +#define BUTTON_SPACE (40) + +QWoSftpQueueModel::QWoSftpQueueModel(QObject *parent) + : QAbstractListModel(parent) + , m_maxTextWidth(0) + , m_maxTextHeight(0) +{ + m_font = QGuiApplication::font(); + m_uploadIcon = QIcon(QPixmap(":/woterm/resource/skin/upload.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); + m_downIcon = QIcon(QPixmap(":/woterm/resource/skin/download.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); + m_dirIcon = QIcon(QPixmap(":/woterm/resource/skin/dirs.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); + m_fileIcon = QIcon(QPixmap(":/woterm/resource/skin/file.png").scaled(18, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); +} + +bool QWoSftpQueueModel::append(const TaskInfo &ti) +{ + auto it = std::find_if(m_tasks.begin(), m_tasks.end(), [=](const TaskInfo& t){ + return ti.local == t.local || ti.remote == t.remote; + }); + if(it != m_tasks.end()) { + return false; + } + beginResetModel(); + m_tasks.append(ti); + QFontMetrics fm(m_font); + int w = fm.width(tr("Local:") + ti.local); + int w2 = fm.width(tr("Remote:")+ti.remote); + if(w < w2) { + w = w2; + } + if(w > m_maxTextWidth) { + m_maxTextWidth = w; + } + TaskInfo& tmp = m_tasks.last(); + tmp.textWidth = w; + if(m_maxTextHeight < fm.height()){ + m_maxTextHeight = fm.height(); + } + endResetModel(); + return true; +} + +int QWoSftpQueueModel::maxColumnWidth() +{ + return m_maxTextWidth + BUTTON_SPACE; +} + +bool QWoSftpQueueModel::isEmpty() const +{ + return m_tasks.isEmpty(); +} + +TaskInfo QWoSftpQueueModel::takeFirst() +{ + beginResetModel(); + const TaskInfo& ti = m_tasks.takeFirst(); + QFontMetrics fm(m_font); + + int w = fm.width(tr("Local:") + ti.local); + int w2 = fm.width(tr("Remote:")+ti.remote); + if(w < w2) { + w = w2; + } + if(w == m_maxTextWidth) { + m_maxTextWidth = 0; + for(int i = 0; i < m_tasks.length(); i++) { + const TaskInfo& tmp = m_tasks.at(i); + if(tmp.textWidth > m_maxTextWidth) { + m_maxTextWidth = tmp.textWidth; + } + } + if(m_maxTextWidth == 0) { + m_maxTextWidth = 100; + } + } + endResetModel(); + return ti; +} + +void QWoSftpQueueModel::removeTask(int tid) +{ + beginResetModel(); + QFontMetrics fm(m_font); + for(auto it = m_tasks.begin(); it != m_tasks.end(); it++) { + const TaskInfo& tmp = *it; + if(tmp.taskId == tid) { + m_tasks.erase(it); + break; + } + } + m_maxTextWidth = 0; + for(int i = 0; i < m_tasks.length(); i++) { + const TaskInfo& tmp = m_tasks.at(i); + if(tmp.textWidth > m_maxTextWidth) { + m_maxTextWidth = tmp.textWidth; + } + } + if(m_maxTextWidth == 0) { + m_maxTextWidth = 100; + } + endResetModel(); +} + +void QWoSftpQueueModel::removeAll() +{ + beginResetModel(); + m_tasks.clear(); + endResetModel(); +} + +int QWoSftpQueueModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()){ + return 0; + } + return m_tasks.count(); +} + +QVariant QWoSftpQueueModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role == Qt::DisplayRole) { + if(section == 0) { + return tr("Type"); + }else if(section == 1) { + return tr("FileName"); + } + } + return QAbstractListModel::headerData(section, orientation, role); +} + +QVariant QWoSftpQueueModel::data(const QModelIndex &idx, int role) const +{ + //qDebug() << "HostListModel" << role; + if (idx.row() < 0 || idx.row() >= m_tasks.size()) { + return QVariant(); + } + if (!idx.isValid()){ + return QVariant(); + } + const TaskInfo& ti = m_tasks.at(idx.row()); + + if(role == Qt::FontRole) { + return m_font; + } + + if(role == Qt::ToolTipRole) { + QString label = tr("Local:") + ti.local+"\n"+tr("Remote:")+ti.remote; + return label; + } + + if(role == Qt::SizeHintRole) { + QFontMetrics fm(m_font); + int column = idx.column(); + if(column == 0) { + return QSize(40, 24); + } + if(column == 1) { + return QSize(m_maxTextWidth + BUTTON_SPACE, m_maxTextHeight * 3); + } + return QSize(-1, 24); + } + + if(role == ROLE_INDEX) { + return idx.row(); + } + + if(role == ROLE_TASKINFO) { + QVariant v; + v.setValue(ti); + return v; + } + + if (role == Qt::DisplayRole) { + if(idx.column() == 0) { + return QVariant(); + } + if(idx.column() == 1) { + QString label = tr("Local:") + ti.local+"\n"+tr("Remote:")+ti.remote; + return label; + } + return QVariant(); + } + return QVariant(); +} + +bool QWoSftpQueueModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return false; +} + +Qt::ItemFlags QWoSftpQueueModel::flags(const QModelIndex &idx) const +{ + if (!idx.isValid()){ + return QAbstractListModel::flags(idx) | Qt::ItemIsDropEnabled; + } + return QAbstractListModel::flags(idx)| Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +bool QWoSftpQueueModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (count < 1 || row < 0 || row > rowCount(parent)){ + return false; + } + + beginInsertRows(QModelIndex(), row, row + count - 1); + + for (int r = 0; r < count; ++r){ + m_tasks.insert(row, TaskInfo()); + } + + endInsertRows(); + + return QAbstractListModel::insertRows(row, count, parent); +} + +bool QWoSftpQueueModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (count <= 0 || row < 0 || (row + count) > rowCount(parent)){ + return false; + } + + beginRemoveRows(QModelIndex(), row, row + count - 1); + + const auto it = m_tasks.begin() + row; + m_tasks.erase(it, it + count); + + endRemoveRows(); + + return true; +} + +void QWoSftpQueueModel::sort(int column, Qt::SortOrder order) +{ + return QAbstractListModel::sort(column, order); +} + +int QWoSftpQueueModel::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +Qt::DropActions QWoSftpQueueModel::supportedDropActions() const +{ + return QAbstractListModel::supportedDropActions(); +} + +void QWoSftpQueueModel::test() +{ + QString path = QCoreApplication::applicationDirPath(); + for(int i = 0; i < 10; i++) { + TaskInfo ti; + ti.isDir = i % 2; + ti.isDown = i % 2; + ti.local = path + QString("/local-%1").arg(i); + ti.remote = path + QString("/remote-%1").arg(i); + ti.taskId = 100 + i; + append(ti); + } +} diff --git a/woterm/qwosftpqueuemodel.h b/woterm/qwosftpqueuemodel.h new file mode 100644 index 0000000..f969b5c --- /dev/null +++ b/woterm/qwosftpqueuemodel.h @@ -0,0 +1,65 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWOSFTPQUEUEMODEL_H +#define QWOSFTPQUEUEMODEL_H + +#include +#include +#include +#include + +#include "qwoglobal.h" + +class QWoSftpQueueModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit QWoSftpQueueModel(QObject *parent = nullptr); + bool append(const TaskInfo& ti); + int maxColumnWidth(); + bool isEmpty() const; + TaskInfo takeFirst(); + void removeTask(int tid); + void removeAll(); +private: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + Qt::DropActions supportedDropActions() const override; +private: + Q_INVOKABLE void test(); +private: + Q_DISABLE_COPY(QWoSftpQueueModel) + QList m_tasks; + QIcon m_dirIcon; + QIcon m_fileIcon; + QIcon m_uploadIcon; + QIcon m_downIcon; + QFont m_font; + int m_maxTextWidth; + int m_maxTextHeight; +}; + +#endif // QWOSFTPQUEUEMODEL_H diff --git a/woterm/qwosftpremotemodel.cpp b/woterm/qwosftpremotemodel.cpp new file mode 100644 index 0000000..044d78d --- /dev/null +++ b/woterm/qwosftpremotemodel.cpp @@ -0,0 +1,316 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwosftpremotemodel.h" + +#include +#include +#include +#include +#include +#include +#include + +QWoSftpRemoteModel::QWoSftpRemoteModel(QObject *parent) + : QAbstractListModel (parent) +{ + m_font = QGuiApplication::font(); + m_dirIcon = QIcon(QPixmap(":/woterm/resource/skin/folder2.png").scaled(24, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); + m_fileIcon = QIcon(QPixmap(":/woterm/resource/skin/file.png").scaled(24, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); + m_linkIcon = QIcon(QPixmap(":/woterm/resource/skin/link.png").scaled(24, 24, Qt::KeepAspectRatio ,Qt::SmoothTransformation)); +} + +QWoSftpRemoteModel::~QWoSftpRemoteModel() +{ + +} + +QString QWoSftpRemoteModel::path() const +{ + return m_path; +} + +bool QWoSftpRemoteModel::exist(const QString &fileName) +{ + auto it = std::find_if(m_fileInfos.begin(), m_fileInfos.end(), [=](const FileInfo& fi) { + return fi.name == fileName; + }); + return it != m_fileInfos.end(); +} + +int QWoSftpRemoteModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()){ + return 0; + } + return m_fileInfos.count(); +} + +QVariant QWoSftpRemoteModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role == Qt::DisplayRole) { + switch (section) { + case 0: + return tr("Name"); + case 1: + return tr("Permission"); + case 2: + return tr("Owner"); + case 3: + return tr("Group"); + case 4: + return tr("Size"); + case 5: + return tr("Date"); + } + } + return QAbstractListModel::headerData(section, orientation, role); +} + +QVariant QWoSftpRemoteModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_fileInfos.size()) { + return QVariant(); + } + if (!index.isValid()){ + return QVariant(); + } + if(role == Qt::FontRole) { + return m_font; + } + const FileInfo& fi = m_fileInfos.at(index.row()); + int column = index.column(); + + if(role == ROLE_INDEX) { + return index.row(); + } + + if(role == ROLE_FILEINFO) { + QVariant v; + v.setValue(fi); + return v; + } + if(role == ROLE_REFILTER) { + QVariant v; + v.setValue(QString("%1").arg(fi.name)); + return v; + } + if(role == ROLE_FRIENDLY_NAME) { + QVariant v; + v.setValue(fi.name); + return v; + } + if(role == Qt::SizeHintRole) { + QFontMetrics fm(m_font); + if(column == 0) { + int w = fm.width(fi.name) + 50; + return QSize(w, 24); + } + if(column == 1) { + int w = fm.width(fi.permission) + 10; + return QSize(w, 24); + } + if(column == 2) { + int w = fm.width(fi.owner) + 10; + return QSize(w, 24); + } + if(column == 3) { + int w = fm.width(fi.group) + 10; + return QSize(w, 24); + } + if(column == 4) { + int w = fm.width(fi.size) + 10; + return QSize(w, 24); + } + if(column == 5) { + int w = fm.width(fi.date) + 10; + return QSize(w, 24); + } + } + + if(column == 0) { + if(role == Qt::DecorationRole) { + if(fi.type == "d") { + return m_dirIcon; + }else if(fi.type == "l") { + return m_linkIcon; + } + return m_fileIcon; + }else if(role == Qt::DisplayRole) { + return fi.name; + } + return QVariant(); + } + if(role != Qt::DisplayRole) { + return QVariant(); + } + if(column == 1) { + return fi.permission; + } + if(column == 2) { + return fi.owner; + } + if(column == 3) { + return fi.group; + } + if(column == 4) { + return fi.size; + } + if(column == 5) { + return fi.date; + } + return QVariant(); +} + + + +bool QWoSftpRemoteModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return false; +} + +Qt::ItemFlags QWoSftpRemoteModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()){ + return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled; + } + return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +bool QWoSftpRemoteModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (count < 1 || row < 0 || row > rowCount(parent)){ + return false; + } + + beginInsertRows(QModelIndex(), row, row + count - 1); + +// for (int r = 0; r < count; ++r){ +// m_hosts.insert(row, HostInfo()); +// } + + endInsertRows(); + + return QAbstractListModel::insertRows(row, count, parent); +} + +bool QWoSftpRemoteModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (count <= 0 || row < 0 || (row + count) > rowCount(parent)){ + return false; + } + + beginRemoveRows(QModelIndex(), row, row + count - 1); + +// const auto it = m_hosts.begin() + row; +// m_hosts.erase(it, it + count); + + endRemoveRows(); + + return true; +} + +static bool ascendingLessThan(const QPair &s1, const QPair &s2) +{ + return s1.first < s2.first; +} + +static bool decendingLessThan(const QPair &s1, const QPair &s2) +{ + return s1.first > s2.first; +} + +void QWoSftpRemoteModel::sort(int column, Qt::SortOrder order) +{ +#if 1 + return QAbstractListModel::sort(column, order); +#else + emit layoutAboutToBeChanged(QList(), VerticalSortHint); + + QVector > list; + const int lstCount = m_hosts.count(); + list.reserve(lstCount); + for (int i = 0; i < lstCount; ++i){ + list.append(QPair(m_hosts.at(i), i)); + } + + if (order == Qt::AscendingOrder) + std::sort(list.begin(), list.end(), ascendingLessThan); + else + std::sort(list.begin(), list.end(), decendingLessThan); + + m_hosts.clear(); + QVector forwarding(lstCount); + for (int i = 0; i < lstCount; ++i) { + m_hosts.append(list.at(i).first); + forwarding[list.at(i).second] = i; + } + + QModelIndexList oldList = persistentIndexList(); + QModelIndexList newList; + const int numOldIndexes = oldList.count(); + newList.reserve(numOldIndexes); + for (int i = 0; i < numOldIndexes; ++i) + newList.append(index(forwarding.at(oldList.at(i).row()), 0)); + changePersistentIndexList(oldList, newList); + + emit layoutChanged(QList(), VerticalSortHint); +#endif +} + +int QWoSftpRemoteModel::columnCount(const QModelIndex &parent) const +{ + return 6; +} + +Qt::DropActions QWoSftpRemoteModel::supportedDropActions() const +{ + return QAbstractListModel::supportedDropActions(); +} + +static bool lessThan(const FileInfo &s1, const FileInfo &s2) +{ + static QString type = "dl-"; + int t1 = type.indexOf(s1.type); + int t2 = type.indexOf(s2.type); + if(t1 < t2) { + return true; + } + if(t1 > t2) { + return false; + } + return s1.name < s2.name; +} + +void QWoSftpRemoteModel::onDirOpen(const QString &path, const QList &v, const QVariantMap& userData) +{ + beginResetModel(); + m_path = path; + m_fileInfos.clear(); + for(int i = 0; i < v.length(); i++) { + QMap mdata = v.at(i).toMap(); + FileInfo fi; + fi.longName = mdata.value("longName").toString(); + fi.label = mdata.value("label").toString(); + fi.name = mdata.value("name").toString(); + fi.type = mdata.value("type").toString(); + fi.owner = mdata.value("owner").toString(); + fi.group = mdata.value("group").toString(); + fi.size = mdata.value("size").toString(); + fi.date = mdata.value("date").toString(); + fi.permission = mdata.value("permission").toString(); + m_fileInfos.append(fi); + } + std::sort(m_fileInfos.begin(), m_fileInfos.end(), lessThan); + endResetModel(); + + emit pathChanged(m_path); +} diff --git a/woterm/qwosftpremotemodel.h b/woterm/qwosftpremotemodel.h new file mode 100644 index 0000000..e7c40b7 --- /dev/null +++ b/woterm/qwosftpremotemodel.h @@ -0,0 +1,62 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#pragma once + +#include "qwoglobal.h" + +#include +#include +#include +#include + +class QQuickItem; + +class QWoSftpRemoteModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit QWoSftpRemoteModel(QObject *parent = nullptr); + virtual ~QWoSftpRemoteModel(); +public: + QString path() const; + bool exist(const QString& fileName); +signals: + void pathChanged(const QString& path); +private slots: + void onDirOpen(const QString& path, const QList& data, const QVariantMap& userData); +private: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + Qt::DropActions supportedDropActions() const override; +private: + Q_DISABLE_COPY(QWoSftpRemoteModel) + QList m_fileInfos; + QString m_path; + QIcon m_dirIcon; + QIcon m_fileIcon; + QIcon m_linkIcon; + QFont m_font; +}; diff --git a/woterm/qwosftptask.cpp b/woterm/qwosftptask.cpp new file mode 100644 index 0000000..d1800a7 --- /dev/null +++ b/woterm/qwosftptask.cpp @@ -0,0 +1,116 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qwosftptask.h" +#include "qwosetting.h" + +#include +#include +#include + +static bool createTasksTable(SQLite::Database& db) { + QString sql="CREATE TABLE IF NOT EXISTS tasks("; + sql += "id INTEGER PRIMARY KEY AUTOINCREMENT,"; + sql += "name VARCHAR(100) NOT NULL,"; + sql += "local VARCHAR(1024) NOT NULL,"; + sql += "remote VARCHAR(1024) NOT NULL,"; + sql += "isDir INT DEFAULT (0),"; + sql += "isDown INT DEFAULT (0),"; + sql += "state INT DEFAULT (0),"; + sql += "ct DATETIME NOT NULL,"; + sql += "ut DATETIME NOT NULL"; + sql +=")"; + db.exec(sql.toUtf8()); + if(db.tableExists("tasks")) { + return true; + } + return false; +} + +static bool createErrorsTable(SQLite::Database& db) { + QString sql="CREATE TABLE IF NOT EXISTS errors("; + sql += "id INTEGER PRIMARY KEY AUTOINCREMENT,"; + sql += "tid INT NOT NULL,"; + sql += "desc TEXT NOT NULL,"; + sql += "ct DATETIME NOT NULL"; + sql +=")"; + db.exec(sql.toUtf8()); + if(db.tableExists("errors")) { + return true; + } + return false; +} + +QWoSftpTask::QWoSftpTask(QObject *parent) + : QObject(parent) + , m_bInit(false) +{ + m_dbFile = QWoSetting::sftpTaskDbPath(); + init(); +} + +int QWoSftpTask::addTask(const QString &name, const QString &pathLocal, const QString &pathRemote, bool isDir, bool isDown) +{ + int tid = 0; + return tid; +} + +void QWoSftpTask::addError(int tid, const QString &desc) +{ + +} + +void QWoSftpTask::init() +{ + if(m_bInit) { + return; + } + if(QFile::exists(m_dbFile)) { + if(!databaseValid(m_dbFile)) { + // bad db file. + QFile::remove(m_dbFile); + init(); + }else{ + m_bInit = true; + return; + } + }else{ + try{ + SQLite::Database db(m_dbFile.toUtf8(), SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); + createTasksTable(db); + createErrorsTable(db); + }catch(...) { + qWarning() << "failed to create database" << m_dbFile; + } + } + m_bInit = databaseValid(m_dbFile); +} + +bool QWoSftpTask::databaseValid(const QString &dbFile) +{ + try { + SQLite::Database db(dbFile.toUtf8(), SQLite::OPEN_READONLY); + SQLite::Statement health(db, "PRAGMA quick_check"); + if(health.executeStep()) { + QByteArrayList dbset = {"tasks", "errors"}; + for(auto it = dbset.begin(); it != dbset.end(); it++) { + QByteArray name = *it; + if(!db.tableExists(name)) { + return false; + } + } + return true; + } + } catch (...) { + qWarning() << "databaseValid" << dbFile; + } + return false; +} diff --git a/woterm/qwosftptask.h b/woterm/qwosftptask.h new file mode 100644 index 0000000..cf2f059 --- /dev/null +++ b/woterm/qwosftptask.h @@ -0,0 +1,38 @@ +/******************************************************************************************* +* +* Copyright (C) 2022 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QWOSFTPTASK_H +#define QWOSFTPTASK_H + +#include +#include + +class QWoSftpTask : public QObject +{ + Q_OBJECT +public: + explicit QWoSftpTask(QObject *parent = nullptr); + + /* return tid */ + int addTask(const QString& name, const QString& pathLocal, const QString& pathRemote, bool isDir, bool isDown); + void addError(int tid, const QString& desc); +private: + void init(); +private: + static bool databaseValid(const QString &dbFile); +signals: + +private: + QString m_dbFile; + bool m_bInit; +}; + +#endif // QWOSFTPTASK_H diff --git a/woterm/qwosftpwidget.cpp b/woterm/qwosftpwidget.cpp index 7d3d4d9..8d11809 100644 --- a/woterm/qwosftpwidget.cpp +++ b/woterm/qwosftpwidget.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -687,7 +686,7 @@ void QWoSftpWidget::onLocalMenuUpload() if(typeTransfer == QMessageBox::Yes || typeTransfer == QMessageBox::No){ QMessageBox::StandardButtons buttonType = QMessageBox::Yes|QMessageBox::No; - if(m_isUltimate) { + if(m_isUltimate && lsfi.length() > 1) { buttonType |= QMessageBox::YesToAll|QMessageBox::NoToAll; } typeTransfer = QKxMessageBox::warning(this, tr("FileExist"), @@ -838,7 +837,7 @@ void QWoSftpWidget::onRemoteMenuDownload() if(typeTransfer == QMessageBox::Yes || typeTransfer == QMessageBox::No){ QMessageBox::StandardButtons buttonType = QMessageBox::Yes|QMessageBox::No; - if(m_isUltimate) { + if(m_isUltimate && lsfi.length() > 1) { buttonType |= QMessageBox::YesToAll|QMessageBox::NoToAll; } typeTransfer = QKxMessageBox::warning(this, tr("FileExist"), diff --git a/woterm/qwosftpwidgetimpl.cpp b/woterm/qwosftpwidgetimpl.cpp index befd4a7..cab19f6 100644 --- a/woterm/qwosftpwidgetimpl.cpp +++ b/woterm/qwosftpwidgetimpl.cpp @@ -17,10 +17,10 @@ #include "qwomainwindow.h" #include "qwoevent.h" #include "qwocommandlineinput.h" +#include "qkxmessagebox.h" #include #include -#include #include #include #include diff --git a/woterm/qwoshower.cpp b/woterm/qwoshower.cpp index e98362a..8345a63 100644 --- a/woterm/qwoshower.cpp +++ b/woterm/qwoshower.cpp @@ -24,11 +24,11 @@ #include "qwoutils.h" #include "qwosshconf.h" #include "qwofloatwindow.h" +#include "qkxmessagebox.h" #include "qkxver.h" #include #include -#include #include #include #include @@ -133,7 +133,7 @@ bool QWoShower::openRLogin(const QString &target) emit floatChanged(impl, false); return true; } - QMessageBox::StandardButton btn = QMessageBox::warning(this, "Info", "RLogin must be run with root permission, try to run to now?", QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, "Info", "RLogin must be run with root permission, try to run to now?", QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return false; } @@ -174,7 +174,7 @@ bool QWoShower::openSerial(const QString &target) emit floatChanged(impl, false); return true; } - QMessageBox::StandardButton btn = QMessageBox::warning(this, "Info", "SerialPort must be run with root permission, try to run to now?", QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, "Info", "SerialPort must be run with root permission, try to run to now?", QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return false; } @@ -298,7 +298,7 @@ void QWoShower::closeSession(int idx) } msg.append(tr("\r\n\r\nContinue To Close It?")); } - QMessageBox::StandardButton btn = QMessageBox::warning(this, tr("CloseSession"), msg, QMessageBox::Ok|QMessageBox::No); + QMessageBox::StandardButton btn = QKxMessageBox::warning(this, tr("CloseSession"), msg, QMessageBox::Ok|QMessageBox::No); if(btn == QMessageBox::No) { return ; } @@ -389,7 +389,7 @@ void QWoShower::onCloseThisTabSession() QVariant vimpl = m_tabMenu->property(TAB_TARGET_IMPL); QWidget *impl = vimpl.value(); if(impl == nullptr) { - QMessageBox::warning(this, tr("alert"), tr("failed to find impl infomation")); + QKxMessageBox::warning(this, tr("alert"), tr("failed to find impl infomation")); return; } impl->deleteLater(); @@ -403,7 +403,7 @@ void QWoShower::onCloseOtherTabSession() QVariant vimpl = m_tabMenu->property(TAB_TARGET_IMPL); QWidget *impl = vimpl.value(); if(impl == nullptr) { - QMessageBox::warning(this, tr("alert"), tr("failed to find impl infomation")); + QKxMessageBox::warning(this, tr("alert"), tr("failed to find impl infomation")); return; } for(int i = 0; i < m_tab->count(); i++) { diff --git a/woterm/qwossh.cpp b/woterm/qwossh.cpp index 74e8d6e..4d9c081 100644 --- a/woterm/qwossh.cpp +++ b/woterm/qwossh.cpp @@ -62,19 +62,6 @@ static ulong LOCAL_IP = inet_addr("127.0.0.1"); -#define MT_FTP_OPENDIR (10) -#define MT_FTP_MKDIR (11) -#define MT_FTP_RMDIR (12) -#define MT_FTP_UNLINK (13) -#define MT_FTP_DOWNLOAD (14) -#define MT_FTP_UPLOAD (15) -#define MT_FTP_UPLOADNEXT (16) -#define MT_FTP_ABORT (17) -#define MT_FTP_LISTFILE (18) -#define MT_FTP_LISTFILENEXT (19) -#define MT_FTP_REMOVEFILENEXT (20) - - class QSshInteractiveClient : public QSshAuthClient { public: @@ -304,10 +291,15 @@ class QWoPowerSftp : public QWoSshFtp return reasonResult(err, user); } + QVariantMap fileInfoResult(int err, const QVariantMap& user) { + return reasonResult(err, user); + } + QVariantMap unlinkResult(int err, const QVariantMap& user) { return reasonResult(err, user); } + QVariantMap mkDirResult(int err, const QVariantMap& user) { QVariantMap dm = user; if(err == -999) { @@ -328,6 +320,10 @@ class QWoPowerSftp : public QWoSshFtp return dm; } + QVariantMap mkPathResult(int err, const QVariantMap& user) { + return mkDirResult(err, user); + } + QVariantMap rmDirResult(int err, const QVariantMap& user) { return reasonResult(err, user); } @@ -335,9 +331,11 @@ class QWoPowerSftp : public QWoSshFtp QVariantMap reasonResult(int err, const QVariantMap& user) { QVariantMap dm = user; if(err == -999) { + dm.insert("code", -999); dm.insert("reason", "abort"); }else if(err < 0) { int code = sftpLastError(dm); + dm.insert("code", code); if(code == 0 || code == SSH_FX_NO_CONNECTION || code == SSH_FX_CONNECTION_LOST) { // net broken, will has code == 0. so add it. dm.insert("reason", "fatal"); @@ -345,6 +343,7 @@ class QWoPowerSftp : public QWoSshFtp dm.insert("reason", "error"); } }else{ + dm.insert("code", 0); dm.insert("reason", "ok"); } return dm; @@ -611,6 +610,99 @@ class QWoPowerSftp : public QWoSshFtp return 0; } + int runFileContent(const QByteArray& remote, qint64 offset, qint64 maxSize, const QVariantMap& user) { + m_abort = false; + m_userData = user; + m_errorString.clear(); + QVariantMap dm = user; + handleCommandStart(MT_FTP_READ_FILE_CONTENT, user); + int code = _runFileContent(remote, offset, maxSize, dm); + if(code <= 0) { + handleCommandFinish(MT_FTP_READ_FILE_CONTENT, reasonResult(code, dm)); + } + return code; + } + + int _runFileContent(const QByteArray& _path, qint64 offset, qint64 maxSize, QVariantMap& user) { + QByteArray path = _path; + if(path.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + path = path.mid(1); + path.insert(0, tmp); + ssh_string_free_char(tmp); + } + sftp_attributes attr = sftp_stat(m_sftp, path); + if(attr == nullptr) { + return -2; + } + qint64 total = attr->size; + sftp_attributes_free(attr); + sftp_file rf = sftp_open(m_sftp, path, O_RDONLY, 0); + if(rf == nullptr) { + return -4; + } + if(offset > total || offset < 0) { + sftp_close(rf); + return -5; + } + if(sftp_seek64(rf, offset) < 0) { + sftp_close(rf); + return -8; + } + QByteArray buf; + buf.resize(maxSize); + int n = sftp_read(rf, buf.data(), maxSize); + if(n > 0) { + buf.resize(n); + } + sftp_close(rf); + QVariantMap file; + file.insert("content", buf); + file.insert("fileSize", total); + file.insert("offset", offset); + user.insert("fileContent", file); + return 0; + } + + int runWriteFileContent(const QByteArray& remote, const QByteArray& content, const QVariantMap& user) { + m_abort = false; + m_userData = user; + m_errorString.clear(); + handleCommandStart(MT_FTP_WRITE_FILE_CONTENT, user); + int code = _runWriteFileContent(remote, content, user); + if(code <= 0) { + handleCommandFinish(MT_FTP_WRITE_FILE_CONTENT, reasonResult(code, user)); + } + return code; + } + + int _runWriteFileContent(const QByteArray& _path, const QByteArray& content, const QVariantMap& user) { + QByteArray path = _path; + if(path.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + path = path.mid(1); + path.insert(0, tmp); + ssh_string_free_char(tmp); + } + int access_type = O_WRONLY | O_CREAT | O_TRUNC; + sftp_file wf = sftp_open(m_sftp, path, access_type, 0600); + if(wf == nullptr) { + return -1; + } + if(sftp_write(wf, content.data(), content.length()) != content.length()) { + sftp_close(wf); + return -2; + } + sftp_close(wf); + return 0; + } + int runDownload(const QByteArray& remote, const QByteArray& local, int policy, const QVariantMap& user) { m_abort = false; m_userData = user; @@ -626,12 +718,22 @@ class QWoPowerSftp : public QWoSshFtp return code; } - int _runDownload(const QByteArray& remote, const QByteArray& local, int policy, const QVariantMap& user) { + int _runDownload(const QByteArray& _remote, const QByteArray& local, int policy, const QVariantMap& user) { + QByteArray remote = _remote; + if(remote.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + remote = remote.mid(1); + remote.insert(0, tmp); + ssh_string_free_char(tmp); + } sftp_attributes attr = sftp_stat(m_sftp, remote); if(attr == nullptr) { return -2; } - int total = attr->size; + qint64 total = attr->size; sftp_attributes_free(attr); sftp_file rf = sftp_open(m_sftp, remote, O_RDONLY, 0); if(rf == nullptr) { @@ -722,9 +824,19 @@ class QWoPowerSftp : public QWoSshFtp return code; } - int _runUpload(const QByteArray& local, const QByteArray& remote, int policy, const QVariantMap& user) { + int _runUpload(const QByteArray& local, const QByteArray& _remote, int policy, const QVariantMap& user) { + QByteArray remote = _remote; + if(remote.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + remote = remote.mid(1); + remote.insert(0, tmp); + ssh_string_free_char(tmp); + } int access_type = O_WRONLY | O_CREAT | O_TRUNC; - int fileOffset = 0; + qint64 fileOffset = 0; sftp_attributes attr = sftp_stat(m_sftp, remote); if(attr == nullptr) { // file not exist. @@ -797,6 +909,134 @@ class QWoPowerSftp : public QWoSshFtp return uploadNext(user) ? 1 : 0; } + int runFileInfo(const QByteArray& path, const QVariantMap& user) { + m_abort = false; + m_userData = user; + m_errorString.clear(); + handleCommandStart(MT_FTP_FILE_INFO, user); + QVariantMap result = user; + int code = _runFileInfo(path, result); + handleCommandFinish(MT_FTP_FILE_INFO, fileInfoResult(code, result)); + return code; + } + + int _runFileInfo(const QByteArray& _path, QVariantMap& user) { + QByteArray path = _path; + if(path.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + path = path.mid(1); + path.insert(0, tmp); + ssh_string_free_char(tmp); + } + sftp_attributes attr = sftp_stat(m_sftp, path); + if(attr == nullptr) { + return -1; + } + int ownerMax = 0, groupMax = 0, sizeMax = 0, dateMax = 0, nameMax = 0; + QList item; + { + QByteArray longName(attr->longname); + QByteArray type = QWoUtils::filePermissionToText(attr->type, attr->permissions); + QByteArray owner(attr->owner); + QByteArray group(attr->group); + QByteArray size(QByteArray::number(qint64(attr->size))); + qint64 mtime = qMax(qint64(attr->mtime), qint64(attr->mtime64)); + if(mtime < 10) { + mtime = qMax(qint64(attr->atime), qint64(attr->atime64)); + } + QDateTime dt = QDateTime::fromMSecsSinceEpoch(mtime * 1000); + QByteArray date = dt.toString("dd.MM.yyyy hh:mm:ss").toLatin1(); + QByteArray name(attr->name); + if(ownerMax < owner.length()) { + ownerMax = owner.length(); + } + if(groupMax < group.length()) { + groupMax = group.length(); + } + if(sizeMax < size.length()) { + sizeMax = size.length(); + } + if(dateMax < date.length()) { + dateMax = date.length(); + } + if(nameMax < name.length()) { + nameMax = name.length(); + } + item.append(longName); + item.append(type); + item.append(owner); + item.append(group); + item.append(size); + item.append(date); + item.append(name); + } + sftp_attributes_free(attr); + QVariantMap mdata; + { + QByteArray longName = item.at(0); + mdata.insert("longName", item.at(0)); + QString label = item.at(1); + mdata.insert("type", QString("%1").arg(label.at(0))); + mdata.insert("permission", label); + { + QByteArray owner = item.at(2); + mdata.insert("owner", owner); + int nleft = (ownerMax + 7) / 8 * 8 + 1 - owner.length(); + for(int i = 0; i < nleft; i++) { + owner.prepend(QChar::Space); + } + label.append(owner); + } + { + QByteArray group = item.at(3); + mdata.insert("group", group); + int nleft = (groupMax + 7) / 8 * 8 + 1 - group.length(); + for(int i = 0; i < nleft; i++) { + group.prepend(QChar::Space); + } + label.append(group); + } + { + QByteArray size = item.at(4); + mdata.insert("size", size); + int nleft = (sizeMax + 7) / 8 * 8 + 1 - size.length(); + for(int i = 0; i < nleft; i++) { + size.prepend(QChar::Space); + } + label.append(size); + } + { + QByteArray date = item.at(5); + mdata.insert("date", date); + int nleft = (dateMax + 7) / 8 * 8 + 1 - date.length(); + for(int i = 0; i < nleft; i++) { + date.prepend(QChar::Space); + } + label.append(date); + } + { + QByteArray name = item.at(6); + mdata.insert("name", name); + int nleft = (nameMax + 7) / 8 * 8 + 1 - name.length(); + for(int i = 0; i < nleft; i++) { + name.prepend(QChar::Space); + } + label.append(name); + } + } + char *tmp = sftp_canonicalize_path(m_sftp, path); + if(tmp == nullptr) { + return -2; + } + mdata.insert("absPath", QByteArray(tmp)); + user.insert("fileInfo", mdata); + ssh_string_free_char(tmp); + return 0; + } + int runUnlink(const QByteArray& path, const QVariantMap& user) { m_abort = false; m_userData = user; @@ -807,9 +1047,19 @@ class QWoPowerSftp : public QWoSshFtp return code; } - int _runUnlink(const QByteArray& path, const QVariantMap& user) { + int _runUnlink(const QByteArray& _path, const QVariantMap& user) { + QByteArray path = _path; + if(path.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + path = path.mid(1); + path.insert(0, tmp); + ssh_string_free_char(tmp); + } if(sftp_unlink(m_sftp, path) != SSH_OK) { - return -1; + return -2; } return 0; } @@ -900,6 +1150,50 @@ class QWoPowerSftp : public QWoSshFtp return 0; } + int runMkPath(const QByteArray& path, int mode, const QVariantMap& user) { + m_abort = false; + m_userData = user; + m_errorString.clear(); + handleCommandStart(MT_FTP_MKPATH, user); + int code = _runMkPath(path, mode, user); + handleCommandFinish(MT_FTP_MKPATH, mkPathResult(code, user)); + return code; + } + + int _runMkPath(const QByteArray& _path, int mode, const QVariantMap& user) { + QByteArray path = _path; + if(path.startsWith('~')) { + char *tmp = sftp_canonicalize_path(m_sftp, "."); + if(tmp == nullptr) { + return -1; + } + path = path.mid(1); + path.insert(0, tmp); + ssh_string_free_char(tmp); + } + QByteArrayList names = path.split('/'); + if(names.isEmpty()) { + return 0; + } + QByteArray mkpath = names.takeFirst(); + for(int i = 0; i < names.length(); i++) { + QByteArray tmp = names.at(i); + if(tmp.isEmpty()) { + continue; + } + mkpath = mkpath + "/" + tmp; + if(sftp_mkdir(m_sftp, mkpath, mode) != SSH_OK) { + int err = sftp_get_error(m_sftp); + if(err == SSH_FX_FILE_ALREADY_EXISTS) { + continue; + } + return 1; + } + } + + return 0; + } + virtual bool handleOpen(void *session){ sftp_session sftp = sftp_new(ssh_session(session)); if (sftp == nullptr) { @@ -1033,6 +1327,13 @@ class QWoPowerSftp : public QWoSshFtp int mode; ds >> path >> mode >> user; return runMkDir(path, mode, user); + }else if(type == MT_FTP_MKPATH) { + QDataStream ds(data); + QByteArray path; + QVariantMap user; + int mode; + ds >> path >> mode >> user; + return runMkPath(path, mode, user); }else if(type == MT_FTP_RMDIR) { QDataStream ds(data); QByteArray path; @@ -1063,6 +1364,26 @@ class QWoPowerSftp : public QWoSshFtp QString logFile; ds >> remote >> local >> policy >> user; return runDownload(remote, local, policy, user); + }else if(type == MT_FTP_FILE_INFO) { + QDataStream ds(data); + QByteArray path; + QVariantMap user; + ds >> path >> user; + return runFileInfo(path, user); + }else if(type == MT_FTP_READ_FILE_CONTENT) { + QDataStream ds(data); + QByteArray path; + qint64 offset, maxSize; + QVariantMap user; + ds >> path >> offset >> maxSize >> user; + return runFileContent(path, offset, maxSize, user); + }else if(type == MT_FTP_WRITE_FILE_CONTENT) { + QDataStream ds(data); + QByteArray path; + QByteArray content; + QVariantMap user; + ds >> path >> content >> user; + return runWriteFileContent(path, content, user); } return true; } @@ -1144,6 +1465,13 @@ class QSshMultClient : public QSshInteractiveClient push(MT_FTP_MKDIR, buf, cli); } + void sftpMkPath(QWoSshChannel *cli, const QString& path, int mode, const QVariantMap& user) { + QByteArray buf; + QDataStream ds(&buf, QIODevice::WriteOnly); + ds << path.toUtf8() << mode << user; + push(MT_FTP_MKPATH, buf, cli); + } + void sftpRmDir(QWoSshChannel *cli, const QString& path, const QVariantMap& user) { QByteArray buf; QDataStream ds(&buf, QIODevice::WriteOnly); @@ -1158,6 +1486,20 @@ class QSshMultClient : public QSshInteractiveClient push(MT_FTP_UNLINK, buf, cli); } + void sftpFileContent(QWoSshChannel *cli, const QString& remote, qint64 offset, qint64 maxSize, const QVariantMap& user) { + QByteArray buf; + QDataStream ds(&buf, QIODevice::WriteOnly); + ds << remote.toUtf8() << offset << maxSize << user; + push(MT_FTP_READ_FILE_CONTENT, buf, cli); + } + + void sftpWriteFileContent(QWoSshChannel *cli, const QString& remote, const QByteArray& content, const QVariantMap& user) { + QByteArray buf; + QDataStream ds(&buf, QIODevice::WriteOnly); + ds << remote.toUtf8() << content << user; + push(MT_FTP_WRITE_FILE_CONTENT, buf, cli); + } + void sftpDownload(QWoSshChannel *cli, const QString& remote, const QString& local, int policy, const QVariantMap& user) { QByteArray buf; QDataStream ds(&buf, QIODevice::WriteOnly); @@ -1179,6 +1521,13 @@ class QSshMultClient : public QSshInteractiveClient push(MT_FTP_LISTFILE, buf, cli); } + void sftpFileInfo(QWoSshChannel *cli, const QString &path, const QVariantMap &user) { + QByteArray buf; + QDataStream ds(&buf, QIODevice::WriteOnly); + ds << path.toUtf8() << user; + push(MT_FTP_FILE_INFO, buf, cli); + } + void internalUploadNext(QWoSshChannel *cli, const QVariantMap& user) { QByteArray buf; QDataStream ds(&buf, QIODevice::WriteOnly); @@ -1350,6 +1699,19 @@ bool QWoSSHConnection::start(const QString &host) return true; } +bool QWoSSHConnection::start(const HostInfo &hi) +{ + if(hasRunning()) { + return false; + } + if(!init(hi)) { + return false; + } + QThread::start(); + emit connectionStart(); + return true; +} + void QWoSSHConnection::stop() { if(m_listenSocket > 0) { @@ -1406,6 +1768,13 @@ void QWoSSHConnection::sftpMkDir(QWoSshChannel *cli, const QString &path, int mo } } +void QWoSSHConnection::sftpMkPath(QWoSshChannel *cli, const QString &path, int mode, const QVariantMap& user) +{ + if(m_conn) { + m_conn->sftpMkPath(cli, path, mode, user); + } +} + void QWoSSHConnection::sftpRmDir(QWoSshChannel *cli, const QString &path, const QVariantMap& user) { if(m_conn) { @@ -1420,6 +1789,20 @@ void QWoSSHConnection::sftpUnlink(QWoSshChannel *cli, const QString &path, const } } +void QWoSSHConnection::sftpFileContent(QWoSshChannel *cli, const QString &remote, qint64 offset, qint64 maxSize, const QVariantMap &user) +{ + if(m_conn) { + m_conn->sftpFileContent(cli, remote, offset, maxSize, user); + } +} + +void QWoSSHConnection::sftpWriteFileContent(QWoSshChannel *cli, const QString &remote, const QByteArray &content, const QVariantMap &user) +{ + if(m_conn) { + m_conn->sftpWriteFileContent(cli, remote, content, user); + } +} + void QWoSSHConnection::sftpDownload(QWoSshChannel *cli, const QString &remote, const QString &local, int policy, const QVariantMap& user) { if(m_conn) { @@ -1441,6 +1824,13 @@ void QWoSSHConnection::sftpListFile(QWoSshChannel *cli, const QString &path, con } } +void QWoSSHConnection::sftpFileInfo(QWoSshChannel *cli, const QString &path, const QVariantMap &user) +{ + if(m_conn) { + m_conn->sftpFileInfo(cli, path, user); + } +} + void QWoSSHConnection::sftpAbort(QWoSshChannel *cli) { if(m_conn) { @@ -1579,6 +1969,33 @@ bool QWoSSHConnection::init(const QString &host) return true; } +bool QWoSSHConnection::init(const HostInfo &hi) +{ + if(m_listenSocket > 0) { + myclosesocket(socket_t(m_listenSocket)); + } + m_listenSocket = 0; + m_listenPort = 0; + socket_t server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + ushort port = QWoUtils::listenLocal(server, 20327); + if(port == 0) { + myclosesocket(server); + return false; + } + m_listenSocket = int(server); + m_listenPort = port; + QSshClient::TargetInfo ti = qsshToTarget(hi, false); + m_conn = new QSshMultClient(ti, this); + if(!m_conn->init(m_listenSocket, m_listenPort)) { + return false; + } + QObject::connect(m_conn, SIGNAL(finished()), this, SLOT(onFinished())); + QObject::connect(m_conn, SIGNAL(errorArrived(QString,QVariantMap)), this, SIGNAL(errorArrived(QString,QVariantMap))); + QObject::connect(m_conn, SIGNAL(passwordArrived(QString,QByteArray)), this, SIGNAL(passwordArrived(QString,QByteArray))); + QObject::connect(m_conn, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); + return true; +} + void QWoSSHConnection::run() { bool ok = running(); @@ -1658,6 +2075,29 @@ bool QWoSshChannel::start(const QString &host, int gid) return true; } +bool QWoSshChannel::start(const HostInfo &hi, int gid) +{ + QWoSshFactory *factory = QWoSshFactory::instance(); + bool ct = false; + m_conn = factory->get(gid, &ct); + if(ct) { + QObject::connect(m_conn, SIGNAL(errorArrived(QString,QVariantMap)), this, SIGNAL(errorArrived(QString,QVariantMap))); + QObject::connect(m_conn, SIGNAL(passwordArrived(QString,QByteArray)), this, SIGNAL(passwordArrived(QString,QByteArray))); + QObject::connect(m_conn, SIGNAL(inputArrived(QString,QString,bool)), this, SIGNAL(inputArrived(QString,QString,bool))); + QObject::connect(m_conn, SIGNAL(connectionStart()), this, SIGNAL(connectionStart())); + QObject::connect(m_conn, SIGNAL(connectionFinished(bool)), this, SIGNAL(connectionFinished(bool))); + if(!m_conn->start(hi)) { + return false; + } + }else{ + QObject::connect(m_conn, SIGNAL(connectionStart()), this, SIGNAL(connectionStart())); + QObject::connect(m_conn, SIGNAL(connectionFinished(bool)), this, SIGNAL(connectionFinished(bool))); + } + init(); + m_conn->append(this); + return true; +} + void QWoSshChannel::stop() { if(m_conn){ @@ -1785,6 +2225,14 @@ void QWoSshFtp::mkDir(const QString &path, int mode, const QVariantMap& user) } } +void QWoSshFtp::mkPath(const QString &path, int mode, const QVariantMap &user) +{ + QString tmp = QDir::cleanPath(path); + if(m_conn) { + m_conn->sftpMkPath(this, tmp, mode, user); + } +} + void QWoSshFtp::rmDir(const QString &path, const QVariantMap& user) { QString tmp = QDir::cleanPath(path); @@ -1801,6 +2249,28 @@ void QWoSshFtp::unlink(const QString &path, const QVariantMap& user) } } +void QWoSshFtp::fileInfo(const QString &path, const QVariantMap &user) +{ + QString tmp = QDir::cleanPath(path); + if(m_conn) { + m_conn->sftpFileInfo(this, tmp, user); + } +} + +void QWoSshFtp::fileContent(const QString &remote, qint64 offset, qint64 maxSize, const QVariantMap &user) +{ + if(m_conn) { + m_conn->sftpFileContent(this, remote, offset, maxSize, user); + } +} + +void QWoSshFtp::writeFileContent(const QString &remote, const QByteArray &content, const QVariantMap &user) +{ + if(m_conn) { + m_conn->sftpWriteFileContent(this, remote, content, user); + } +} + void QWoSshFtp::download(const QString &remote, const QString &local, TransferPolicy policy, const QVariantMap& user) { QString path = local; diff --git a/woterm/qwossh.h b/woterm/qwossh.h index 2aa79ee..35a1742 100644 --- a/woterm/qwossh.h +++ b/woterm/qwossh.h @@ -19,6 +19,22 @@ #include #include +#define MT_FTP_OPENDIR (10) +#define MT_FTP_MKDIR (11) +#define MT_FTP_RMDIR (12) +#define MT_FTP_UNLINK (13) +#define MT_FTP_DOWNLOAD (14) +#define MT_FTP_UPLOAD (15) +#define MT_FTP_UPLOADNEXT (16) +#define MT_FTP_ABORT (17) +#define MT_FTP_LISTFILE (18) +#define MT_FTP_LISTFILENEXT (19) +#define MT_FTP_REMOVEFILENEXT (20) +#define MT_FTP_FILE_INFO (21) +#define MT_FTP_MKPATH (22) +#define MT_FTP_READ_FILE_CONTENT (23) +#define MT_FTP_WRITE_FILE_CONTENT (24) + struct MsgRequest; class QSshClient; class QSshProxyClient; @@ -36,17 +52,22 @@ class QWoSSHConnection : public QThread void remove(QWoSshChannel *cli); bool hasRunning(); bool start(const QString& host); + bool start(const HostInfo& hi); void stop(); void setInputResult(const QString& pass); void shellWrite(QWoSshChannel *cli, const QByteArray& buf); void shellSize(QWoSshChannel *cli, int cols, int rows); void sftpOpenDir(QWoSshChannel *cli, const QStringList& paths, const QVariantMap& user); void sftpMkDir(QWoSshChannel *cli, const QString& path, int mode, const QVariantMap& user); + void sftpMkPath(QWoSshChannel *cli, const QString& path, int mode, const QVariantMap& user); void sftpRmDir(QWoSshChannel *cli, const QString& path, const QVariantMap& user); void sftpUnlink(QWoSshChannel *cli, const QString &path, const QVariantMap& user); + void sftpFileContent(QWoSshChannel *cli, const QString& remote, qint64 offset, qint64 maxSize, const QVariantMap& user); + void sftpWriteFileContent(QWoSshChannel *cli, const QString& remote, const QByteArray& content, const QVariantMap& user); void sftpDownload(QWoSshChannel *cli, const QString& remote, const QString& local, int policy, const QVariantMap& user); void sftpUpload(QWoSshChannel *cli, const QString& local, const QString& remote, int policy, const QVariantMap& user); void sftpListFile(QWoSshChannel *cli, const QString& path, const QVariantMap& user); + void sftpFileInfo(QWoSshChannel *cli, const QString& path, const QVariantMap& user); void sftpAbort(QWoSshChannel *cli); void internalUploadNext(QWoSshChannel *cli, const QVariantMap& user); void internalListFileNext(QWoSshChannel *cli, const QByteArray& path, const QVariantMap& user); @@ -65,6 +86,7 @@ private slots: void onConnectionFinished(bool ok); private: bool init(const QString& host); + bool init(const HostInfo& hi); void run(); bool running(); bool connectToProxy(int i, const QString& host, ushort port); @@ -84,6 +106,7 @@ class QWoSshChannel : public QObject explicit QWoSshChannel(QObject *parent=nullptr); virtual ~QWoSshChannel(); bool start(const QString& host, int gid); + bool start(const HostInfo& hi, int gid); void stop(); void setInputResult(const QString& pass); bool hasRunning(); @@ -148,8 +171,12 @@ class QWoSshFtp : public QWoSshChannel void openDir(const QStringList& paths, const QVariantMap& user=QVariantMap()); void openDir(const QString& path="~", const QVariantMap& user=QVariantMap()); void mkDir(const QString& path, int mode, const QVariantMap& user=QVariantMap()); + void mkPath(const QString& path, int mode, const QVariantMap& user=QVariantMap()); void rmDir(const QString& path, const QVariantMap& user=QVariantMap()); void unlink(const QString &path, const QVariantMap& user=QVariantMap()); + void fileInfo(const QString& path, const QVariantMap& user=QVariantMap()); + void fileContent(const QString& remote, qint64 offset, qint64 maxSize, const QVariantMap& user=QVariantMap()); + void writeFileContent(const QString& remote, const QByteArray& content, const QVariantMap& user=QVariantMap()); void download(const QString& remote, const QString& local, TransferPolicy policy = TP_Append, const QVariantMap& user=QVariantMap()); void upload(const QString& local, const QString& remote, TransferPolicy policy = TP_Append, const QVariantMap& user=QVariantMap()); void listFile(const QString& path="~", const QVariantMap& user=QVariantMap()); diff --git a/woterm/qwosshconf.cpp b/woterm/qwosshconf.cpp index 21cf1ed..9671495 100644 --- a/woterm/qwosshconf.cpp +++ b/woterm/qwosshconf.cpp @@ -13,17 +13,18 @@ #include "qwosetting.h" #include "qwoutils.h" #include "qwoidentify.h" +#include "qkxmessagebox.h" #include #include #include #include -#include #include #include #include #include +#include /* * # @@ -215,10 +216,12 @@ bool QWoSshConf::restore(const QString &dbBackup) QFile::remove(m_dbFile); SQLite::Database db(m_dbFile.toUtf8(), SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); int err = db.backup(dbBackup.toUtf8(), db.Load); + resetLater(); return err == SQLite::OK; } catch (...) { qDebug() << "SQLite initialize failed"; } + return false; } @@ -726,6 +729,11 @@ QStringList QWoSshConf::groupNameList() const return names; } +QStringList QWoSshConf::tableList() const +{ + return {"servers","groups","identities"}; +} + bool QWoSshConf::renameGroup(const QString &nameNew, const QString &nameOld) { if(_renameGroup(nameNew, nameOld)) { @@ -733,6 +741,7 @@ bool QWoSshConf::renameGroup(const QString &nameNew, const QString &nameOld) GroupInfo& gi = *it; if(nameOld == gi.name) { gi.name = nameNew; + resetLater(); return true; } } @@ -755,6 +764,7 @@ bool QWoSshConf::updateGroup(const QString &name, int order) gi.order = order; m_groups.append(gi); std::sort(m_groups.begin(), m_groups.end()); + resetLater(); return true; } return false; @@ -767,6 +777,7 @@ bool QWoSshConf::removeGroup(const QString &name) GroupInfo& gi = *it; if(name == gi.name) { m_groups.erase(it); + resetLater(); return true; } } @@ -848,6 +859,16 @@ bool QWoSshConf::_removeGroup(const QString &name) return false; } +void QWoSshConf::resetLater() +{ + if(m_timer == nullptr) { + m_timer = new QTimer(this); + QObject::connect(m_timer, SIGNAL(timeout()), this, SLOT(onResetLater())); + m_timer->setSingleShot(true); + } + m_timer->start(100); +} + bool QWoSshConf::backup(const QString &dbBackup) { try { @@ -928,6 +949,7 @@ bool QWoSshConf::removeServer(const QString &name) SQLite::Statement insert(db, QString("DELETE FROM servers WHERE name='%1'").arg(name).toUtf8()); int cnt = insert.exec(); qDebug() << "remove server" << name << cnt; + resetLater(); return true; }catch(std::exception& e) { qDebug() << "QWoSshConf::remove" << e.what(); @@ -945,6 +967,7 @@ bool QWoSshConf::removeServerByGroup(const QString &name) SQLite::Statement insert(db, QString("DELETE FROM servers WHERE groupName='%1'").arg(name).toUtf8()); int cnt = insert.exec(); qDebug() << "remove server" << name << cnt; + resetLater(); return true; }catch(std::exception& e) { qDebug() << "QWoSshConf::removeAllByGroup" << e.what(); @@ -965,6 +988,7 @@ bool QWoSshConf::renameServerGroup(const QString &nameNew, const QString &nameOl update.bind("@nameOld", nameOld.toStdString()); int cnt = update.exec(); qDebug() << "renameServerGroup" << nameOld << nameNew << cnt; + resetLater(); return cnt > 0; }catch(std::exception& e) { qDebug() << "QWoSshConf::renameServerGroup" << e.what(); @@ -975,6 +999,7 @@ bool QWoSshConf::renameServerGroup(const QString &nameNew, const QString &nameOl bool QWoSshConf::modify(const HostInfo &hi) { m_hosts.insert(hi.name, hi); + resetLater(); return save(hi); } @@ -984,12 +1009,14 @@ bool QWoSshConf::append(const HostInfo &hi) return false; } m_hosts.insert(hi.name, hi); + resetLater(); return save(hi); } bool QWoSshConf::modifyOrAppend(const HostInfo &hi) { m_hosts.insert(hi.name, hi); + resetLater(); return save(hi); } @@ -1094,6 +1121,13 @@ void QWoSshConf::onAboutToQuit() backup(m_dbFile + ".bak"); } +void QWoSshConf::onResetLater() +{ + QTimer *timer = qobject_cast(sender()); + timer->stop(); + emit dataReset(); +} + HostInfo QWoSshConf::find(const QString &name) const { return m_hosts.value(name); } diff --git a/woterm/qwosshconf.h b/woterm/qwosshconf.h index 2dc553c..5e01593 100644 --- a/woterm/qwosshconf.h +++ b/woterm/qwosshconf.h @@ -14,9 +14,11 @@ #include "qwoglobal.h" #include +#include #include class QBuffer; +class QTimer; class QWoSshConf : public QObject { @@ -24,10 +26,11 @@ class QWoSshConf : public QObject public: static QWoSshConf* instance(); static bool databaseValid(const QString& dbFile); - /* groud name */ QList groupList() const; QStringList groupNameList() const; + QStringList tableList() const; + bool renameGroup(const QString& nameNew, const QString& nameOld); bool updateGroup(const QString& name, int order = 0); bool removeGroup(const QString& name); @@ -55,8 +58,11 @@ class QWoSshConf : public QObject QStringList hostNameList(EHostType type = All) const; QList proxyJumpers(const QString& name, int max=2) const; +signals: + void dataReset(); private slots: void onAboutToQuit(); + void onResetLater(); protected: explicit QWoSshConf(const QString& dbFile, QObject *parent = nullptr); @@ -67,6 +73,7 @@ private slots: bool _renameGroup(const QString& nameNew, const QString& nameOld); bool _updateGroup(const QString& name, int order = 0); bool _removeGroup(const QString& name); + void resetLater(); private: static QHash parse(const QByteArray& buf); static void importIdentityToSqlite(const QString& path, const QString& dbFile); @@ -79,4 +86,5 @@ private slots: bool m_bInit; QHash m_hosts; QList m_groups; + QPointer m_timer; }; diff --git a/woterm/qwosshtermwidget.cpp b/woterm/qwosshtermwidget.cpp index 42ee56b..c8db5a7 100644 --- a/woterm/qwosshtermwidget.cpp +++ b/woterm/qwosshtermwidget.cpp @@ -24,21 +24,23 @@ #include "qwoevent.h" #include "qwosessionproperty.h" #include "qwofloatwindow.h" +#include "qkxmessagebox.h" #include "qkxtermwidget.h" #include "qkxtermitem.h" #include "qwoshower.h" + #include #include #include #include #include -#include #include #include #include +#include QWoSshTermWidget::QWoSshTermWidget(const QString& target, int gid, QWidget *parent) : QWoTermWidget(target, gid, parent) @@ -46,12 +48,14 @@ QWoSshTermWidget::QWoSshTermWidget(const QString& target, int gid, QWidget *pare , m_stateConnected(ESC_Ready) { QObject::connect(m_term, SIGNAL(termSizeChanged(int,int)), this, SLOT(onTermSizeChanged(int,int))); - QObject::connect(m_term, SIGNAL(sendData(const QByteArray&)), this, SLOT(onSendData(const QByteArray&))); + QObject::connect(m_term, SIGNAL(sendData(QByteArray)), this, SLOT(onSendData(QByteArray))); + QObject::connect(m_term, SIGNAL(activePathArrived(QString)), this, SLOT(onActivePathArrived(QString))); m_modem = QWoModemFactory::instance()->create(false); - QObject::connect(m_modem, SIGNAL(dataArrived(const QByteArray&)), this, SLOT(onZmodemDataArrived(const QByteArray&))); - QObject::connect(m_modem, SIGNAL(statusArrived(const QByteArray&)), this, SLOT(onZmodemStatusArrived(const QByteArray&))); + QObject::connect(m_modem, SIGNAL(dataArrived(QByteArray)), this, SLOT(onZmodemDataArrived(QByteArray))); + QObject::connect(m_modem, SIGNAL(statusArrived(QByteArray)), this, SLOT(onZmodemStatusArrived(QByteArray))); QObject::connect(m_modem, SIGNAL(finished()), this, SLOT(onZmodemFinished())); + QMetaObject::invokeMethod(this, "reconnect", Qt::QueuedConnection); } @@ -99,6 +103,10 @@ void QWoSshTermWidget::onDataArrived(const QByteArray &buf) //qDebug() << "onDataArrived" << buf; } }else{ + if(m_restoreLastActivePath) { + m_ssh->write("\r\ncd " + m_lastActivePath.toUtf8() + "\r\n"); + m_restoreLastActivePath = false; + } if(m_term->appMode()) { m_term->parse(buf); }else{ @@ -152,8 +160,12 @@ void QWoSshTermWidget::onSendData(const QByteArray &buf) #endif if(m_stateConnected == ESC_Disconnected) { if(m_dlgConfirm == nullptr) { - m_dlgConfirm = new QMessageBox(QMessageBox::Question, tr("Reconnection confirmation"), tr("Continue to connect to the server?"), QMessageBox::Yes|QMessageBox::No, this); - if(m_dlgConfirm->exec() == QMessageBox::Yes) { + m_dlgConfirm = new QKxMessageBox(QMessageBox::Question, tr("Reconnection confirmation"), tr("Continue to connect to the server?"), QMessageBox::Yes|QMessageBox::No, this); + QPushButton *btn = new QPushButton(tr("Restore"), m_dlgConfirm); + QObject::connect(btn, SIGNAL(clicked()), this, SLOT(onRestoreLastPath())); + m_dlgConfirm->addButton(btn, QMessageBox::ActionRole); + int code = m_dlgConfirm->exec(); + if(code == QMessageBox::Yes) { QMetaObject::invokeMethod(this, "reconnect", Qt::QueuedConnection); } m_dlgConfirm->deleteLater(); @@ -175,6 +187,19 @@ void QWoSshTermWidget::onCopyToClipboard() termItem()->tryToCopy(); } +void QWoSshTermWidget::onRestoreLastPath() +{ + if(m_dlgConfirm) { + m_dlgConfirm->done(QMessageBox::LastButton + 1); + reconnect(true); + } +} + +void QWoSshTermWidget::onActivePathArrived(const QString &path) +{ + m_lastActivePath = path; +} + void QWoSshTermWidget::onPasteFromClipboard() { termItem()->tryToPaste(); @@ -255,7 +280,7 @@ void QWoSshTermWidget::onNewSessionMultiplex() void QWoSshTermWidget::onModifyThisSession() { if(!QWoSshConf::instance()->exists(m_target)){ - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); return; } QWoSessionProperty dlg(this); @@ -273,7 +298,7 @@ void QWoSshTermWidget::onZmodemSend(bool local) // if(local) { // m_term->parseError("failed to find rz program, please install lrzsz."); // m_term->waitInput(); -// QMessageBox::warning(this, tr("warning"), tr("failed to find rz program, please install lrzsz.")); +// QKxMessageBox::warning(this, tr("warning"), tr("failed to find rz program, please install lrzsz.")); // } // return; // } @@ -416,7 +441,7 @@ bool QWoSshTermWidget::checkProgram(const QByteArray &name) return code == 0; } -void QWoSshTermWidget::reconnect() +void QWoSshTermWidget::reconnect(bool restore) { if(m_passInput) { m_passInput->deleteLater(); @@ -429,19 +454,25 @@ void QWoSshTermWidget::reconnect() QObject::connect(m_ssh, SIGNAL(connectionFinished(bool)), this, SLOT(onConnectionFinished(bool))); QObject::connect(m_ssh, SIGNAL(finishArrived(int)), this, SLOT(onFinishArrived(int))); - QObject::connect(m_ssh, SIGNAL(dataArrived(const QByteArray&)), this, SLOT(onDataArrived(const QByteArray&))); - QObject::connect(m_ssh, SIGNAL(errorArrived(const QByteArray&)), this, SLOT(onErrorArrived(const QByteArray&))); - QObject::connect(m_ssh, SIGNAL(passwordArrived(const QString&,const QByteArray&)), this, SLOT(onPasswordArrived(const QString&,const QByteArray&))); - QObject::connect(m_ssh, SIGNAL(inputArrived(const QString&,const QString&,bool)), this, SLOT(onInputArrived(const QString&,const QString&,bool))); + QObject::connect(m_ssh, SIGNAL(dataArrived(QByteArray)), this, SLOT(onDataArrived(QByteArray))); + QObject::connect(m_ssh, SIGNAL(errorArrived(QByteArray)), this, SLOT(onErrorArrived(QByteArray))); + QObject::connect(m_ssh, SIGNAL(passwordArrived(QString,QByteArray)), this, SLOT(onPasswordArrived(QString,QByteArray))); + QObject::connect(m_ssh, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); m_ssh->start(m_target, m_gid); QSize sz = m_term->termSize(); m_ssh->updateSize(sz.width(), sz.height()); showLoading(true); - HostInfo hi = QWoSshConf::instance()->find(m_target); - if(!hi.script.isEmpty()){ - executeCommand(hi.script.toUtf8()); + if(restore) { + qDebug() << "path"; + m_restoreLastActivePath = true; + }else{ + m_restoreLastActivePath = false; + HostInfo hi = QWoSshConf::instance()->find(m_target); + if(!hi.script.isEmpty()){ + executeCommand(hi.script.toUtf8()); + } } m_stateConnected = ESC_Connecting; @@ -453,10 +484,10 @@ void QWoSshTermWidget::executeCommand(const QByteArray &cmd) QWoSshFactory::instance()->release(m_cmd); } m_cmd = QWoSshFactory::instance()->createShell(true); - QObject::connect(m_cmd, SIGNAL(dataArrived(const QByteArray&)), this, SLOT(onDataArrived(const QByteArray&))); - QObject::connect(m_cmd, SIGNAL(errorArrived(const QByteArray&)), this, SLOT(onErrorArrived(const QByteArray&))); - QObject::connect(m_cmd, SIGNAL(passwordArrived(const QString&,const QByteArray&)), this, SLOT(onPasswordArrived(const QString&,const QByteArray&))); - QObject::connect(m_cmd, SIGNAL(inputArrived(const QString&,const QString&,bool)), this, SLOT(onInputArrived(const QString&,const QString&,bool))); + QObject::connect(m_cmd, SIGNAL(dataArrived(QByteArray)), this, SLOT(onDataArrived(QByteArray))); + QObject::connect(m_cmd, SIGNAL(errorArrived(QByteArray)), this, SLOT(onErrorArrived(QByteArray))); + QObject::connect(m_cmd, SIGNAL(passwordArrived(QString,QByteArray)), this, SLOT(onPasswordArrived(QString,QByteArray))); + QObject::connect(m_cmd, SIGNAL(inputArrived(QString,QString,bool)), this, SLOT(onInputArrived(QString,QString,bool))); m_cmd->start(m_target, m_gid); QSize sz = m_term->termSize(); m_cmd->updateSize(sz.width(), sz.height()); diff --git a/woterm/qwosshtermwidget.h b/woterm/qwosshtermwidget.h index 4deb2f0..04b4d14 100644 --- a/woterm/qwosshtermwidget.h +++ b/woterm/qwosshtermwidget.h @@ -47,6 +47,8 @@ private slots: void onTermSizeChanged(int lines, int columns); void onSendData(const QByteArray& buf); void onCopyToClipboard(); + void onRestoreLastPath(); + void onActivePathArrived(const QString& path); void onPasteFromClipboard(); void onPasswordInputResult(const QString& pass, bool isSave); void onSessionReconnect(); @@ -74,7 +76,7 @@ private slots: int isZmodemCommand(const QByteArray &data); bool checkProgram(const QByteArray &name); private: - Q_INVOKABLE void reconnect(); + Q_INVOKABLE void reconnect(bool restore=false); Q_INVOKABLE void executeCommand(const QByteArray& cmd); private: virtual void resizeEvent(QResizeEvent *ev); @@ -95,4 +97,6 @@ private slots: QPointer m_shortCutCopy; QPointer m_shortCutPaste; EStateConnect m_stateConnected; + QString m_lastActivePath; + bool m_restoreLastActivePath; }; diff --git a/woterm/qwotelnettermwidget.cpp b/woterm/qwotelnettermwidget.cpp index 18d75f9..2860e18 100644 --- a/woterm/qwotelnettermwidget.cpp +++ b/woterm/qwotelnettermwidget.cpp @@ -27,6 +27,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxmessagebox.h" #include @@ -34,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -232,7 +232,7 @@ void QWoTelnetTermWidget::onDuplicateInNewWindow() void QWoTelnetTermWidget::onModifyThisSession() { if(!QWoSshConf::instance()->exists(m_target)){ - QMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); + QKxMessageBox::warning(this, tr("Error"), tr("can't find the session, maybe it had been delete ago")); return; } QWoSessionProperty dlg(this); diff --git a/woterm/qwotermwidget.cpp b/woterm/qwotermwidget.cpp index 945f2bd..ff3eda2 100644 --- a/woterm/qwotermwidget.cpp +++ b/woterm/qwotermwidget.cpp @@ -23,6 +23,7 @@ #include "qwomainwindow.h" #include "qwoshowerwidget.h" #include "qwoshower.h" +#include "qkxmessagebox.h" #include "qkxtermitem.h" @@ -37,7 +38,6 @@ #include #include #include -#include QWoTermWidget::QWoTermWidget(const QString& target, int gid, QWidget *parent) @@ -187,7 +187,7 @@ void QWoTermWidget::onOutputHistoryToFile() } QFile hit(file); if(!hit.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("Warning"), tr("Failed to create file.")); + QKxMessageBox::warning(this, tr("Warning"), tr("Failed to create file.")); return; } hit.close(); diff --git a/woterm/qwotermwidgetimpl.cpp b/woterm/qwotermwidgetimpl.cpp index de68581..742d82e 100644 --- a/woterm/qwotermwidgetimpl.cpp +++ b/woterm/qwotermwidgetimpl.cpp @@ -18,10 +18,10 @@ #include "qwoevent.h" #include "qwocommandlineinput.h" #include "qwoutils.h" +#include "qkxmessagebox.h" #include #include -#include #include #include #include diff --git a/woterm/qwoutils.cpp b/woterm/qwoutils.cpp index f758412..4eca87c 100644 --- a/woterm/qwoutils.cpp +++ b/woterm/qwoutils.cpp @@ -11,9 +11,11 @@ #include "qwoutils.h" #include "qkxver.h" +#include "qkxmessagebox.h" + #include -#include -#include +#include +#include #include #include #include @@ -26,7 +28,6 @@ #include #include #include -#include #include #include @@ -645,6 +646,12 @@ QByteArray QWoUtils::aesOfb128Decrypt(const QByteArray &all, const QByteArray &p QByteArray QWoUtils::aesEncrypt(const QByteArray &all, const QByteArray &pass) { + if(all.isEmpty()) { + return all; + } + if(all.startsWith("WoTerm:")) { + return all; + } QByteArray result = aesOfb128Encrypt(all, pass); QByteArray b64 = result.toBase64(QByteArray::Base64Encoding); return "WoTerm:"+b64; @@ -652,6 +659,9 @@ QByteArray QWoUtils::aesEncrypt(const QByteArray &all, const QByteArray &pass) QByteArray QWoUtils::aesDecrypt(const QByteArray &all, const QByteArray &pass) { + if(all.isEmpty()) { + return all; + } if(!all.startsWith("WoTerm:")) { return all; } @@ -664,7 +674,7 @@ QByteArray QWoUtils::aesDecrypt(const QByteArray &all, const QByteArray &pass) bool QWoUtils::isUltimateVersion(QWidget *parent) { if(!QKxVer::isUltimate()) { - QMessageBox::information(parent, QObject::tr("Ultimate version"), QObject::tr("this is the feature of the ultimate version, please upgrade to latest version.")); + QKxMessageBox::information(parent, QObject::tr("Ultimate version"), QObject::tr("this is the feature of the ultimate version, please upgrade to latest version.")); return false; } return true; diff --git a/woterm/resource/qss/default.qss b/woterm/resource/qss/default.qss index fa4c9e0..a9fb06f 100644 --- a/woterm/resource/qss/default.qss +++ b/woterm/resource/qss/default.qss @@ -193,6 +193,19 @@ QPushButton#transferRemove{ min-width: 70px; } +QPushButton#restoreButton{ + border-radius: 3px; + border: 1px solid darkgray; + padding: 5px; + color: black; + min-width: 60px; +} + +QPushButton:hover#restoreButton{ + border: 1px solid rgba(212,35,122,255); + color: rgba(212,35,122,255); +} + QPushButton:hover{ border: 1px solid rgba(212,35,122,255); color: rgba(212,35,122,255); diff --git a/woterm/version.h b/woterm/version.h index a58b9ff..a4e17bb 100644 --- a/woterm/version.h +++ b/woterm/version.h @@ -1,4 +1,4 @@ #pragma once -#define WOTERM_VERSION ("9.24.1") +#define WOTERM_VERSION ("9.25.0") #define NOISE