├── images
├── icon.ico
├── online.png
└── offline.png
├── ts
├── DrCOM_zh_CN.qm
└── DrCOM_zh_CN.ts
├── DrCOM_JLU_Qt.qrc
├── singleinstance
├── singleapplication.pri
├── LICENSE
├── Windows.md
├── singleapplication_p.h
├── singleapplication.h
├── singleapplication.cpp
├── CHANGELOG.md
├── README.md
└── singleapplication_p.cpp
├── interruptiblesleeper.cpp
├── interruptiblesleeper.h
├── encrypt
├── sha1.h
├── EncryptData.h
├── md4.h
├── md5.h
├── EncryptData.cpp
├── sha1.cpp
├── md4.cpp
└── md5.cpp
├── .gitignore
├── dogcomcontroller.h
├── DogcomSocket.h
├── dogcom.h
├── dogcomcontroller.cpp
├── constants.h
├── mainwindow.h
├── DrCOM_JLU_Qt.pro
├── main.cpp
├── README.md
├── DogcomSocket.cpp
├── mainwindow.ui
├── mainwindow.cpp
├── dogcom.cpp
└── LICENSE
/images/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/HEAD/images/icon.ico
--------------------------------------------------------------------------------
/images/online.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/HEAD/images/online.png
--------------------------------------------------------------------------------
/ts/DrCOM_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/HEAD/ts/DrCOM_zh_CN.qm
--------------------------------------------------------------------------------
/images/offline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/HEAD/images/offline.png
--------------------------------------------------------------------------------
/DrCOM_JLU_Qt.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | images/offline.png
4 | images/online.png
5 | ts/DrCOM_zh_CN.qm
6 |
7 |
8 |
--------------------------------------------------------------------------------
/singleinstance/singleapplication.pri:
--------------------------------------------------------------------------------
1 | QT += core network
2 | CONFIG += c++11
3 |
4 | HEADERS += $$PWD/singleapplication.h \
5 | $$PWD/singleapplication_p.h
6 | SOURCES += $$PWD/singleapplication.cpp \
7 | $$PWD/singleapplication_p.cpp
8 |
9 | INCLUDEPATH += $$PWD
10 |
11 | win32 {
12 | msvc:LIBS += Advapi32.lib
13 | gcc:LIBS += -ladvapi32
14 | }
15 |
16 | DISTFILES += \
17 | $$PWD/README.md \
18 | $$PWD/CHANGELOG.md \
19 | $$PWD/Windows.md
20 |
--------------------------------------------------------------------------------
/interruptiblesleeper.cpp:
--------------------------------------------------------------------------------
1 | #include "interruptiblesleeper.h"
2 | #include
3 |
4 | InterruptibleSleeper::InterruptibleSleeper(QObject *parent) : QObject(parent)
5 | {
6 | qDebug() << "InterruptibleSleeper Constructor";
7 | }
8 |
9 | bool InterruptibleSleeper::Sleep(int timeout)
10 | {
11 | m.tryLock();
12 | if (m.tryLock(timeout)) {
13 | //获取到锁 睡眠失败 被中断了
14 | return false;
15 | }
16 | else {
17 | //未获取到锁 睡眠成功 没有被中断
18 | m.unlock();
19 | return true;
20 | }
21 | }
22 |
23 | void InterruptibleSleeper::Interrupt() {
24 | qDebug() << "InterruptibleSleeper Interruptted";
25 | m.unlock();
26 | }
27 |
--------------------------------------------------------------------------------
/interruptiblesleeper.h:
--------------------------------------------------------------------------------
1 | #ifndef INTERRUPTIBLESLEEPER_H
2 | #define INTERRUPTIBLESLEEPER_H
3 |
4 | #include
5 | #include
6 |
7 | /**
8 | * @brief The InterruptibleSleeper class
9 | * 使用说明:直接Sleep指定时长即可,单位是ms
10 | * 中止后台时调用Interrupt,会立即响应
11 | * 通过判断Sleep的返回值确定是否被中断,true即睡眠成功未被中断,false即被中断
12 | */
13 | class InterruptibleSleeper : public QObject
14 | {
15 | Q_OBJECT
16 | public:
17 | explicit InterruptibleSleeper(QObject *parent = nullptr);
18 | // 睡眠成功返回 true 被中断返回 false
19 | bool Sleep(int timeout);
20 | void Interrupt();
21 |
22 | private:
23 | QMutex m;
24 | };
25 |
26 | #endif // INTERRUPTIBLESLEEPER_H
27 |
--------------------------------------------------------------------------------
/encrypt/sha1.h:
--------------------------------------------------------------------------------
1 | #ifndef SHA1_H
2 | #define SHA1_H
3 | /* ================ sha1.h ================ */
4 | /*
5 | SHA-1 in C
6 | By Steve Reid
7 | 100% Public Domain
8 | */
9 | #include
10 |
11 | typedef struct {
12 | uint32_t state[5];
13 | uint32_t count[2];
14 | unsigned char buffer[64];
15 | } SHA1_CTX;
16 |
17 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
18 | void SHA1Init(SHA1_CTX* context);
19 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
20 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
21 |
22 | void SHA1(const unsigned char* data, uint32_t len, unsigned char digest[20]);
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/encrypt/EncryptData.h:
--------------------------------------------------------------------------------
1 | #ifndef ENCRYPTDATA_H
2 | #define ENCRYPTDATA_H
3 |
4 | #include
5 | #include
6 |
7 | #ifdef _WIN32
8 | #include
9 | #include
10 | #endif
11 |
12 | #ifdef _WIN32
13 | QByteArray EncryptWithWindowsDPAPI(const QByteArray& data);
14 | QByteArray DecryptWithWindowsDPAPI(const QByteArray& encryptedData);
15 | #else
16 | QByteArray SimpleEncrypt(const QByteArray& data, const QByteArray& key = "SimpleKey");
17 | QByteArray SimpleDecrypt(const QByteArray& data, const QByteArray& key = "SimpleKey");
18 | #endif
19 |
20 | QString EncryptString(const QString& password);
21 | QString DecryptString(const QString& base64EncodedPassword);
22 |
23 | #endif // ENCRYPTDATA_H
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 | *.slo
3 | *.lo
4 | *.o
5 | *.a
6 | *.la
7 | *.lai
8 | *.so
9 | *.dll
10 | *.dylib
11 |
12 | # Qt-es
13 | object_script.*.Release
14 | object_script.*.Debug
15 | *_plugin_import.cpp
16 | /.qmake.cache
17 | /.qmake.stash
18 | *.pro.user
19 | *.pro.user.*
20 | *.qbs.user
21 | *.qbs.user.*
22 | *.moc
23 | moc_*.cpp
24 | moc_*.h
25 | qrc_*.cpp
26 | ui_*.h
27 | *.qmlc
28 | *.jsc
29 | Makefile*
30 | *build-*
31 |
32 | # Qt unit tests
33 | target_wrapper.*
34 |
35 | # QtCreator
36 | *.autosave
37 |
38 | # QtCreator Qml
39 | *.qmlproject.user
40 | *.qmlproject.user.*
41 |
42 | # QtCreator CMake
43 | CMakeLists.txt.user*
44 |
45 | *.docx
46 | *.vsdx
47 | .vs
48 | .vscode
49 | *.code-workspace
50 | *.zip
51 |
52 | # Build
53 | build/
--------------------------------------------------------------------------------
/dogcomcontroller.h:
--------------------------------------------------------------------------------
1 | #ifndef DOGCOMCONTROLLER_H
2 | #define DOGCOMCONTROLLER_H
3 |
4 | #include
5 | #include
6 | #include "constants.h"
7 | #include "dogcom.h"
8 | #include "interruptiblesleeper.h"
9 |
10 | class DogcomController : public QObject
11 | {
12 | Q_OBJECT
13 | public:
14 | DogcomController();
15 | virtual ~DogcomController();
16 | void Login(const QString &account, const QString &password, const QString &mac);
17 | void LogOut();
18 | public slots:
19 | void HandleDogcomOffline(int reason);
20 | void HandleDogcomOnline();
21 | void HandleIpAddress(unsigned char x1, unsigned char x2, unsigned char x3, unsigned char x4);
22 | signals:
23 | void HaveBeenOffline(int reason);
24 | void HaveLoggedIn();
25 | void HaveObtainedIp(const QString &ip);
26 | private:
27 | InterruptibleSleeper *sleeper=nullptr;
28 | DogCom *dogcom=nullptr;
29 | };
30 |
31 | #endif // DOGCOMCONTROLLER_H
32 |
--------------------------------------------------------------------------------
/singleinstance/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Itay Grudev 2015 - 2016
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 | Note: Some of the examples include code not distributed under the terms of the
24 | MIT License.
25 |
--------------------------------------------------------------------------------
/DogcomSocket.h:
--------------------------------------------------------------------------------
1 | #ifndef MYUDPSOCKET_DOGCOMSOCKET_H
2 | #define MYUDPSOCKET_DOGCOMSOCKET_H
3 |
4 | #include
5 |
6 | #ifdef WIN32
7 | #include
8 | #else
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #endif
15 |
16 | namespace DogcomError {
17 | enum DogcomSocketError {
18 | WSA_START_UP = 0x10,
19 | SOCKET,
20 | BIND,
21 | SET_SOCK_OPT_TIMEOUT,
22 | SET_SOCK_OPT_REUSE
23 | };
24 | }
25 |
26 | class DogcomSocketException : public std::exception {
27 | public:
28 | int errCode = -1;
29 | int realErrCode = -1;
30 | const char *what() const noexcept override;
31 | DogcomSocketException(int errCode, int realErrCode);
32 | };
33 |
34 | class DogcomSocket {
35 | private:
36 | int sockfd = -1;
37 | struct sockaddr_in bind_addr;
38 | struct sockaddr_in dest_addr;
39 | const static int BIND_PORT = 61440;
40 | const static int DEST_PORT = 61440;
41 | static char AUTH_SERVER[20];
42 |
43 | public:
44 | DogcomSocket();
45 | void init();
46 | int write(const char *buf, int len);
47 | int read(char *buf);
48 |
49 | virtual ~DogcomSocket();
50 |
51 | };
52 |
53 | #endif //MYUDPSOCKET_DOGCOMSOCKET_H
54 |
--------------------------------------------------------------------------------
/dogcom.h:
--------------------------------------------------------------------------------
1 | #ifndef DOGCOM_H
2 | #define DOGCOM_H
3 |
4 | #include
5 | #include "interruptiblesleeper.h"
6 | #include
7 | #include "constants.h"
8 | #include "DogcomSocket.h"
9 |
10 | class DogCom : public QThread
11 | {
12 | Q_OBJECT
13 | public:
14 | DogCom(InterruptibleSleeper *);
15 | void Stop();
16 | void FillConfig(QString a, QString p, QString m);
17 | protected:
18 | void run() override;
19 | private:
20 | InterruptibleSleeper *sleeper;
21 | QString account;
22 | QString password;
23 | QString mac_addr;
24 |
25 | bool dhcp_challenge(DogcomSocket &socket, unsigned char seed[]);
26 | bool log;
27 | void print_packet(const char msg[10], const unsigned char *packet, int length);
28 | int dhcp_login(DogcomSocket &socket, unsigned char seed[], unsigned char auth_information[]);
29 | int keepalive_1(DogcomSocket &socket, unsigned char auth_information[]);
30 | int keepalive_2(DogcomSocket &socket, int *keepalive_counter, int *first);
31 | void gen_crc(unsigned char seed[], int encrypt_type, unsigned char crc[]);
32 | void keepalive_2_packetbuilder(unsigned char keepalive_2_packet[], int keepalive_counter, int filepacket, int type);
33 |
34 |
35 | signals:
36 | void ReportOffline(int reason);
37 | void ReportOnline();
38 | void ReportIpAddress(unsigned char x1, unsigned char x2, unsigned char x3, unsigned char x4);
39 | };
40 |
41 | #endif // DOGCOM_H
42 |
--------------------------------------------------------------------------------
/dogcomcontroller.cpp:
--------------------------------------------------------------------------------
1 | #include "dogcomcontroller.h"
2 | #include
3 |
4 | DogcomController::DogcomController()
5 | {
6 | sleeper = new InterruptibleSleeper();
7 | dogcom = new DogCom(sleeper);
8 |
9 | connect(dogcom, &DogCom::ReportOnline, this, &DogcomController::HandleDogcomOnline);
10 | connect(dogcom, &DogCom::ReportOffline, this, &DogcomController::HandleDogcomOffline);
11 | connect(dogcom, &DogCom::ReportIpAddress, this, &DogcomController::HandleIpAddress);
12 | }
13 |
14 | DogcomController::~DogcomController(){
15 | if(sleeper!=nullptr) delete sleeper;
16 | if(dogcom!=nullptr) delete dogcom;
17 | }
18 |
19 | void DogcomController::Login(const QString &account, const QString &password, const QString &mac_addr) {
20 | qDebug() << "Filling config...";
21 | dogcom->FillConfig(account, password, mac_addr);
22 | qDebug() << "Fill config done.";
23 | dogcom->start();
24 | }
25 |
26 | void DogcomController::LogOut()
27 | {
28 | dogcom->Stop();
29 | }
30 |
31 | void DogcomController::HandleDogcomOffline(int reason)
32 | {
33 | emit HaveBeenOffline(reason);
34 | }
35 |
36 | void DogcomController::HandleDogcomOnline()
37 | {
38 | emit HaveLoggedIn();
39 | }
40 |
41 | void DogcomController::HandleIpAddress(unsigned char x1, unsigned char x2, unsigned char x3, unsigned char x4)
42 | {
43 | QString ip = QString::asprintf("%d.%d.%d.%d", x1, x2, x3, x4);
44 | qDebug() << "IP ADDRESS:" << ip;
45 | emit HaveObtainedIp(ip);
46 | }
47 |
48 |
49 |
--------------------------------------------------------------------------------
/singleinstance/Windows.md:
--------------------------------------------------------------------------------
1 | Windows Specific Implementations
2 | ================================
3 |
4 | Setting the foreground window
5 | -----------------------------
6 |
7 | In the `instanceStarted()` example in the `README` we demonstrated how an
8 | application can bring it's primary instance window whenever a second copy
9 | of the application is started.
10 |
11 | On Windows the ability to bring the application windows to the foreground is
12 | restricted, see [`AllowSetForegroundWindow()`][AllowSetForegroundWindow] for more
13 | details.
14 |
15 | The background process (the primary instance) can bring its windows to the
16 | foreground if it is allowed by the current foreground process (the secondary
17 | instance). To bypass this `SingleApplication` must be initialized with the
18 | `allowSecondary` parameter set to `true` and the `options` parameter must
19 | include `Mode::SecondaryNotification`, See `SingleApplication::Mode` for more
20 | details.
21 |
22 | Here is an example:
23 |
24 | ```cpp
25 | if( app.isSecondary() ) {
26 | // This API requires LIBS += User32.lib to be added to the project
27 | AllowSetForegroundWindow( DWORD( app.primaryPid() ) );
28 | }
29 |
30 | if( app.isPrimary() ) {
31 | QObject::connect(
32 | &app,
33 | &SingleApplication::instanceStarted,
34 | this,
35 | &App::instanceStarted
36 | );
37 | }
38 | ```
39 |
40 | ```cpp
41 | void App::instanceStarted() {
42 | QApplication::setActiveWindow( [window/widget to set to the foreground] );
43 | }
44 | ```
45 |
46 | [AllowSetForegroundWindow]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632668.aspx
47 |
--------------------------------------------------------------------------------
/constants.h:
--------------------------------------------------------------------------------
1 | #ifndef CONSTANTS_H
2 | #define CONSTANTS_H
3 |
4 | #include
5 |
6 | enum {
7 | // 离线原因
8 | OFF_UNKNOWN,
9 | OFF_USER_LOGOUT,
10 | OFF_CHALLENGE_FAILED,
11 | OFF_CHECK_MAC,
12 | OFF_SERVER_BUSY,
13 | OFF_WRONG_PASS,
14 | OFF_NOT_ENOUGH,
15 | OFF_FREEZE_UP,
16 | OFF_NOT_ON_THIS_IP,
17 | OFF_NOT_ON_THIS_MAC,
18 | OFF_TOO_MUCH_IP,
19 | OFF_UPDATE_CLIENT,
20 | OFF_NOT_ON_THIS_IP_MAC,
21 | OFF_MUST_USE_DHCP,
22 | OFF_TIMEOUT,
23 |
24 | // 初始化失败的原因
25 | OFF_WSA_STARTUP,
26 | OFF_CREATE_SOCKET,
27 | OFF_BIND_FAILED,
28 | OFF_SET_SOCKET_TIMEOUT,
29 | OFF_SET_SOCKET_REUSE,
30 |
31 |
32 | // challenge 成功 获取到服务器返回的ip地址
33 | OBTAIN_IP_ADDRESS,
34 |
35 | // 当前状态
36 | STATE_OFFLINE,
37 | STATE_LOGGING,
38 | STATE_ONLINE
39 |
40 | };
41 |
42 | enum {
43 | LOGIN_CHECK_MAC = 0x01,
44 | LOGIN_SERVER_BUSY = 0x02,
45 | LOGIN_WRONG_PASS = 0x03,
46 | LOGIN_NOT_ENOUGH = 0x04,
47 | LOGIN_FREEZE_UP = 0x05,
48 | LOGIN_NOT_ON_THIS_IP = 0x07,
49 | LOGIN_NOT_ON_THIS_MAC = 0x0B,
50 | LOGIN_TOO_MUCH_IP = 0x14,
51 | LOGIN_UPDATE_CLIENT = 0x15,
52 | LOGIN_NOT_ON_THIS_IP_MAC = 0x16,
53 | LOGIN_MUST_USE_DHCP = 0x17
54 | };
55 |
56 | const int PORT_BIND = 61440;
57 | const int PORT_DEST = 61440;
58 | const QString SERVER_IP = "10.100.61.3";
59 | const QString SETTINGS_FILE_NAME = "DrCOM_JLU_Qt";
60 | const QString
61 | ID_ACCOUNT = "account",
62 | ID_PASSWORD = "password",
63 | ID_MAC = "mac",
64 | ID_REMEMBER = "remember",
65 | ID_AUTO_LOGIN = "autoLogin",
66 | ID_HIDE_WINDOW = "showWindow",
67 | ID_NOT_SHOW_WELCOME = "showWelcome";
68 | const QString ID_RESTART_TIMES = "restartTimes";
69 | const int RETRY_TIMES = 3;
70 |
71 | #endif // CONSTANTS_H
72 |
--------------------------------------------------------------------------------
/encrypt/md4.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
3 | * MD4 Message-Digest Algorithm (RFC 1320).
4 | *
5 | * Homepage:
6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
7 | *
8 | * Author:
9 | * Alexander Peslyak, better known as Solar Designer
10 | *
11 | * This software was written by Alexander Peslyak in 2001. No copyright is
12 | * claimed, and the software is hereby placed in the public domain.
13 | * In case this attempt to disclaim copyright and place the software in the
14 | * public domain is deemed null and void, then the software is
15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
16 | * general public under the following terms:
17 | *
18 | * Redistribution and use in source and binary forms, with or without
19 | * modification, are permitted.
20 | *
21 | * There's ABSOLUTELY NO WARRANTY, express or implied.
22 | *
23 | * See md4.c for more information.
24 | */
25 |
26 | #ifdef HAVE_OPENSSL
27 | #include
28 | #elif !defined(_MD4_H)
29 | #define _MD4_H
30 |
31 | /* Any 32-bit or wider unsigned integer data type will do */
32 | typedef unsigned int MD4_u32plus;
33 |
34 | typedef struct {
35 | MD4_u32plus lo, hi;
36 | MD4_u32plus a, b, c, d;
37 | unsigned char buffer[64];
38 | MD4_u32plus block[16];
39 | } MD4_CTX;
40 |
41 | extern void MD4_Init(MD4_CTX *ctx);
42 | extern void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size);
43 | extern void MD4_Final(unsigned char *result, MD4_CTX *ctx);
44 |
45 | void MD4(const void *data, unsigned long size, unsigned char *result);
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/encrypt/md5.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
3 | * MD5 Message-Digest Algorithm (RFC 1321).
4 | *
5 | * Homepage:
6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
7 | *
8 | * Author:
9 | * Alexander Peslyak, better known as Solar Designer
10 | *
11 | * This software was written by Alexander Peslyak in 2001. No copyright is
12 | * claimed, and the software is hereby placed in the public domain.
13 | * In case this attempt to disclaim copyright and place the software in the
14 | * public domain is deemed null and void, then the software is
15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
16 | * general public under the following terms:
17 | *
18 | * Redistribution and use in source and binary forms, with or without
19 | * modification, are permitted.
20 | *
21 | * There's ABSOLUTELY NO WARRANTY, express or implied.
22 | *
23 | * See md5.c for more information.
24 | */
25 |
26 | #ifdef HAVE_OPENSSL
27 | #include
28 | #elif !defined(_MD5_H)
29 | #define _MD5_H
30 |
31 | /* Any 32-bit or wider unsigned integer data type will do */
32 | typedef unsigned int MD5_u32plus;
33 |
34 | typedef struct {
35 | MD5_u32plus lo, hi;
36 | MD5_u32plus a, b, c, d;
37 | unsigned char buffer[64];
38 | MD5_u32plus block[16];
39 | } MD5_CTX;
40 |
41 | extern void MD5_Init(MD5_CTX *ctx);
42 | extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
43 | extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
44 |
45 | void MD5(const void *data, unsigned long size, unsigned char *result);
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/encrypt/EncryptData.cpp:
--------------------------------------------------------------------------------
1 | #include "EncryptData.h"
2 |
3 | #ifdef _WIN32
4 |
5 | // 加密
6 | QByteArray EncryptWithWindowsDPAPI(const QByteArray& data)
7 | {
8 | DATA_BLOB inBlob;
9 | inBlob.pbData = (BYTE*)data.data();
10 | inBlob.cbData = data.size();
11 |
12 | DATA_BLOB outBlob;
13 | if (CryptProtectData(&inBlob, L"Password", nullptr, nullptr, nullptr, 0, &outBlob)) {
14 | QByteArray result((char*)outBlob.pbData, outBlob.cbData);
15 | LocalFree(outBlob.pbData);
16 | return result;
17 | } else {
18 | return {};
19 | }
20 | }
21 |
22 | // 解密
23 | QByteArray DecryptWithWindowsDPAPI(const QByteArray& encryptedData)
24 | {
25 | DATA_BLOB inBlob;
26 | inBlob.pbData = (BYTE*)encryptedData.data();
27 | inBlob.cbData = encryptedData.size();
28 |
29 | DATA_BLOB outBlob;
30 | if (CryptUnprotectData(&inBlob, nullptr, nullptr, nullptr, nullptr, 0, &outBlob)) {
31 | QByteArray result((char*)outBlob.pbData, outBlob.cbData);
32 | LocalFree(outBlob.pbData);
33 | return result;
34 | } else {
35 | return {};
36 | }
37 | }
38 |
39 | #else
40 | QByteArray SimpleEncrypt(const QByteArray& data, const QByteArray& key)
41 | {
42 | QByteArray ba = data;
43 | for (int i = 0; i < ba.size(); ++i){
44 | ba[i] = ba[i] ^ key[i % key.size()];
45 | }
46 | return ba;
47 | }
48 |
49 | QByteArray SimpleDecrypt(const QByteArray& data, const QByteArray& key)
50 | {
51 | QByteArray ba = data;
52 | for (int i = 0; i < ba.size(); ++i){
53 | ba[i] = ba[i] ^ key[i % key.size()];
54 | }
55 | return ba;
56 | }
57 | #endif
58 |
59 | // 包装为字符串操作
60 | QString EncryptString(const QString& password)
61 | {
62 | #ifdef _WIN32
63 | QByteArray encrypted = EncryptWithWindowsDPAPI(password.toUtf8());
64 | #else
65 | QByteArray encrypted = SimpleEncrypt(password.toUtf8());
66 | #endif
67 | return encrypted.toBase64();
68 | }
69 |
70 | QString DecryptString(const QString& base64EncodedPassword)
71 | {
72 | QByteArray decoded = QByteArray::fromBase64(base64EncodedPassword.toUtf8());
73 | #ifdef _WIN32
74 | QByteArray decrypted = DecryptWithWindowsDPAPI(decoded);
75 | #else
76 | QByteArray decrypted = SimpleDecrypt(decoded);
77 | #endif
78 | return QString::fromUtf8(decrypted);
79 | }
80 |
--------------------------------------------------------------------------------
/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "singleapplication.h"
10 |
11 | namespace Ui {
12 | class MainWindow;
13 | }
14 |
15 | class MainWindow : public QDialog
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | explicit MainWindow(SingleApplication *parentApp=nullptr, QWidget *parent = nullptr);
21 | ~MainWindow() override;
22 |
23 | void closeEvent(QCloseEvent *) override;
24 |
25 | private slots:
26 | void on_checkBoxAutoLogin_toggled(bool checked);
27 | void on_checkBoxRemember_toggled(bool checked);
28 | void on_comboBoxMAC_currentTextChanged(const QString &arg1);
29 | void on_pushButtonLogin_clicked();
30 | void IconActivated(QSystemTrayIcon::ActivationReason reason);
31 | void UserLogOut();
32 |
33 | void on_checkBoxNotShowWelcome_toggled(bool checked);
34 |
35 | void on_checkBoxHideLoginWindow_toggled(bool checked);
36 |
37 | public slots:
38 | void HandleOffline(int reason);
39 | void HandleLoggedIn();
40 | void HandleIpAddress(const QString &ip);
41 | void ShowLoginWindow();
42 | void RestartDrcom();
43 | void QuitDrcom();
44 | void RestartDrcomByUser();
45 |
46 | private:
47 | Ui::MainWindow *ui;
48 | SingleApplication *app=nullptr;
49 | const QString CUSTOM_MAC = tr("custom (format: 1A:2B:3C:4D:5E:6F case insensitive)");
50 | const QString APP_NAME = tr("DrCOM JLU Qt version");
51 |
52 | // 用于确保调用socket的析构函数,释放资源
53 | bool bQuit=false;
54 | bool bRestart=false;
55 |
56 | // 记录用户保存的信息
57 | QString account, password, mac_addr;
58 | bool bRemember, bAutoLogin;
59 | bool bHideWindow, bNotShowWelcome;
60 |
61 | // 用于在未登录时关闭窗口就退出
62 | int CURR_STATE;
63 |
64 | QValidator *macValidator;
65 | DogcomController *dogcomController;
66 |
67 | // 设置托盘中的注销按钮的可用性
68 | void DisableLogOutButton(bool yes);
69 |
70 | // 窗口菜单
71 | QAction *aboutAction;
72 | QMenu *windowMenu;
73 | void AboutDrcom();
74 |
75 | // 托盘图标
76 | QAction *restartAction;
77 | QAction *restoreAction;
78 | QAction *logOutAction;
79 | QAction *quitAction;
80 | QSystemTrayIcon *trayIcon;
81 | QMenu *trayIconMenu;
82 | void CreateActions();
83 | void CreateTrayIcon();
84 | void SetIcon(bool online);
85 |
86 | void GetInputs();
87 | void LoadSettings();
88 | void SaveSettings();
89 | void SetMAC(const QString &m);
90 | void SetDisableInput(bool yes);
91 | };
92 |
93 | #endif // MAINWINDOW_H
94 |
--------------------------------------------------------------------------------
/DrCOM_JLU_Qt.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2019-03-03T13:30:28
4 | #
5 | #-------------------------------------------------
6 |
7 | QT += core gui network widgets
8 | RC_ICONS = images/icon.ico
9 |
10 | # translations
11 | TRANSLATIONS += ts/DrCOM_zh_CN.ts
12 |
13 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
14 |
15 | TARGET = DrCOM_JLU_Qt
16 | TEMPLATE = app
17 |
18 | # The following define makes your compiler emit warnings if you use
19 | # any feature of Qt which has been marked as deprecated (the exact warnings
20 | # depend on your compiler). Please consult the documentation of the
21 | # deprecated API in order to know how to port your code away from it.
22 | DEFINES += QT_DEPRECATED_WARNINGS
23 |
24 | # You can also make your code fail to compile if you use deprecated APIs.
25 | # In order to do so, uncomment the following line.
26 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
27 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
28 |
29 | CONFIG += c++11
30 |
31 | SOURCES += \
32 | encrypt/EncryptData.cpp \
33 | main.cpp \
34 | mainwindow.cpp \
35 | dogcomcontroller.cpp \
36 | interruptiblesleeper.cpp \
37 | dogcom.cpp \
38 | encrypt/md4.cpp \
39 | encrypt/md5.cpp \
40 | encrypt/sha1.cpp \
41 | DogcomSocket.cpp
42 |
43 | HEADERS += \
44 | encrypt/EncryptData.h \
45 | mainwindow.h \
46 | dogcomcontroller.h \
47 | constants.h \
48 | interruptiblesleeper.h \
49 | dogcom.h \
50 | encrypt/md4.h \
51 | encrypt/md5.h \
52 | encrypt/sha1.h \
53 | DogcomSocket.h
54 |
55 | FORMS += \
56 | mainwindow.ui
57 |
58 | # Default rules for deployment.
59 | qnx: target.path = /tmp/$${TARGET}/bin
60 | else: unix:!android: target.path = /opt/$${TARGET}/bin
61 | !isEmpty(target.path): INSTALLS += target
62 |
63 | RESOURCES += \
64 | DrCOM_JLU_Qt.qrc
65 |
66 | # Single Application implementation
67 | include(singleinstance/singleapplication.pri)
68 | DEFINES += QAPPLICATION_CLASS=QApplication
69 |
70 | VERSION = 1.0.0.6
71 |
72 | win32:LIBS += -lwsock32
73 | win32:LIBS += -lcrypt32
74 | # 更新日志:
75 | # v 0.0.0.0 实现基本功能
76 | # v 1.0.0.1 修复适配高DPI时只窗口大小适配但字号不适配的bug
77 | # v 1.0.0.2 增加重启功能(能解决一些网络的错误
78 | # 调整字体为微软雅黑10号(就是win下正常的字体
79 | # v 1.0.0.3 没有这个版本,上次该发布0.2版本时候压缩包名字打错了。。。应该为1.0.0.2的,所以跳过这个版本号
80 | # v 1.0.0.4 优化用户体验,调整掉线时的提示信息,增加掉线时直接重启客户端的提示
81 | # v 1.0.0.5 解决不稳定的bug,自动重启客户端重新登录,新增日志功能,方便查错
82 | # v 1.0.0.6 更换QUdpSocket为win和linux原生接口,增强稳定性(并不知道为什么QUdpSocket要设置一个状态
83 | # 增加了两个checkbox可选关闭校园网之窗和隐藏登录窗口
84 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | static QString timePoint;
10 |
11 | //日志生成
12 | void LogMsgOutput(QtMsgType type,
13 | const QMessageLogContext&,
14 | const QString& msg)
15 | {
16 | static QMutex mutex; //日志代码互斥锁
17 |
18 | // 持有锁
19 | mutex.lock();
20 |
21 | // Critical Resource of Code
22 | QByteArray localMsg = msg.toLocal8Bit();
23 | QString log;
24 |
25 | switch (type) {
26 | case QtDebugMsg:
27 | log.append(QString("Debug %1")
28 | .arg(localMsg.constData()));
29 | break;
30 | case QtInfoMsg:
31 | log.append(QString("Info: %1")
32 | .arg(localMsg.constData()));
33 | break;
34 | case QtWarningMsg:
35 | log.append(QString("Warning: %1")
36 | .arg(localMsg.constData()));
37 | break;
38 | case QtCriticalMsg:
39 | log.append(QString("Critical: %1")
40 | .arg(localMsg.constData()));
41 | break;
42 | case QtFatalMsg:
43 | log.append(QString("Fatal: %1")
44 | .arg(localMsg.constData()));
45 | break;
46 | }
47 |
48 | QDir dir(QApplication::applicationDirPath());
49 | dir.mkdir("logs");
50 | QFile file(dir.path() + QString("/logs/log%1.lgt").arg(timePoint));
51 | file.open(QIODevice::WriteOnly | QIODevice::Append);
52 | QTextStream out(&file);
53 | out << log << Qt::endl;
54 | file.close();
55 |
56 | // 释放锁
57 | mutex.unlock();
58 | }
59 |
60 | int main(int argc, char *argv[])
61 | {
62 |
63 | Q_INIT_RESOURCE(DrCOM_JLU_Qt);
64 | SingleApplication a(argc, argv);
65 |
66 | //release模式输出日志到文件
67 | // 因为调用了QApplication::applicationDirPath()
68 | // 要在QApplication实例化之后调用
69 | #ifndef QT_DEBUG
70 | timePoint = QDateTime::currentDateTime().toString("yyyyMMddHHmmss");
71 | qInstallMessageHandler(LogMsgOutput);
72 | #endif
73 |
74 | SingleApplication::setQuitOnLastWindowClosed(false);
75 | // SingleApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
76 |
77 | qDebug() << "...main...";
78 |
79 | QFont font = a.font();
80 | font.setPointSize(10);
81 | font.setFamily("Microsoft YaHei");
82 | a.setFont(font);
83 |
84 | QTranslator translator;
85 | if (!translator.load(":/ts/DrCOM_zh_CN.qm")) {
86 | qWarning() << "Failed to load translation file!";
87 | }
88 | a.installTranslator(&translator);
89 |
90 | MainWindow w(&a);
91 | QObject::connect(&a, &SingleApplication::instanceStarted, [&w]() {
92 | qDebug() << "One instance had started. Its window will be shown by the next line of the source code.";
93 | w.ShowLoginWindow();
94 | });
95 |
96 | QSettings s(SETTINGS_FILE_NAME);
97 | bool bHideWindow=s.value(ID_HIDE_WINDOW, false).toBool();
98 | // 如果是软件自行重启的就不显示窗口
99 | int restartTimes = s.value(ID_RESTART_TIMES, 0).toInt();
100 | qDebug() << "main: restartTimes=" << restartTimes;
101 | if(bHideWindow){
102 | qDebug()<<"not show window caused by user settings";
103 | } else if (restartTimes > 0) {
104 | // 是自行重启不显示窗口
105 | qDebug()<<"not show window caused by self restart";
106 | } else {
107 | qDebug() << "show caused by normal start";
108 | w.show();
109 | }
110 | return a.exec();
111 | }
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # drcom-jlu-qt
2 | drcom for jlu in qt cross platform
3 |
4 | 跨平台 **win linux** [下载链接](https://github.com/code4lala/drcom-jlu-qt/releases)
5 | **macOS** [下载链接](https://github.com/jsy061030/drcom-jlu-qt-macOS/releases)
6 |
7 | # 功能对比
8 | | 功能 | 官方 | 本版 | 说明 |
9 | |----------------------|------|------|------------------------------------------------------------------|
10 | | 记住密码 自动登录 | √ | √ | |
11 | | 密文保存密码 | √ | √ | Windows平台上采用 Windows 提供的 数据保护 API(DPAPI),保护仅当前账户能够解密数据,其他平台暂时使用简单的XOR加密 |
12 | | <<<已知问题>>> | | | |
13 | | 多语言支持 | √ | | 或许不会改进了... |
14 | | 被顶掉 | √ | | 警告!巨大缺陷!本版掉线后会自动重启重新登录!所以顶不掉!待改进 |
15 | | 释放socket | √ | | 不是每次关机前都能保证释放socket,导致有时候会报端口已占用错误,待改进 |
16 | | <<<优势>>> | | | |
17 | | 打开速度 | 慢 | 快 | 我也不知道为什么官版打开那么慢 |
18 | | 单实例 | | √ | 开机自启慢的话可以直接打开不会报错说已经在运行 |
19 | | 快速注销 | | √ | 官方版是真·注销,本版是直接关闭socket,所以不需要等20s的发包周期 |
20 | | 托盘图标无bug | | √ | 不知道你们有没有碰到过官方win版托盘有俩图标的bug |
21 | | 可选不弹出校园网之窗 | | √ | |
22 | | 完全隐藏登录窗口 | | √ | |
23 | | 适配高分屏 | | √ | |
24 | | 快速重启客户端 | | √ | 有时候重启功能不好使,点了重启当前退了没有蹦出来新的,待改进 |
25 | | win版不需要管理员 | | √ | |
26 | | linux版最小化到托盘 | | √ | |
27 | | linux版不需要root | | √ | |
28 | | 不限制NAT | | √ | 并不支持有违校方意愿的做法,请自行承担后果 |
29 |
30 | # 注意事项
31 | - 掉线后客户端自动重启重连尝试三次。自动重启登录成功后不弹窗口只最小化到托盘。注:自动重启功能依赖于“记住我”选项的勾选,否则没有账户密码自行重启也并没有什么用
32 | - 连接JLU.PC登录的时候mac地址随便填就可以,或者随便选一个网卡也可以,只有有线网要求mac地址和网络中心的一致
33 | - macOS上,可能会面临“未认证的开发者”之类的提示,这时打开系统,打开安全性与隐私,找到类似“仍要打开”类似的按钮按下,再次打开此app,即可运行。
34 |
35 | # 截图
36 | > WIN:
37 |
38 | 
39 |
40 | > UBUNTU:
41 |
42 | 
43 |
44 | > Ubuntu 18不显示托盘图标的bug的解决方案:
45 | > [https://askubuntu.com/questions/1056226/ubuntu-budgie-18-04-lts-system-tray-icons-not-all-showing](https://askubuntu.com/questions/1056226/ubuntu-budgie-18-04-lts-system-tray-icons-not-all-showing)
46 |
47 | # 感谢
48 |
49 | **图标作者**
50 | > [https://github.com/lyj3516](https://github.com/lyj3516)
51 |
52 | **jlu的drcom协议细节**
53 | > [https://github.com/drcoms/jlu-drcom-client/blob/master/jlu-drcom-java/jlu-drcom-protocol.md](https://github.com/drcoms/jlu-drcom-client/blob/master/jlu-drcom-java/jlu-drcom-protocol.md)
54 |
55 | **唯一实例**
56 | > [https://github.com/itay-grudev/SingleApplication](https://github.com/itay-grudev/SingleApplication)
57 |
58 | # 特别感谢
59 | **登录部分复制了jlu部分代码**
60 | > [https://github.com/mchome/dogcom](https://github.com/mchome/dogcom)
61 |
62 | # 许可证
63 |
64 | [GNU Affero General Public License v3.0](https://github.com/code4lala/drcom-jlu-qt/blob/master/LICENSE)
65 |
--------------------------------------------------------------------------------
/singleinstance/singleapplication_p.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) Itay Grudev 2015 - 2016
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | //
24 | // W A R N I N G !!!
25 | // -----------------
26 | //
27 | // This file is not part of the SingleApplication API. It is used purely as an
28 | // implementation detail. This header file may change from version to
29 | // version without notice, or may even be removed.
30 | //
31 |
32 | #ifndef SINGLEAPPLICATION_P_H
33 | #define SINGLEAPPLICATION_P_H
34 |
35 | #include
36 | #include
37 | #include
38 | #include "singleapplication.h"
39 |
40 | struct InstancesInfo {
41 | bool primary;
42 | quint32 secondary;
43 | qint64 primaryPid;
44 | quint16 checksum;
45 | };
46 |
47 | struct ConnectionInfo {
48 | explicit ConnectionInfo() :
49 | msgLen(0), instanceId(0), stage(0) {}
50 | qint64 msgLen;
51 | quint32 instanceId;
52 | quint8 stage;
53 | };
54 |
55 | class SingleApplicationPrivate : public QObject {
56 | Q_OBJECT
57 | public:
58 | enum ConnectionType : quint8 {
59 | InvalidConnection = 0,
60 | NewInstance = 1,
61 | SecondaryInstance = 2,
62 | Reconnect = 3
63 | };
64 | enum ConnectionStage : quint8 {
65 | StageHeader = 0,
66 | StageBody = 1,
67 | StageConnected = 2,
68 | };
69 | Q_DECLARE_PUBLIC(SingleApplication)
70 |
71 | SingleApplicationPrivate( SingleApplication *q_ptr );
72 | ~SingleApplicationPrivate();
73 |
74 | void genBlockServerName();
75 | void initializeMemoryBlock();
76 | void startPrimary();
77 | void startSecondary();
78 | void connectToPrimary(int msecs, ConnectionType connectionType );
79 | quint16 blockChecksum();
80 | qint64 primaryPid();
81 | void readInitMessageHeader(QLocalSocket *socket);
82 | void readInitMessageBody(QLocalSocket *socket);
83 |
84 | SingleApplication *q_ptr;
85 | QSharedMemory *memory;
86 | QLocalSocket *socket;
87 | QLocalServer *server;
88 | quint32 instanceNumber;
89 | QString blockServerName;
90 | SingleApplication::Options options;
91 | QMap connectionMap;
92 |
93 | public Q_SLOTS:
94 | void slotConnectionEstablished();
95 | void slotDataAvailable( QLocalSocket*, quint32 );
96 | void slotClientConnectionClosed( QLocalSocket*, quint32 );
97 | };
98 |
99 | #endif // SINGLEAPPLICATION_P_H
100 |
--------------------------------------------------------------------------------
/singleinstance/singleapplication.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) Itay Grudev 2015 - 2018
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #ifndef SINGLE_APPLICATION_H
24 | #define SINGLE_APPLICATION_H
25 |
26 | #include
27 | #include
28 |
29 | #ifndef QAPPLICATION_CLASS
30 | #define QAPPLICATION_CLASS QCoreApplication
31 | #endif
32 |
33 | #include QT_STRINGIFY(QAPPLICATION_CLASS)
34 |
35 | class SingleApplicationPrivate;
36 |
37 | /**
38 | * @brief The SingleApplication class handles multipe instances of the same
39 | * Application
40 | * @see QCoreApplication
41 | */
42 | class SingleApplication : public QAPPLICATION_CLASS
43 | {
44 | Q_OBJECT
45 |
46 | typedef QAPPLICATION_CLASS app_t;
47 |
48 | public:
49 | /**
50 | * @brief Mode of operation of SingleApplication.
51 | * Whether the block should be user-wide or system-wide and whether the
52 | * primary instance should be notified when a secondary instance had been
53 | * started.
54 | * @note Operating system can restrict the shared memory blocks to the same
55 | * user, in which case the User/System modes will have no effect and the
56 | * block will be user wide.
57 | * @enum
58 | */
59 | enum Mode {
60 | User = 1 << 0,
61 | System = 1 << 1,
62 | SecondaryNotification = 1 << 2,
63 | ExcludeAppVersion = 1 << 3,
64 | ExcludeAppPath = 1 << 4
65 | };
66 | Q_DECLARE_FLAGS(Options, Mode)
67 |
68 | /**
69 | * @brief Intitializes a SingleApplication instance with argc command line
70 | * arguments in argv
71 | * @arg {int &} argc - Number of arguments in argv
72 | * @arg {const char *[]} argv - Supplied command line arguments
73 | * @arg {bool} allowSecondary - Whether to start the instance as secondary
74 | * if there is already a primary instance.
75 | * @arg {Mode} mode - Whether for the SingleApplication block to be applied
76 | * User wide or System wide.
77 | * @arg {int} timeout - Timeout to wait in miliseconds.
78 | * @note argc and argv may be changed as Qt removes arguments that it
79 | * recognizes
80 | * @note Mode::SecondaryNotification only works if set on both the primary
81 | * instance and the secondary instance.
82 | * @note The timeout is just a hint for the maximum time of blocking
83 | * operations. It does not guarantee that the SingleApplication
84 | * initialisation will be completed in given time, though is a good hint.
85 | * Usually 4*timeout would be the worst case (fail) scenario.
86 | * @see See the corresponding QAPPLICATION_CLASS constructor for reference
87 | */
88 | explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
89 | ~SingleApplication();
90 |
91 | /**
92 | * @brief Returns if the instance is the primary instance
93 | * @returns {bool}
94 | */
95 | bool isPrimary();
96 |
97 | /**
98 | * @brief Returns if the instance is a secondary instance
99 | * @returns {bool}
100 | */
101 | bool isSecondary();
102 |
103 | /**
104 | * @brief Returns a unique identifier for the current instance
105 | * @returns {qint32}
106 | */
107 | quint32 instanceId();
108 |
109 | /**
110 | * @brief Returns the process ID (PID) of the primary instance
111 | * @returns {qint64}
112 | */
113 | qint64 primaryPid();
114 |
115 | /**
116 | * @brief Sends a message to the primary instance. Returns true on success.
117 | * @param {int} timeout - Timeout for connecting
118 | * @returns {bool}
119 | * @note sendMessage() will return false if invoked from the primary
120 | * instance.
121 | */
122 | bool sendMessage( QByteArray message, int timeout = 100 );
123 |
124 | Q_SIGNALS:
125 | void instanceStarted();
126 | void receivedMessage( quint32 instanceId, QByteArray message );
127 |
128 | private:
129 | SingleApplicationPrivate *d_ptr;
130 | Q_DECLARE_PRIVATE(SingleApplication)
131 | };
132 |
133 | Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
134 |
135 | #endif // SINGLE_APPLICATION_H
136 |
--------------------------------------------------------------------------------
/DogcomSocket.cpp:
--------------------------------------------------------------------------------
1 | #ifdef WIN32
2 | #include
3 | #else
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #endif
10 | #include
11 | #include
12 | #include
13 | #include "DogcomSocket.h"
14 |
15 | using std::string;
16 |
17 | char DogcomSocket::AUTH_SERVER[20] = "10.100.61.3";
18 |
19 | DogcomSocket::DogcomSocket() {
20 | }
21 |
22 | void DogcomSocket::init(){
23 | int r;
24 | #ifdef WIN32
25 | WORD sockVersion = MAKEWORD(2, 2);
26 | WSADATA wsadata;
27 | if ((r = WSAStartup(sockVersion, &wsadata)) != 0) {
28 | throw DogcomSocketException(DogcomError::WSA_START_UP, r);
29 | }
30 | #endif
31 |
32 | memset(&bind_addr, 0, sizeof(bind_addr));
33 | bind_addr.sin_family = AF_INET;
34 | #ifdef WIN32
35 | bind_addr.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");
36 | #else
37 | bind_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
38 | #endif
39 | bind_addr.sin_port = htons(BIND_PORT);
40 |
41 | memset(&dest_addr, 0, sizeof(dest_addr));
42 | dest_addr.sin_family = AF_INET;
43 | #ifdef WIN32
44 | dest_addr.sin_addr.S_un.S_addr = inet_addr(AUTH_SERVER);
45 | #else
46 | dest_addr.sin_addr.s_addr = inet_addr(AUTH_SERVER);
47 | #endif
48 | dest_addr.sin_port = htons(DEST_PORT);
49 |
50 | srand(time(nullptr));
51 |
52 | // create socket
53 | if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
54 | #ifdef WIN32
55 | throw DogcomSocketException(DogcomError::SOCKET, WSAGetLastError());
56 | #else
57 | throw DogcomSocketException(DogcomError::SOCKET,sockfd);
58 | #endif
59 | }
60 |
61 | // bind socket
62 | if (bind(sockfd, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) < 0) {
63 | #ifdef WIN32
64 | throw DogcomSocketException(DogcomError::BIND, WSAGetLastError());
65 | #else
66 | throw DogcomSocketException(DogcomError::BIND,sockfd);
67 | #endif
68 | }
69 |
70 | // set timeout
71 | #ifdef WIN32
72 | int timeout = 3000;
73 | #else
74 | struct timeval timeout;
75 | timeout.tv_sec = 3;
76 | timeout.tv_usec = 0;
77 | #endif
78 | if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout,
79 | sizeof(timeout)) < 0) {
80 | #ifdef WIN32
81 | throw DogcomSocketException(DogcomError::SET_SOCK_OPT_TIMEOUT,
82 | WSAGetLastError());
83 | #else
84 | throw DogcomSocketException(DogcomError::SET_SOCK_OPT_TIMEOUT,
85 | sockfd);
86 | #endif
87 | }
88 |
89 | //set port reuse
90 |
91 | int optval = 1;
92 | #ifdef WIN32
93 | if ((r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof(optval))) < 0) {
94 | throw DogcomSocketException(DogcomError::SET_SOCK_OPT_REUSE, r);
95 | }
96 | #else
97 | if ((r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof(optval))) < 0) {
98 | throw DogcomSocketException(DogcomError::SET_SOCK_OPT_REUSE,r);
99 | }
100 |
101 | #ifdef SO_REUSEPORT
102 | if ((r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char *) &optval, sizeof(optval))) < 0) {
103 | throw DogcomSocketException(DogcomError::SET_SOCK_OPT_REUSE,r);
104 | }
105 | #endif
106 | #endif
107 | }
108 |
109 | int DogcomSocket::write(const char *buf, int len) {
110 | return sendto(sockfd, buf, len, 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr));
111 | }
112 |
113 | int DogcomSocket::read(char *buf) {
114 | #ifdef WIN32
115 | int addrlen = sizeof(dest_addr);
116 | #else
117 | socklen_t addrlen=sizeof(dest_addr);
118 | #endif
119 | return recvfrom(sockfd, buf, 1024, 0, (struct sockaddr *) &dest_addr, &addrlen);
120 | }
121 |
122 | DogcomSocket::~DogcomSocket() {
123 | qDebug()<<"DogcomSocket destructor";
124 | #ifdef WIN32
125 | if (sockfd != -1)
126 | closesocket(sockfd);
127 | WSACleanup();
128 | #else
129 | if (sockfd != -1)
130 | close(sockfd);
131 | #endif
132 | qDebug()<<"socket closed";
133 | }
134 |
135 | const char *DogcomSocketException::what() const noexcept {
136 | static char buf[1024];
137 | switch (errCode) {
138 | case DogcomError::WSA_START_UP:
139 | snprintf(buf, sizeof(buf),
140 | "WSAStartup failed. Error code: %d",
141 | realErrCode);
142 | break;
143 | case DogcomError::SOCKET:
144 | snprintf(buf, sizeof(buf),
145 | "socket failed. Error code: %d",
146 | realErrCode);
147 | break;
148 | case DogcomError::BIND:
149 | snprintf(buf, sizeof(buf),
150 | "bind failed. Error code: %d",
151 | realErrCode);
152 | break;
153 | case DogcomError::SET_SOCK_OPT_TIMEOUT:
154 | snprintf(buf,sizeof(buf),
155 | "timeout failed. Error code: %d",
156 | realErrCode);
157 | break;
158 | case DogcomError::SET_SOCK_OPT_REUSE:
159 | snprintf(buf, sizeof(buf),
160 | "port reuse failed. Error code: %d",
161 | realErrCode);
162 | break;
163 | }
164 | return buf;
165 | }
166 |
167 | DogcomSocketException::DogcomSocketException(int errCode, int realErrCode)
168 | : errCode(errCode), realErrCode(realErrCode) {
169 | }
170 |
--------------------------------------------------------------------------------
/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 250
10 | 237
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | DrCOM JLU Qt
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 0
31 | 0
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Lucida Sans Typewriter
47 |
48 |
49 |
50 | QLineEdit::EchoMode::Password
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | MAC
61 |
62 |
63 | Qt::AlignmentFlag::AlignCenter
64 |
65 |
66 |
67 |
68 |
69 |
70 | Account
71 |
72 |
73 | Qt::AlignmentFlag::AlignCenter
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Password
87 |
88 |
89 | Qt::AlignmentFlag::AlignCenter
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | Remember
101 |
102 |
103 |
104 |
105 |
106 |
107 | AutoLogin
108 |
109 |
110 |
111 |
112 |
113 |
114 | NotShowWelcomePage
115 |
116 |
117 |
118 |
119 |
120 |
121 | HideLoginWindow
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | Login
131 |
132 |
133 |
134 |
135 |
136 |
137 | Your IP address will be shown here
138 |
139 |
140 | Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | lineEditAccount
154 | lineEditPass
155 | comboBoxMAC
156 | lineEditMAC
157 | checkBoxRemember
158 | checkBoxAutoLogin
159 | pushButtonLogin
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/singleinstance/singleapplication.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) Itay Grudev 2015 - 2018
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include
30 |
31 | #include "singleapplication.h"
32 | #include "singleapplication_p.h"
33 |
34 | /**
35 | * @brief Constructor. Checks and fires up LocalServer or closes the program
36 | * if another instance already exists
37 | * @param argc
38 | * @param argv
39 | * @param {bool} allowSecondaryInstances
40 | */
41 | SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
42 | : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
43 | {
44 | Q_D(SingleApplication);
45 |
46 | // Store the current mode of the program
47 | d->options = options;
48 |
49 | // Generating an application ID used for identifying the shared memory
50 | // block and QLocalServer
51 | d->genBlockServerName();
52 |
53 | #ifdef Q_OS_UNIX
54 | // By explicitly attaching it and then deleting it we make sure that the
55 | // memory is deleted even after the process has crashed on Unix.
56 | d->memory = new QSharedMemory( d->blockServerName );
57 | d->memory->attach();
58 | delete d->memory;
59 | #endif
60 | // Guarantee thread safe behaviour with a shared memory block.
61 | d->memory = new QSharedMemory( d->blockServerName );
62 |
63 | // Create a shared memory block
64 | if( d->memory->create( sizeof( InstancesInfo ) ) ) {
65 | // Initialize the shared memory block
66 | d->memory->lock();
67 | d->initializeMemoryBlock();
68 | d->memory->unlock();
69 | } else {
70 | // Attempt to attach to the memory segment
71 | if( ! d->memory->attach() ) {
72 | qCritical() << "SingleApplication: Unable to attach to shared memory block.";
73 | qCritical() << d->memory->errorString();
74 | delete d;
75 | ::exit( EXIT_FAILURE );
76 | }
77 | }
78 |
79 | InstancesInfo* inst = static_cast( d->memory->data() );
80 | QElapsedTimer time;
81 | time.start();
82 |
83 | // Make sure the shared memory block is initialised and in consistent state
84 | while( true ) {
85 | d->memory->lock();
86 |
87 | if( d->blockChecksum() == inst->checksum ) break;
88 |
89 | if( time.elapsed() > 5000 ) {
90 | qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
91 | d->initializeMemoryBlock();
92 | }
93 |
94 | d->memory->unlock();
95 |
96 | // Random sleep here limits the probability of a collision between two racing apps
97 | srand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max() );
98 | QThread::sleep( 8 + static_cast ( static_cast ( rand() ) / RAND_MAX * 10 ) );
99 | }
100 |
101 | if( inst->primary == false) {
102 | d->startPrimary();
103 | d->memory->unlock();
104 | return;
105 | }
106 |
107 | // Check if another instance can be started
108 | if( allowSecondary ) {
109 | inst->secondary += 1;
110 | inst->checksum = d->blockChecksum();
111 | d->instanceNumber = inst->secondary;
112 | d->startSecondary();
113 | if( d->options & Mode::SecondaryNotification ) {
114 | d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
115 | }
116 | d->memory->unlock();
117 | return;
118 | }
119 |
120 | d->memory->unlock();
121 |
122 | d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
123 |
124 | delete d;
125 |
126 | ::exit( EXIT_SUCCESS );
127 | }
128 |
129 | /**
130 | * @brief Destructor
131 | */
132 | SingleApplication::~SingleApplication()
133 | {
134 | Q_D(SingleApplication);
135 | delete d;
136 | }
137 |
138 | bool SingleApplication::isPrimary()
139 | {
140 | Q_D(SingleApplication);
141 | return d->server != nullptr;
142 | }
143 |
144 | bool SingleApplication::isSecondary()
145 | {
146 | Q_D(SingleApplication);
147 | return d->server == nullptr;
148 | }
149 |
150 | quint32 SingleApplication::instanceId()
151 | {
152 | Q_D(SingleApplication);
153 | return d->instanceNumber;
154 | }
155 |
156 | qint64 SingleApplication::primaryPid()
157 | {
158 | Q_D(SingleApplication);
159 | return d->primaryPid();
160 | }
161 |
162 | bool SingleApplication::sendMessage( QByteArray message, int timeout )
163 | {
164 | Q_D(SingleApplication);
165 |
166 | // Nobody to connect to
167 | if( isPrimary() ) return false;
168 |
169 | // Make sure the socket is connected
170 | d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect );
171 |
172 | d->socket->write( message );
173 | bool dataWritten = d->socket->flush();
174 | d->socket->waitForBytesWritten( timeout );
175 | return dataWritten;
176 | }
177 |
--------------------------------------------------------------------------------
/singleinstance/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | __3.0.14__
5 | ----------
6 |
7 | * Fixed uninitialised variables in the `SingleApplicationPrivate` constructor.
8 |
9 | __3.0.13a__
10 | ----------
11 |
12 | * Process socket events asynchronously
13 | * Fix undefined variable error on Windows
14 |
15 | _Francis Giraldeau_
16 |
17 | __3.0.12a__
18 | ----------
19 |
20 | * Removed signal handling.
21 |
22 | __3.0.11a__
23 | ----------
24 |
25 | * Fixed bug where the message sent by the second process was not received
26 | correctly when the message is sent immediately following a connection.
27 |
28 | _Francis Giraldeau_
29 |
30 | * Refactored code and implemented shared memory block consistency checks
31 | via `qChecksum()` (CRC-16).
32 | * Explicit `qWarning` and `qCritical` when the library is unable to initialise
33 | correctly.
34 |
35 | __3.0.10__
36 | ----------
37 |
38 | * Removed C style casts and eliminated all clang warnings. Fixed `instanceId`
39 | reading from only one byte in the message deserialization. Cleaned up
40 | serialization code using `QDataStream`. Changed connection type to use
41 | `quint8 enum` rather than `char`.
42 | * Renamed `SingleAppConnectionType` to `ConnectionType`. Added initialization
43 | values to all `ConnectionType` enum cases.
44 |
45 | _Jedidiah Buck McCready_
46 |
47 | __3.0.9__
48 | ---------
49 |
50 | * Added SingleApplicationPrivate::primaryPid() as a solution to allow
51 | bringing the primary window of an application to the foreground on
52 | Windows.
53 |
54 | _Eelco van Dam from Peacs BV_
55 |
56 | __3.0.8__
57 | ---------
58 |
59 | * Bug fix - changed QApplication::instance() to QCoreApplication::instance()
60 |
61 | _Evgeniy Bazhenov_
62 |
63 | __3.0.7a__
64 | ----------
65 |
66 | * Fixed compilation error with Mingw32 in MXE thanks to Vitaly Tonkacheyev.
67 | * Removed QMutex used for thread safe behaviour. The implementation now uses
68 | QCoreApplication::instance() to get an instance to SingleApplication for
69 | memory deallocation.
70 |
71 | __3.0.6a__
72 | ----------
73 |
74 | * Reverted GetUserName API usage on Windows. Fixed bug with missing library.
75 | * Fixed bug in the Calculator example, preventing it's window to be raised
76 | on Windows.
77 |
78 | Special thanks to Charles Gunawan.
79 |
80 | __3.0.5a__
81 | ----------
82 |
83 | * Fixed a memory leak in the SingleApplicationPrivate destructor.
84 |
85 | _Sergei Moiseev_
86 |
87 | __3.0.4a__
88 | ----------
89 |
90 | * Fixed shadow and uninitialised variable warnings.
91 |
92 | _Paul Walmsley_
93 |
94 | __3.0.3a__
95 | ----------
96 |
97 | * Removed Microsoft Windows specific code for getting username due to
98 | multiple problems and compiler differences on Windows platforms. On
99 | Windows the shared memory block in User mode now includes the user's
100 | home path (which contains the user's username).
101 |
102 | * Explicitly getting absolute path of the user's home directory as on Unix
103 | a relative path (`~`) may be returned.
104 |
105 | __3.0.2a__
106 | ----------
107 |
108 | * Fixed bug on Windows when username containing wide characters causes the
109 | library to crash.
110 |
111 | _Le Liu_
112 |
113 | __3.0.1a__
114 | ----------
115 |
116 | * Allows the application path and version to be excluded from the server name
117 | hash. The following flags were added for this purpose:
118 | * `SingleApplication::Mode::ExcludeAppVersion`
119 | * `SingleApplication::Mode::ExcludeAppPath`
120 | * Allow a non elevated process to connect to a local server created by an
121 | elevated process run by the same user on Windows
122 | * Fixes a problem with upper case letters in paths on Windows
123 |
124 | _Le Liu_
125 |
126 | __v3.0a__
127 | ---------
128 |
129 | * Depricated secondary instances count.
130 | * Added a sendMessage() method to send a message to the primary instance.
131 | * Added a receivedMessage() signal, emitted when a message is received from a
132 | secondary instance.
133 | * The SingleApplication constructor's third parameter is now a bool
134 | specifying if the current instance should be allowed to run as a secondary
135 | instance if there is already a primary instance.
136 | * The SingleApplication constructor accept a fourth parameter specifying if
137 | the SingleApplication block should be User-wide or System-wide.
138 | * SingleApplication no longer relies on `applicationName` and
139 | `organizationName` to be set. It instead concatenates all of the following
140 | data and computes a `SHA256` hash which is used as the key of the
141 | `QSharedMemory` block and the `QLocalServer`. Since at least
142 | `applicationFilePath` is always present there is no need to explicitly set
143 | any of the following prior to initialising `SingleApplication`.
144 | * `QCoreApplication::applicationName`
145 | * `QCoreApplication::applicationVersion`
146 | * `QCoreApplication::applicationFilePath`
147 | * `QCoreApplication::organizationName`
148 | * `QCoreApplication::organizationDomain`
149 | * User name or home directory path if in User mode
150 | * The primary instance is no longer notified when a secondary instance had
151 | been started by default. A `Mode` flag for this feature exists.
152 | * Added `instanceNumber()` which represents a unique identifier for each
153 | secondary instance started. When called from the primary instance will
154 | return `0`.
155 |
156 | __v2.4__
157 | --------
158 |
159 | * Stability improvements
160 | * Support for secondary instances.
161 | * The library now recovers safely after the primary process has crashed
162 | and the shared memory had not been deleted.
163 |
164 | __v2.3__
165 | --------
166 |
167 | * Improved pimpl design and inheritance safety.
168 |
169 | _Vladislav Pyatnichenko_
170 |
171 | __v2.2__
172 | --------
173 |
174 | * The `QAPPLICATION_CLASS` macro can now be defined in the file including the
175 | Single Application header or with a `DEFINES+=` statement in the project file.
176 |
177 | __v2.1__
178 | --------
179 |
180 | * A race condition can no longer occur when starting two processes nearly
181 | simultaneously.
182 |
183 | Fix issue [#3](https://github.com/itay-grudev/SingleApplication/issues/3)
184 |
185 | __v2.0__
186 | --------
187 |
188 | * SingleApplication is now being passed a reference to `argc` instead of a
189 | copy.
190 |
191 | Fix issue [#1](https://github.com/itay-grudev/SingleApplication/issues/1)
192 |
193 | * Improved documentation.
194 |
--------------------------------------------------------------------------------
/encrypt/sha1.cpp:
--------------------------------------------------------------------------------
1 |
2 | /* from valgrind tests */
3 |
4 | /* ================ sha1.c ================ */
5 | /*
6 | SHA-1 in C
7 | By Steve Reid
8 | 100% Public Domain
9 |
10 | Test Vectors (from FIPS PUB 180-1)
11 | "abc"
12 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
13 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
14 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
15 | A million repetitions of "a"
16 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
17 | */
18 |
19 | /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
20 | /* #define SHA1HANDSOFF * Copies data before messing with it. */
21 |
22 | #define SHA1HANDSOFF
23 |
24 | #include
25 | #include
26 | #include
27 | #include "sha1.h"
28 |
29 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
30 |
31 | /* blk0() and blk() perform the initial expand. */
32 | /* I got the idea of expanding during the round function from SSLeay */
33 | #if BYTE_ORDER == LITTLE_ENDIAN
34 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
35 | |(rol(block->l[i],8)&0x00FF00FF))
36 | #elif BYTE_ORDER == BIG_ENDIAN
37 | #define blk0(i) block->l[i]
38 | #else
39 | #error "Endianness not defined!"
40 | #endif
41 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
42 | ^block->l[(i+2)&15]^block->l[i&15],1))
43 |
44 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
45 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
46 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
47 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
48 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
49 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
50 |
51 |
52 | /* Hash a single 512-bit block. This is the core of the algorithm. */
53 |
54 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
55 | {
56 | uint32_t a, b, c, d, e;
57 | typedef union {
58 | unsigned char c[64];
59 | uint32_t l[16];
60 | } CHAR64LONG16;
61 | #ifdef SHA1HANDSOFF
62 | CHAR64LONG16 block[1]; /* use array to appear as a pointer */
63 | memcpy(block, buffer, 64);
64 | #else
65 | /* The following had better never be used because it causes the
66 | * pointer-to-const buffer to be cast into a pointer to non-const.
67 | * And the result is written through. I threw a "const" in, hoping
68 | * this will cause a diagnostic.
69 | */
70 | CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
71 | #endif
72 | /* Copy context->state[] to working vars */
73 | a = state[0];
74 | b = state[1];
75 | c = state[2];
76 | d = state[3];
77 | e = state[4];
78 | /* 4 rounds of 20 operations each. Loop unrolled. */
79 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
80 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
81 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
82 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
83 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
84 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
85 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
86 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
87 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
88 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
89 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
90 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
91 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
92 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
93 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
94 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
95 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
96 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
97 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
98 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
99 | /* Add the working vars back into context.state[] */
100 | state[0] += a;
101 | state[1] += b;
102 | state[2] += c;
103 | state[3] += d;
104 | state[4] += e;
105 | /* Wipe variables */
106 | a = b = c = d = e = 0;
107 | #ifdef SHA1HANDSOFF
108 | memset(block, '\0', sizeof(block));
109 | #endif
110 | }
111 |
112 |
113 | /* SHA1Init - Initialize new context */
114 |
115 | void SHA1Init(SHA1_CTX* context)
116 | {
117 | /* SHA1 initialization constants */
118 | context->state[0] = 0x67452301;
119 | context->state[1] = 0xEFCDAB89;
120 | context->state[2] = 0x98BADCFE;
121 | context->state[3] = 0x10325476;
122 | context->state[4] = 0xC3D2E1F0;
123 | context->count[0] = context->count[1] = 0;
124 | }
125 |
126 |
127 | /* Run your data through this. */
128 |
129 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
130 | {
131 | uint32_t i, j;
132 |
133 | j = context->count[0];
134 | if ((context->count[0] += len << 3) < j)
135 | context->count[1]++;
136 | context->count[1] += (len>>29);
137 | j = (j >> 3) & 63;
138 | if ((j + len) > 63) {
139 | memcpy(&context->buffer[j], data, (i = 64-j));
140 | SHA1Transform(context->state, context->buffer);
141 | for ( ; i + 63 < len; i += 64) {
142 | SHA1Transform(context->state, &data[i]);
143 | }
144 | j = 0;
145 | }
146 | else i = 0;
147 | memcpy(&context->buffer[j], &data[i], len - i);
148 | }
149 |
150 |
151 | /* Add padding and return the message digest. */
152 |
153 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
154 | {
155 | unsigned i;
156 | unsigned char finalcount[8];
157 | unsigned char c;
158 |
159 | #if 0 /* untested "improvement" by DHR */
160 | /* Convert context->count to a sequence of bytes
161 | * in finalcount. Second element first, but
162 | * big-endian order within element.
163 | * But we do it all backwards.
164 | */
165 | unsigned char *fcp = &finalcount[8];
166 |
167 | for (i = 0; i < 2; i++)
168 | {
169 | uint32_t t = context->count[i];
170 | int j;
171 |
172 | for (j = 0; j < 4; t >>= 8, j++)
173 | *--fcp = (unsigned char) t;
174 | }
175 | #else
176 | for (i = 0; i < 8; i++) {
177 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
178 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
179 | }
180 | #endif
181 | c = 0200;
182 | SHA1Update(context, &c, 1);
183 | while ((context->count[0] & 504) != 448) {
184 | c = 0000;
185 | SHA1Update(context, &c, 1);
186 | }
187 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
188 | for (i = 0; i < 20; i++) {
189 | digest[i] = (unsigned char)
190 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
191 | }
192 | /* Wipe variables */
193 | memset(context, '\0', sizeof(*context));
194 | memset(&finalcount, '\0', sizeof(finalcount));
195 | }
196 | /* ================ end of sha1.c ================ */
197 |
198 | void SHA1(const unsigned char* data, uint32_t len, unsigned char digest[20]) {
199 | SHA1_CTX ctx;
200 | SHA1Init(&ctx);
201 | SHA1Update(&ctx, data, len);
202 | SHA1Final(digest, &ctx);
203 | }
--------------------------------------------------------------------------------
/singleinstance/README.md:
--------------------------------------------------------------------------------
1 | SingleApplication
2 | =================
3 |
4 | This is a replacement of the QtSingleApplication for `Qt5`.
5 |
6 | Keeps the Primary Instance of your Application and kills each subsequent
7 | instances. It can (if enabled) spawn secondary (non-related to the primary)
8 | instances and can send data to the primary instance from secondary instances.
9 |
10 | Usage
11 | -----
12 |
13 | The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
14 | class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
15 | default). Further usage is similar to the use of the `Q[Core|Gui]Application`
16 | classes.
17 |
18 | The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
19 | instance of your Application is your Primary Instance. It would check if the
20 | shared memory block exists and if not it will start a `QLocalServer` and listen
21 | for connections. Each subsequent instance of your application would check if the
22 | shared memory block exists and if it does, it will connect to the QLocalServer
23 | to notify the primary instance that a new instance had been started, after which
24 | it would terminate with status code `0`. In the Primary Instance
25 | `SingleApplication` would emit the `instanceStarted()` signal upon detecting
26 | that a new instance had been started.
27 |
28 | The library uses `stdlib` to terminate the program with the `exit()` function.
29 |
30 | You can use the library as if you use any other `QCoreApplication` derived
31 | class:
32 |
33 | ```cpp
34 | #include
35 | #include
36 |
37 | int main( int argc, char* argv[] )
38 | {
39 | SingleApplication app( argc, argv );
40 |
41 | return app.exec();
42 | }
43 | ```
44 |
45 | To include the library files I would recommend that you add it as a git
46 | submodule to your project and include it's contents with a `.pri` file. Here is
47 | how:
48 |
49 | ```bash
50 | git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
51 | ```
52 |
53 | Then include the `singleapplication.pri` file in your `.pro` project file. Also
54 | don't forget to specify which `QCoreApplication` class your app is using if it
55 | is not `QCoreApplication`.
56 |
57 | ```qmake
58 | include(singleapplication/singleapplication.pri)
59 | DEFINES += QAPPLICATION_CLASS=QApplication
60 | ```
61 |
62 | The `Instance Started` signal
63 | ------------------------
64 |
65 | The SingleApplication class implements a `instanceStarted()` signal. You can
66 | bind to that signal to raise your application's window when a new instance had
67 | been started, for example.
68 |
69 | ```cpp
70 | // window is a QWindow instance
71 | QObject::connect(
72 | &app,
73 | &SingleApplication::instanceStarted,
74 | &window,
75 | &QWindow::raise
76 | );
77 | ```
78 |
79 | Using `SingleApplication::instance()` is a neat way to get the
80 | `SingleApplication` instance for binding to it's signals anywhere in your
81 | program.
82 |
83 | __Note:__ On Windows the ability to bring the application windows to the
84 | foreground is restricted. See [Windows specific implementations](Windows.md)
85 | for a workaround and an example implementation.
86 |
87 |
88 | Secondary Instances
89 | -------------------
90 |
91 | If you want to be able to launch additional Secondary Instances (not related to
92 | your Primary Instance) you have to enable that with the third parameter of the
93 | `SingleApplication` constructor. The default is `false` meaning no Secondary
94 | Instances. Here is an example of how you would start a Secondary Instance send
95 | a message with the command line arguments to the primary instance and then shut
96 | down.
97 |
98 | ```cpp
99 | int main(int argc, char *argv[])
100 | {
101 | SingleApplication app( argc, argv, true );
102 |
103 | if( app.isSecondary() ) {
104 | app.sendMessage( app.arguments().join(' ')).toUtf8() );
105 | app.exit( 0 );
106 | }
107 |
108 | return app.exec();
109 | }
110 | ```
111 |
112 | *__Note:__ A secondary instance won't cause the emission of the
113 | `instanceStarted()` signal by default. See `SingleApplication::Mode` for more
114 | details.*
115 |
116 | You can check whether your instance is a primary or secondary with the following
117 | methods:
118 |
119 | ```cpp
120 | app.isPrimary();
121 | // or
122 | app.isSecondary();
123 | ```
124 |
125 | *__Note:__ If your Primary Instance is terminated a newly launched instance
126 | will replace the Primary one even if the Secondary flag has been set.*
127 |
128 | API
129 | ---
130 |
131 | ### Members
132 |
133 | ```cpp
134 | SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
135 | ```
136 |
137 | Depending on whether `allowSecondary` is set, this constructor may terminate
138 | your app if there is already a primary instance running. Additional `Options`
139 | can be specified to set whether the SingleApplication block should work
140 | user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
141 | used to notify the primary instance whenever a secondary instance had been
142 | started (disabled by default). `timeout` specifies the maximum time in
143 | milliseconds to wait for blocking operations.
144 |
145 | *__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
146 | recognizes.*
147 |
148 | *__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
149 | and the secondary instance.*
150 |
151 | *__Note:__ Operating system can restrict the shared memory blocks to the same
152 | user, in which case the User/System modes will have no effect and the block will
153 | be user wide.*
154 |
155 | ---
156 |
157 | ```cpp
158 | bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
159 | ```
160 |
161 | Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
162 | in milliseconds for blocking functions
163 |
164 | ---
165 |
166 | ```cpp
167 | bool SingleApplication::isPrimary()
168 | ```
169 |
170 | Returns if the instance is the primary instance.
171 |
172 | ---
173 |
174 | ```cpp
175 | bool SingleApplication::isSecondary()
176 | ```
177 | Returns if the instance is a secondary instance.
178 |
179 | ---
180 |
181 | ```cpp
182 | quint32 SingleApplication::instanceId()
183 | ```
184 |
185 | Returns a unique identifier for the current instance.
186 |
187 | ---
188 |
189 | ```cpp
190 | qint64 SingleApplication::primaryPid()
191 | ```
192 |
193 | Returns the process ID (PID) of the primary instance.
194 |
195 | ### Signals
196 |
197 | ```cpp
198 | void SingleApplication::instanceStarted()
199 | ```
200 |
201 | Triggered whenever a new instance had been started, except for secondary
202 | instances if the `Mode::SecondaryNotification` flag is not specified.
203 |
204 | ---
205 |
206 | ```cpp
207 | void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
208 | ```
209 |
210 | Triggered whenever there is a message received from a secondary instance.
211 |
212 | ---
213 |
214 | ### Flags
215 |
216 | ```cpp
217 | enum SingleApplication::Mode
218 | ```
219 |
220 | * `Mode::User` - The SingleApplication block should apply user wide. This adds
221 | user specific data to the key used for the shared memory and server name.
222 | This is the default functionality.
223 | * `Mode::System` – The SingleApplication block applies system-wide.
224 | * `Mode::SecondaryNotification` – Whether to trigger `instanceStarted()` even
225 | whenever secondary instances are started.
226 | * `Mode::ExcludeAppPath` – Excludes the application path from the server name
227 | (and memory block) hash.
228 | * `Mode::ExcludeAppVersion` – Excludes the application version from the server
229 | name (and memory block) hash.
230 |
231 | *__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
232 | and the secondary instance.*
233 |
234 | *__Note:__ Operating system can restrict the shared memory blocks to the same
235 | user, in which case the User/System modes will have no effect and the block will
236 | be user wide.*
237 |
238 | ---
239 |
240 | Versioning
241 | ----------
242 |
243 | Each major version introduces either very significant changes or is not
244 | backwards compatible with the previous version. Minor versions only add
245 | additional features, bug fixes or performance improvements and are backwards
246 | compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
247 | more details.
248 |
249 | Implementation
250 | --------------
251 |
252 | The library is implemented with a QSharedMemory block which is thread safe and
253 | guarantees a race condition will not occur. It also uses a QLocalSocket to
254 | notify the main process that a new instance had been spawned and thus invoke the
255 | `instanceStarted()` signal and for messaging the primary instance.
256 |
257 | Additionally the library can recover from being forcefully killed on *nix
258 | systems and will reset the memory block given that there are no other
259 | instances running.
260 |
261 | License
262 | -------
263 | This library and it's supporting documentation are released under
264 | `The MIT License (MIT)` with the exception of the Qt calculator examples which
265 | is distributed under the BSD license.
266 |
--------------------------------------------------------------------------------
/encrypt/md4.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
3 | * MD4 Message-Digest Algorithm (RFC 1320).
4 | *
5 | * Homepage:
6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
7 | *
8 | * Author:
9 | * Alexander Peslyak, better known as Solar Designer
10 | *
11 | * This software was written by Alexander Peslyak in 2001. No copyright is
12 | * claimed, and the software is hereby placed in the public domain.
13 | * In case this attempt to disclaim copyright and place the software in the
14 | * public domain is deemed null and void, then the software is
15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
16 | * general public under the following terms:
17 | *
18 | * Redistribution and use in source and binary forms, with or without
19 | * modification, are permitted.
20 | *
21 | * There's ABSOLUTELY NO WARRANTY, express or implied.
22 | *
23 | * (This is a heavily cut-down "BSD license".)
24 | *
25 | * This differs from Colin Plumb's older public domain implementation in that
26 | * no exactly 32-bit integer data type is required (any 32-bit or wider
27 | * unsigned integer data type will do), there's no compile-time endianness
28 | * configuration, and the function prototypes match OpenSSL's. No code from
29 | * Colin Plumb's implementation has been reused; this comment merely compares
30 | * the properties of the two independent implementations.
31 | *
32 | * The primary goals of this implementation are portability and ease of use.
33 | * It is meant to be fast, but not as fast as possible. Some known
34 | * optimizations are not included to reduce source code size and avoid
35 | * compile-time configuration.
36 | */
37 |
38 | #ifndef HAVE_OPENSSL
39 |
40 | #include
41 |
42 | #include "md4.h"
43 |
44 | /*
45 | * The basic MD4 functions.
46 | *
47 | * F and G are optimized compared to their RFC 1320 definitions, with the
48 | * optimization for F borrowed from Colin Plumb's MD5 implementation.
49 | */
50 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
51 | #define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
52 | #define H(x, y, z) ((x) ^ (y) ^ (z))
53 |
54 | /*
55 | * The MD4 transformation for all three rounds.
56 | */
57 | #define STEP(f, a, b, c, d, x, s) \
58 | (a) += f((b), (c), (d)) + (x); \
59 | (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));
60 |
61 | /*
62 | * SET reads 4 input bytes in little-endian byte order and stores them in a
63 | * properly aligned word in host byte order.
64 | *
65 | * The check for little-endian architectures that tolerate unaligned memory
66 | * accesses is just an optimization. Nothing will break if it fails to detect
67 | * a suitable architecture.
68 | *
69 | * Unfortunately, this optimization may be a C strict aliasing rules violation
70 | * if the caller's data buffer has effective type that cannot be aliased by
71 | * MD4_u32plus. In practice, this problem may occur if these MD4 routines are
72 | * inlined into a calling function, or with future and dangerously advanced
73 | * link-time optimizations. For the time being, keeping these MD4 routines in
74 | * their own translation unit avoids the problem.
75 | */
76 | #if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
77 | #define SET(n) \
78 | (*(MD4_u32plus *)&ptr[(n) * 4])
79 | #define GET(n) \
80 | SET(n)
81 | #else
82 | #define SET(n) \
83 | (ctx->block[(n)] = \
84 | (MD4_u32plus)ptr[(n) * 4] | \
85 | ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
86 | ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
87 | ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
88 | #define GET(n) \
89 | (ctx->block[(n)])
90 | #endif
91 |
92 | /*
93 | * This processes one or more 64-byte data blocks, but does NOT update the bit
94 | * counters. There are no alignment requirements.
95 | */
96 | static const void *body(MD4_CTX *ctx, const void *data, unsigned long size)
97 | {
98 | const unsigned char *ptr;
99 | MD4_u32plus a, b, c, d;
100 | MD4_u32plus saved_a, saved_b, saved_c, saved_d;
101 | const MD4_u32plus ac1 = 0x5a827999, ac2 = 0x6ed9eba1;
102 |
103 | ptr = (const unsigned char *)data;
104 |
105 | a = ctx->a;
106 | b = ctx->b;
107 | c = ctx->c;
108 | d = ctx->d;
109 |
110 | do {
111 | saved_a = a;
112 | saved_b = b;
113 | saved_c = c;
114 | saved_d = d;
115 |
116 | /* Round 1 */
117 | STEP(F, a, b, c, d, SET(0), 3)
118 | STEP(F, d, a, b, c, SET(1), 7)
119 | STEP(F, c, d, a, b, SET(2), 11)
120 | STEP(F, b, c, d, a, SET(3), 19)
121 | STEP(F, a, b, c, d, SET(4), 3)
122 | STEP(F, d, a, b, c, SET(5), 7)
123 | STEP(F, c, d, a, b, SET(6), 11)
124 | STEP(F, b, c, d, a, SET(7), 19)
125 | STEP(F, a, b, c, d, SET(8), 3)
126 | STEP(F, d, a, b, c, SET(9), 7)
127 | STEP(F, c, d, a, b, SET(10), 11)
128 | STEP(F, b, c, d, a, SET(11), 19)
129 | STEP(F, a, b, c, d, SET(12), 3)
130 | STEP(F, d, a, b, c, SET(13), 7)
131 | STEP(F, c, d, a, b, SET(14), 11)
132 | STEP(F, b, c, d, a, SET(15), 19)
133 |
134 | /* Round 2 */
135 | STEP(G, a, b, c, d, GET(0) + ac1, 3)
136 | STEP(G, d, a, b, c, GET(4) + ac1, 5)
137 | STEP(G, c, d, a, b, GET(8) + ac1, 9)
138 | STEP(G, b, c, d, a, GET(12) + ac1, 13)
139 | STEP(G, a, b, c, d, GET(1) + ac1, 3)
140 | STEP(G, d, a, b, c, GET(5) + ac1, 5)
141 | STEP(G, c, d, a, b, GET(9) + ac1, 9)
142 | STEP(G, b, c, d, a, GET(13) + ac1, 13)
143 | STEP(G, a, b, c, d, GET(2) + ac1, 3)
144 | STEP(G, d, a, b, c, GET(6) + ac1, 5)
145 | STEP(G, c, d, a, b, GET(10) + ac1, 9)
146 | STEP(G, b, c, d, a, GET(14) + ac1, 13)
147 | STEP(G, a, b, c, d, GET(3) + ac1, 3)
148 | STEP(G, d, a, b, c, GET(7) + ac1, 5)
149 | STEP(G, c, d, a, b, GET(11) + ac1, 9)
150 | STEP(G, b, c, d, a, GET(15) + ac1, 13)
151 |
152 | /* Round 3 */
153 | STEP(H, a, b, c, d, GET(0) + ac2, 3)
154 | STEP(H, d, a, b, c, GET(8) + ac2, 9)
155 | STEP(H, c, d, a, b, GET(4) + ac2, 11)
156 | STEP(H, b, c, d, a, GET(12) + ac2, 15)
157 | STEP(H, a, b, c, d, GET(2) + ac2, 3)
158 | STEP(H, d, a, b, c, GET(10) + ac2, 9)
159 | STEP(H, c, d, a, b, GET(6) + ac2, 11)
160 | STEP(H, b, c, d, a, GET(14) + ac2, 15)
161 | STEP(H, a, b, c, d, GET(1) + ac2, 3)
162 | STEP(H, d, a, b, c, GET(9) + ac2, 9)
163 | STEP(H, c, d, a, b, GET(5) + ac2, 11)
164 | STEP(H, b, c, d, a, GET(13) + ac2, 15)
165 | STEP(H, a, b, c, d, GET(3) + ac2, 3)
166 | STEP(H, d, a, b, c, GET(11) + ac2, 9)
167 | STEP(H, c, d, a, b, GET(7) + ac2, 11)
168 | STEP(H, b, c, d, a, GET(15) + ac2, 15)
169 |
170 | a += saved_a;
171 | b += saved_b;
172 | c += saved_c;
173 | d += saved_d;
174 |
175 | ptr += 64;
176 | } while (size -= 64);
177 |
178 | ctx->a = a;
179 | ctx->b = b;
180 | ctx->c = c;
181 | ctx->d = d;
182 |
183 | return ptr;
184 | }
185 |
186 | void MD4_Init(MD4_CTX *ctx)
187 | {
188 | ctx->a = 0x67452301;
189 | ctx->b = 0xefcdab89;
190 | ctx->c = 0x98badcfe;
191 | ctx->d = 0x10325476;
192 |
193 | ctx->lo = 0;
194 | ctx->hi = 0;
195 | }
196 |
197 | void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
198 | {
199 | MD4_u32plus saved_lo;
200 | unsigned long used, available;
201 |
202 | saved_lo = ctx->lo;
203 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
204 | ctx->hi++;
205 | ctx->hi += size >> 29;
206 |
207 | used = saved_lo & 0x3f;
208 |
209 | if (used) {
210 | available = 64 - used;
211 |
212 | if (size < available) {
213 | memcpy(&ctx->buffer[used], data, size);
214 | return;
215 | }
216 |
217 | memcpy(&ctx->buffer[used], data, available);
218 | data = (const unsigned char *)data + available;
219 | size -= available;
220 | body(ctx, ctx->buffer, 64);
221 | }
222 |
223 | if (size >= 64) {
224 | data = body(ctx, data, size & ~(unsigned long)0x3f);
225 | size &= 0x3f;
226 | }
227 |
228 | memcpy(ctx->buffer, data, size);
229 | }
230 |
231 | #define OUT(dst, src) \
232 | (dst)[0] = (unsigned char)(src); \
233 | (dst)[1] = (unsigned char)((src) >> 8); \
234 | (dst)[2] = (unsigned char)((src) >> 16); \
235 | (dst)[3] = (unsigned char)((src) >> 24);
236 |
237 | void MD4_Final(unsigned char *result, MD4_CTX *ctx)
238 | {
239 | unsigned long used, available;
240 |
241 | used = ctx->lo & 0x3f;
242 |
243 | ctx->buffer[used++] = 0x80;
244 |
245 | available = 64 - used;
246 |
247 | if (available < 8) {
248 | memset(&ctx->buffer[used], 0, available);
249 | body(ctx, ctx->buffer, 64);
250 | used = 0;
251 | available = 64;
252 | }
253 |
254 | memset(&ctx->buffer[used], 0, available - 8);
255 |
256 | ctx->lo <<= 3;
257 | OUT(&ctx->buffer[56], ctx->lo)
258 | OUT(&ctx->buffer[60], ctx->hi)
259 |
260 | body(ctx, ctx->buffer, 64);
261 |
262 | OUT(&result[0], ctx->a)
263 | OUT(&result[4], ctx->b)
264 | OUT(&result[8], ctx->c)
265 | OUT(&result[12], ctx->d)
266 |
267 | memset(ctx, 0, sizeof(*ctx));
268 | }
269 |
270 | void MD4(const void *data, unsigned long size, unsigned char *result) {
271 | MD4_CTX ctx;
272 | MD4_Init(&ctx);
273 | MD4_Update(&ctx, data, size);
274 | MD4_Final(result, &ctx);
275 | }
276 |
277 | #endif
278 |
--------------------------------------------------------------------------------
/encrypt/md5.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
3 | * MD5 Message-Digest Algorithm (RFC 1321).
4 | *
5 | * Homepage:
6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
7 | *
8 | * Author:
9 | * Alexander Peslyak, better known as Solar Designer
10 | *
11 | * This software was written by Alexander Peslyak in 2001. No copyright is
12 | * claimed, and the software is hereby placed in the public domain.
13 | * In case this attempt to disclaim copyright and place the software in the
14 | * public domain is deemed null and void, then the software is
15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
16 | * general public under the following terms:
17 | *
18 | * Redistribution and use in source and binary forms, with or without
19 | * modification, are permitted.
20 | *
21 | * There's ABSOLUTELY NO WARRANTY, express or implied.
22 | *
23 | * (This is a heavily cut-down "BSD license".)
24 | *
25 | * This differs from Colin Plumb's older public domain implementation in that
26 | * no exactly 32-bit integer data type is required (any 32-bit or wider
27 | * unsigned integer data type will do), there's no compile-time endianness
28 | * configuration, and the function prototypes match OpenSSL's. No code from
29 | * Colin Plumb's implementation has been reused; this comment merely compares
30 | * the properties of the two independent implementations.
31 | *
32 | * The primary goals of this implementation are portability and ease of use.
33 | * It is meant to be fast, but not as fast as possible. Some known
34 | * optimizations are not included to reduce source code size and avoid
35 | * compile-time configuration.
36 | */
37 |
38 | #ifndef HAVE_OPENSSL
39 |
40 | #include
41 |
42 | #include "md5.h"
43 |
44 | /*
45 | * The basic MD5 functions.
46 | *
47 | * F and G are optimized compared to their RFC 1321 definitions for
48 | * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
49 | * implementation.
50 | */
51 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
52 | #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
53 | #define H(x, y, z) (((x) ^ (y)) ^ (z))
54 | #define H2(x, y, z) ((x) ^ ((y) ^ (z)))
55 | #define I(x, y, z) ((y) ^ ((x) | ~(z)))
56 |
57 | /*
58 | * The MD5 transformation for all four rounds.
59 | */
60 | #define STEP(f, a, b, c, d, x, t, s) \
61 | (a) += f((b), (c), (d)) + (x) + (t); \
62 | (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
63 | (a) += (b);
64 |
65 | /*
66 | * SET reads 4 input bytes in little-endian byte order and stores them in a
67 | * properly aligned word in host byte order.
68 | *
69 | * The check for little-endian architectures that tolerate unaligned memory
70 | * accesses is just an optimization. Nothing will break if it fails to detect
71 | * a suitable architecture.
72 | *
73 | * Unfortunately, this optimization may be a C strict aliasing rules violation
74 | * if the caller's data buffer has effective type that cannot be aliased by
75 | * MD5_u32plus. In practice, this problem may occur if these MD5 routines are
76 | * inlined into a calling function, or with future and dangerously advanced
77 | * link-time optimizations. For the time being, keeping these MD5 routines in
78 | * their own translation unit avoids the problem.
79 | */
80 | #if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
81 | #define SET(n) \
82 | (*(MD5_u32plus *)&ptr[(n) * 4])
83 | #define GET(n) \
84 | SET(n)
85 | #else
86 | #define SET(n) \
87 | (ctx->block[(n)] = \
88 | (MD5_u32plus)ptr[(n) * 4] | \
89 | ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
90 | ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
91 | ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
92 | #define GET(n) \
93 | (ctx->block[(n)])
94 | #endif
95 |
96 | /*
97 | * This processes one or more 64-byte data blocks, but does NOT update the bit
98 | * counters. There are no alignment requirements.
99 | */
100 | static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
101 | {
102 | const unsigned char *ptr;
103 | MD5_u32plus a, b, c, d;
104 | MD5_u32plus saved_a, saved_b, saved_c, saved_d;
105 |
106 | ptr = (const unsigned char *)data;
107 |
108 | a = ctx->a;
109 | b = ctx->b;
110 | c = ctx->c;
111 | d = ctx->d;
112 |
113 | do {
114 | saved_a = a;
115 | saved_b = b;
116 | saved_c = c;
117 | saved_d = d;
118 |
119 | /* Round 1 */
120 | STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
121 | STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
122 | STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
123 | STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
124 | STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
125 | STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
126 | STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
127 | STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
128 | STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
129 | STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
130 | STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
131 | STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
132 | STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
133 | STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
134 | STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
135 | STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
136 |
137 | /* Round 2 */
138 | STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
139 | STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
140 | STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
141 | STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
142 | STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
143 | STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
144 | STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
145 | STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
146 | STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
147 | STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
148 | STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
149 | STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
150 | STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
151 | STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
152 | STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
153 | STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
154 |
155 | /* Round 3 */
156 | STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
157 | STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
158 | STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
159 | STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
160 | STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
161 | STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
162 | STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
163 | STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
164 | STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
165 | STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
166 | STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
167 | STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
168 | STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
169 | STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
170 | STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
171 | STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
172 |
173 | /* Round 4 */
174 | STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
175 | STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
176 | STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
177 | STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
178 | STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
179 | STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
180 | STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
181 | STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
182 | STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
183 | STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
184 | STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
185 | STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
186 | STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
187 | STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
188 | STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
189 | STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
190 |
191 | a += saved_a;
192 | b += saved_b;
193 | c += saved_c;
194 | d += saved_d;
195 |
196 | ptr += 64;
197 | } while (size -= 64);
198 |
199 | ctx->a = a;
200 | ctx->b = b;
201 | ctx->c = c;
202 | ctx->d = d;
203 |
204 | return ptr;
205 | }
206 |
207 | void MD5_Init(MD5_CTX *ctx)
208 | {
209 | ctx->a = 0x67452301;
210 | ctx->b = 0xefcdab89;
211 | ctx->c = 0x98badcfe;
212 | ctx->d = 0x10325476;
213 |
214 | ctx->lo = 0;
215 | ctx->hi = 0;
216 | }
217 |
218 | void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
219 | {
220 | MD5_u32plus saved_lo;
221 | unsigned long used, available;
222 |
223 | saved_lo = ctx->lo;
224 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
225 | ctx->hi++;
226 | ctx->hi += size >> 29;
227 |
228 | used = saved_lo & 0x3f;
229 |
230 | if (used) {
231 | available = 64 - used;
232 |
233 | if (size < available) {
234 | memcpy(&ctx->buffer[used], data, size);
235 | return;
236 | }
237 |
238 | memcpy(&ctx->buffer[used], data, available);
239 | data = (const unsigned char *)data + available;
240 | size -= available;
241 | body(ctx, ctx->buffer, 64);
242 | }
243 |
244 | if (size >= 64) {
245 | data = body(ctx, data, size & ~(unsigned long)0x3f);
246 | size &= 0x3f;
247 | }
248 |
249 | memcpy(ctx->buffer, data, size);
250 | }
251 |
252 | #define OUT(dst, src) \
253 | (dst)[0] = (unsigned char)(src); \
254 | (dst)[1] = (unsigned char)((src) >> 8); \
255 | (dst)[2] = (unsigned char)((src) >> 16); \
256 | (dst)[3] = (unsigned char)((src) >> 24);
257 |
258 | void MD5_Final(unsigned char *result, MD5_CTX *ctx)
259 | {
260 | unsigned long used, available;
261 |
262 | used = ctx->lo & 0x3f;
263 |
264 | ctx->buffer[used++] = 0x80;
265 |
266 | available = 64 - used;
267 |
268 | if (available < 8) {
269 | memset(&ctx->buffer[used], 0, available);
270 | body(ctx, ctx->buffer, 64);
271 | used = 0;
272 | available = 64;
273 | }
274 |
275 | memset(&ctx->buffer[used], 0, available - 8);
276 |
277 | ctx->lo <<= 3;
278 | OUT(&ctx->buffer[56], ctx->lo)
279 | OUT(&ctx->buffer[60], ctx->hi)
280 |
281 | body(ctx, ctx->buffer, 64);
282 |
283 | OUT(&result[0], ctx->a)
284 | OUT(&result[4], ctx->b)
285 | OUT(&result[8], ctx->c)
286 | OUT(&result[12], ctx->d)
287 |
288 | memset(ctx, 0, sizeof(*ctx));
289 | }
290 |
291 | void MD5(const void *data, unsigned long size, unsigned char *result) {
292 | MD5_CTX ctx;
293 | MD5_Init(&ctx);
294 | MD5_Update(&ctx, data, size);
295 | MD5_Final(result, &ctx);
296 | }
297 |
298 | #endif
299 |
--------------------------------------------------------------------------------
/ts/DrCOM_zh_CN.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MainWindow
6 |
7 |
8 | DrCOM JLU Qt
9 | 欢迎登录吉大校园网
10 |
11 |
12 |
13 | MAC
14 | 物理地址
15 |
16 |
17 |
18 | Account
19 | 账号
20 |
21 |
22 |
23 | Password
24 | 密码
25 |
26 |
27 |
28 | Remember
29 | 记住我
30 |
31 |
32 |
33 | AutoLogin
34 | 自动登录
35 |
36 |
37 |
38 | NotShowWelcomePage
39 | 不弹出校园网之窗
40 |
41 |
42 |
43 | HideLoginWindow
44 | 隐藏登录窗口
45 |
46 |
47 |
48 |
49 | Login
50 | 登录
51 |
52 |
53 |
54 | Your IP address will be shown here
55 | 登录IP
56 |
57 |
58 |
59 | Re&start
60 | 重启客户端(&S)
61 |
62 |
63 |
64 | &Restore
65 | 显示窗口(&R)
66 |
67 |
68 |
69 | &Logout
70 | 注销(&L)
71 |
72 |
73 |
74 | &Quit
75 | 退出(&Q)
76 |
77 |
78 |
79 | &About
80 | 关于(&A)
81 |
82 |
83 |
84 | &Help
85 | 帮助(&H)
86 |
87 |
88 |
89 | Input can not be empty!
90 | 输入不能为空!
91 |
92 |
93 |
94 | Illegal MAC address!
95 | 物理地址输入有误!
96 |
97 |
98 |
99 | DrCOM JLU Qt -- online
100 | 吉大校园网 - 在线
101 |
102 |
103 |
104 | DrCOM JLU Qt -- offline
105 | 吉大校园网 - 离线
106 |
107 |
108 |
109 | Logout succeed
110 | 注销成功
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | Login failed
127 | 登录失败
128 |
129 |
130 |
131 | Binding port failed. Please check if there are other clients occupying the port
132 | 绑定端口失败,请检查是否有其他客户端占用了端口
133 |
134 |
135 |
136 | Challenge failed. Please check your connection:)
137 | 尝试连接服务器失败,请检查你的网络连接:)是不是wifi没连或者网线没插呀
138 |
139 |
140 |
141 | Attention that you should connect to wifi or wired firstly and then start the drcom client. If you have connected, you may restart drcom to solve the problem.
142 | 注意你需要先连上wifi或者插上网线然后再打开客户端。如果你已经连上了,重启一下该客户端就可以登录成功了:D。
143 |
144 |
145 |
146 |
147 | Yes
148 | 是
149 |
150 |
151 |
152 |
153 | Nope
154 | 否
155 |
156 |
157 |
158 | Someone is using this account with wired
159 | 有人正在用这个账号
160 |
161 |
162 |
163 | The server is busy, please log back in again
164 | 服务器繁忙,请稍后重试
165 |
166 |
167 |
168 | Account and password not match
169 | 账号密码不匹配
170 |
171 |
172 |
173 | The cumulative time or traffic for this account has exceeded the limit
174 | 该账户的网络时长或者流量超限了
175 |
176 |
177 |
178 | This account is suspended
179 | 该账号被冻结
180 |
181 |
182 |
183 | IP address does not match, this account can only be used in the specified IP address
184 | IP地址不匹配,该账号只能用于指定的IP地址
185 |
186 |
187 |
188 | MAC address does not match, this account can only be used in the specified IP and MAC address
189 | 物理地址不匹配,该账号只能用于指定的物理地址
190 |
191 |
192 |
193 | This account has too many IP addresses
194 | 该账号的IP地址太多了
195 |
196 |
197 |
198 | The client version is incorrect
199 | 登录客户端版本太老了,请更新您的客户端
200 |
201 |
202 |
203 | This account can only be used on specified MAC and IP address
204 | 该账号只能用于指定的物理地址和IP地址
205 |
206 |
207 |
208 | Your PC set up a static IP, please change to DHCP, and then re-login
209 | 你的电脑设置成了静态IP,请改为DHCP自动获取模式,然后重新登录
210 |
211 |
212 |
213 |
214 | You have been offline
215 | 您已离线
216 |
217 |
218 |
219 | Time out, please check your connection
220 | 连接超时,请检查您的网络连接:)是不是wifi没信号了
221 |
222 |
223 |
224 | The DrCOM client will try to restart to solve some unstable problems but the function relies on "remember me"
225 | 该客户端会尝试自动重启并重新登录以解决一些不稳定老掉线的问题,若要启用该功能需勾选“记住我”
226 |
227 |
228 |
229 | Due to some reasons, you should connect to wifi or wired firstly and then start the drcom client. So you may not login until you restart DrCOM :D
230 | 由于某些原因,你需要先连上wifi或者插上网线然后再打开登录客户端。所以如果你现在连上网线或wifi但是不重启客户端的话应该就登录不上:D
231 |
232 |
233 |
234 |
235 | Restart DrCOM?
236 | 立即重启客户端?
237 |
238 |
239 |
240 | Unknow reason
241 | 未知错误
242 |
243 |
244 |
245 | custom (format: 1A:2B:3C:4D:5E:6F case insensitive)
246 | 自定义(格式:1A:2B:3C:4D:5E:6F不区分大小写)
247 |
248 |
249 |
250 | DrCOM JLU Qt version
251 | 吉大校园网客户端Qt版
252 |
253 |
254 |
255 |
--------------------------------------------------------------------------------
/singleinstance/singleapplication_p.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) Itay Grudev 2015 - 2018
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | //
24 | // W A R N I N G !!!
25 | // -----------------
26 | //
27 | // This file is not part of the SingleApplication API. It is used purely as an
28 | // implementation detail. This header file may change from version to
29 | // version without notice, or may even be removed.
30 | //
31 |
32 | #include
33 | #include
34 |
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 |
45 | #include "singleapplication.h"
46 | #include "singleapplication_p.h"
47 |
48 | #ifdef Q_OS_WIN
49 | #include
50 | #include
51 | #endif
52 |
53 | SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
54 | : q_ptr( q_ptr )
55 | {
56 | server = nullptr;
57 | socket = nullptr;
58 | memory = nullptr;
59 | instanceNumber = -1;
60 | }
61 |
62 | SingleApplicationPrivate::~SingleApplicationPrivate()
63 | {
64 | if( socket != nullptr ) {
65 | socket->close();
66 | delete socket;
67 | }
68 |
69 | memory->lock();
70 | InstancesInfo* inst = static_cast(memory->data());
71 | if( server != nullptr ) {
72 | server->close();
73 | delete server;
74 | inst->primary = false;
75 | inst->primaryPid = -1;
76 | inst->checksum = blockChecksum();
77 | }
78 | memory->unlock();
79 |
80 | delete memory;
81 | }
82 |
83 | void SingleApplicationPrivate::genBlockServerName()
84 | {
85 | QCryptographicHash appData( QCryptographicHash::Sha256 );
86 | appData.addData( "SingleApplication");
87 | appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
88 | appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
89 | appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
90 |
91 | if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
92 | appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
93 | }
94 |
95 | if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
96 | #ifdef Q_OS_WIN
97 | appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
98 | #else
99 | appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
100 | #endif
101 | }
102 |
103 | // User level block requires a user specific data in the hash
104 | if( options & SingleApplication::Mode::User ) {
105 | #ifdef Q_OS_WIN
106 | wchar_t username [ UNLEN + 1 ];
107 | // Specifies size of the buffer on input
108 | DWORD usernameLength = UNLEN + 1;
109 | if( GetUserNameW( username, &usernameLength ) ) {
110 | appData.addData( QString::fromWCharArray(username).toUtf8() );
111 | } else {
112 | appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
113 | }
114 | #endif
115 | #ifdef Q_OS_UNIX
116 | QProcess process;
117 | process.start( "whoami" );
118 | if( process.waitForFinished( 100 ) &&
119 | process.exitCode() == QProcess::NormalExit) {
120 | appData.addData( process.readLine() );
121 | } else {
122 | appData.addData(
123 | QDir(
124 | QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
125 | ).absolutePath().toUtf8()
126 | );
127 | }
128 | #endif
129 | }
130 |
131 | // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
132 | // server naming requirements.
133 | blockServerName = appData.result().toBase64().replace("/", "_");
134 | }
135 |
136 | void SingleApplicationPrivate::initializeMemoryBlock()
137 | {
138 | InstancesInfo* inst = static_cast( memory->data() );
139 | inst->primary = false;
140 | inst->secondary = 0;
141 | inst->primaryPid = -1;
142 | inst->checksum = blockChecksum();
143 | }
144 |
145 | void SingleApplicationPrivate::startPrimary()
146 | {
147 | Q_Q(SingleApplication);
148 |
149 | // Successful creation means that no main process exists
150 | // So we start a QLocalServer to listen for connections
151 | QLocalServer::removeServer( blockServerName );
152 | server = new QLocalServer();
153 |
154 | // Restrict access to the socket according to the
155 | // SingleApplication::Mode::User flag on User level or no restrictions
156 | if( options & SingleApplication::Mode::User ) {
157 | server->setSocketOptions( QLocalServer::UserAccessOption );
158 | } else {
159 | server->setSocketOptions( QLocalServer::WorldAccessOption );
160 | }
161 |
162 | server->listen( blockServerName );
163 | QObject::connect(
164 | server,
165 | &QLocalServer::newConnection,
166 | this,
167 | &SingleApplicationPrivate::slotConnectionEstablished
168 | );
169 |
170 | // Reset the number of connections
171 | InstancesInfo* inst = static_cast ( memory->data() );
172 |
173 | inst->primary = true;
174 | inst->primaryPid = q->applicationPid();
175 | inst->checksum = blockChecksum();
176 |
177 | instanceNumber = 0;
178 | }
179 |
180 | void SingleApplicationPrivate::startSecondary()
181 | {
182 | }
183 |
184 | void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
185 | {
186 | // Connect to the Local Server of the Primary Instance if not already
187 | // connected.
188 | if( socket == nullptr ) {
189 | socket = new QLocalSocket();
190 | }
191 |
192 | // If already connected - we are done;
193 | if( socket->state() == QLocalSocket::ConnectedState )
194 | return;
195 |
196 | // If not connect
197 | if( socket->state() == QLocalSocket::UnconnectedState ||
198 | socket->state() == QLocalSocket::ClosingState ) {
199 | socket->connectToServer( blockServerName );
200 | }
201 |
202 | // Wait for being connected
203 | if( socket->state() == QLocalSocket::ConnectingState ) {
204 | socket->waitForConnected( msecs );
205 | }
206 |
207 | // Initialisation message according to the SingleApplication protocol
208 | if( socket->state() == QLocalSocket::ConnectedState ) {
209 | // Notify the parent that a new instance had been started;
210 | QByteArray initMsg;
211 | QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
212 |
213 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
214 | writeStream.setVersion(QDataStream::Qt_5_6);
215 | #endif
216 |
217 | writeStream << blockServerName.toLatin1();
218 | writeStream << static_cast(connectionType);
219 | writeStream << instanceNumber;
220 | quint16 checksum = qChecksum(QByteArrayView(initMsg.constData(), static_cast(initMsg.length())));
221 | writeStream << checksum;
222 |
223 | // The header indicates the message length that follows
224 | QByteArray header;
225 | QDataStream headerStream(&header, QIODevice::WriteOnly);
226 |
227 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
228 | headerStream.setVersion(QDataStream::Qt_5_6);
229 | #endif
230 | headerStream << static_cast ( initMsg.length() );
231 |
232 | socket->write( header );
233 | socket->write( initMsg );
234 | socket->flush();
235 | socket->waitForBytesWritten( msecs );
236 | }
237 | }
238 |
239 | quint16 SingleApplicationPrivate::blockChecksum()
240 | {
241 | return qChecksum(
242 | QByteArrayView((static_cast (memory->data())),
243 | offsetof( InstancesInfo, checksum ))
244 | );
245 | }
246 |
247 | qint64 SingleApplicationPrivate::primaryPid()
248 | {
249 | qint64 pid;
250 |
251 | memory->lock();
252 | InstancesInfo* inst = static_cast( memory->data() );
253 | pid = inst->primaryPid;
254 | memory->unlock();
255 |
256 | return pid;
257 | }
258 |
259 | /**
260 | * @brief Executed when a connection has been made to the LocalServer
261 | */
262 | void SingleApplicationPrivate::slotConnectionEstablished()
263 | {
264 | QLocalSocket *nextConnSocket = server->nextPendingConnection();
265 | connectionMap.insert(nextConnSocket, ConnectionInfo());
266 |
267 | QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
268 | [nextConnSocket, this]() {
269 | auto &info = connectionMap[nextConnSocket];
270 | Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
271 | }
272 | );
273 |
274 | QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
275 | [nextConnSocket, this](){
276 | connectionMap.remove(nextConnSocket);
277 | nextConnSocket->deleteLater();
278 | }
279 | );
280 |
281 | QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
282 | [nextConnSocket, this]() {
283 | auto &info = connectionMap[nextConnSocket];
284 | switch(info.stage) {
285 | case StageHeader:
286 | readInitMessageHeader(nextConnSocket);
287 | break;
288 | case StageBody:
289 | readInitMessageBody(nextConnSocket);
290 | break;
291 | case StageConnected:
292 | Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
293 | break;
294 | default:
295 | break;
296 | };
297 | }
298 | );
299 | }
300 |
301 | void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
302 | {
303 | if (!connectionMap.contains( sock )) {
304 | return;
305 | }
306 |
307 | if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
308 | return;
309 | }
310 |
311 | QDataStream headerStream( sock );
312 |
313 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
314 | headerStream.setVersion( QDataStream::Qt_5_6 );
315 | #endif
316 |
317 | // Read the header to know the message length
318 | quint64 msgLen = 0;
319 | headerStream >> msgLen;
320 | ConnectionInfo &info = connectionMap[sock];
321 | info.stage = StageBody;
322 | info.msgLen = msgLen;
323 |
324 | if ( sock->bytesAvailable() >= (qint64) msgLen ) {
325 | readInitMessageBody( sock );
326 | }
327 | }
328 |
329 | void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
330 | {
331 | Q_Q(SingleApplication);
332 |
333 | if (!connectionMap.contains( sock )) {
334 | return;
335 | }
336 |
337 | ConnectionInfo &info = connectionMap[sock];
338 | if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
339 | return;
340 | }
341 |
342 | // Read the message body
343 | QByteArray msgBytes = sock->read(info.msgLen);
344 | QDataStream readStream(msgBytes);
345 |
346 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
347 | readStream.setVersion( QDataStream::Qt_5_6 );
348 | #endif
349 |
350 | // server name
351 | QByteArray latin1Name;
352 | readStream >> latin1Name;
353 |
354 | // connection type
355 | ConnectionType connectionType = InvalidConnection;
356 | quint8 connTypeVal = InvalidConnection;
357 | readStream >> connTypeVal;
358 | connectionType = static_cast ( connTypeVal );
359 |
360 | // instance id
361 | quint32 instanceId = 0;
362 | readStream >> instanceId;
363 |
364 | // checksum
365 | quint16 msgChecksum = 0;
366 | readStream >> msgChecksum;
367 |
368 | const quint16 actualChecksum = qChecksum( QByteArrayView(msgBytes.constData(), static_cast( msgBytes.length() - sizeof( quint16 ) )) );
369 |
370 | bool isValid = readStream.status() == QDataStream::Ok &&
371 | QLatin1String(latin1Name) == blockServerName &&
372 | msgChecksum == actualChecksum;
373 |
374 | if( !isValid ) {
375 | sock->close();
376 | return;
377 | }
378 |
379 | info.instanceId = instanceId;
380 | info.stage = StageConnected;
381 |
382 | if( connectionType == NewInstance ||
383 | ( connectionType == SecondaryInstance &&
384 | options & SingleApplication::Mode::SecondaryNotification ) )
385 | {
386 | Q_EMIT q->instanceStarted();
387 | }
388 |
389 | if (sock->bytesAvailable() > 0) {
390 | Q_EMIT this->slotDataAvailable( sock, instanceId );
391 | }
392 | }
393 |
394 | void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
395 | {
396 | Q_Q(SingleApplication);
397 | Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
398 | }
399 |
400 | void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
401 | {
402 | if( closedSocket->bytesAvailable() > 0 )
403 | Q_EMIT slotDataAvailable( closedSocket, instanceId );
404 | }
405 |
--------------------------------------------------------------------------------
/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "ui_mainwindow.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "constants.h"
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "encrypt/EncryptData.h"
21 |
22 | MainWindow::MainWindow(SingleApplication *parentApp, QWidget *parent) :
23 | QDialog(parent),
24 | ui(new Ui::MainWindow),
25 | app(parentApp)
26 | {
27 | // 关机时接收退出信号,释放socket
28 | QObject::connect(app, &SingleApplication::aboutToQuit, this, &MainWindow::QuitDrcom);
29 |
30 | qDebug()<<"MainWindow constructor";
31 | ui->setupUi(this);
32 |
33 | CURR_STATE = STATE_OFFLINE;
34 |
35 | // 记住窗口大小功能
36 | QSettings settings;
37 | restoreGeometry(settings.value("mainWindowGeometry").toByteArray());
38 |
39 | // 获取mac地址
40 | foreach(QNetworkInterface i, QNetworkInterface::allInterfaces()) {
41 | if (!i.flags().testFlag(QNetworkInterface::IsLoopBack)) {
42 | ui->comboBoxMAC->addItem(i.hardwareAddress() + " " + i.name());
43 | }
44 | }
45 | ui->comboBoxMAC->addItem(CUSTOM_MAC);
46 |
47 | // 重启功能
48 | restartAction = new QAction(tr("Re&start"), this);
49 | connect(restartAction, &QAction::triggered, this, &MainWindow::RestartDrcomByUser);
50 |
51 | // 创建托盘菜单和图标
52 | // 托盘菜单选项
53 | restoreAction = new QAction(tr("&Restore"), this);
54 | connect(restoreAction, &QAction::triggered, this, &MainWindow::ShowLoginWindow);
55 | logOutAction = new QAction(tr("&Logout"), this);
56 | connect(logOutAction, &QAction::triggered, this, &MainWindow::UserLogOut);
57 | quitAction = new QAction(tr("&Quit"), this);
58 | connect(quitAction, &QAction::triggered, this, &MainWindow::QuitDrcom);
59 | // 新建菜单
60 | trayIconMenu = new QMenu(this);
61 | trayIconMenu->addAction(restoreAction);
62 | trayIconMenu->addSeparator();
63 | trayIconMenu->addAction(logOutAction);
64 | trayIconMenu->addSeparator();
65 | trayIconMenu->addAction(restartAction);
66 | trayIconMenu->addAction(quitAction);
67 | // 新建托盘图标
68 | trayIcon = new QSystemTrayIcon(this);
69 | trayIcon->setContextMenu(trayIconMenu);
70 | // 设置托盘菜单响应函数
71 | connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::IconActivated);
72 | // 设置托盘图标和窗口图标
73 | SetIcon(false);
74 | // 显示出来托盘图标
75 | trayIcon->show();
76 |
77 | // 创建窗口菜单
78 | aboutAction = new QAction(tr("&About"), this);
79 | connect(aboutAction, &QAction::triggered, this, &MainWindow::AboutDrcom);
80 | windowMenu = new QMenu(tr("&Help"), this);
81 | windowMenu->addAction(aboutAction);
82 | windowMenu->addAction(logOutAction);
83 | ui->menuBar->addMenu(windowMenu);
84 | // 重启是个专门的菜单按钮
85 | ui->menuBar->addAction(restartAction);
86 |
87 | // 读取配置文件
88 | LoadSettings();
89 |
90 | // 设置回调函数
91 | dogcomController = new DogcomController();
92 | connect(dogcomController, &DogcomController::HaveBeenOffline,
93 | this, &MainWindow::HandleOffline);
94 | connect(dogcomController, &DogcomController::HaveLoggedIn,
95 | this, &MainWindow::HandleLoggedIn);
96 | connect(dogcomController, &DogcomController::HaveObtainedIp,
97 | this, &MainWindow::HandleIpAddress);
98 |
99 | // 验证手动输入的mac地址
100 | macValidator = new QRegularExpressionValidator(QRegularExpression("[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}"), this);
101 | ui->lineEditMAC->setValidator(macValidator);
102 |
103 | // 尚未登录 不可注销
104 | DisableLogOutButton(true);
105 |
106 | // 自动登录功能
107 | QSettings s(SETTINGS_FILE_NAME);
108 | int restartTimes = s.value(ID_RESTART_TIMES, 0).toInt();
109 | qDebug() << "MainWindow constructor: restartTimes=" << restartTimes;
110 | if (restartTimes == 0) {
111 | if (bAutoLogin) {
112 | emit ui->pushButtonLogin->click();
113 | }
114 | }
115 | else {
116 | // 尝试自动重启中
117 | emit ui->pushButtonLogin->click();
118 | }
119 | }
120 |
121 | void MainWindow::AboutDrcom() {
122 | QDesktopServices::openUrl(QUrl("https://github.com/code4lala/drcom-jlu-qt"));
123 | }
124 |
125 | void MainWindow::closeEvent(QCloseEvent *) {
126 | QSettings settings;
127 | settings.setValue("mainWindowGeometry", saveGeometry());
128 | // 未登录时直接关闭窗口就退出
129 | if (CURR_STATE == STATE_OFFLINE) {
130 | QuitDrcom();
131 | }
132 | }
133 |
134 | void MainWindow::ShowLoginWindow() {
135 | if (!isVisible()) {
136 | // 若登录窗口没显示则显示出来
137 | showNormal();
138 | }
139 | else {
140 | // 已显示则将窗口设为焦点
141 | activateWindow();
142 | }
143 | }
144 |
145 | void MainWindow::RestartDrcom()
146 | {
147 | bRestart=true;
148 | if(CURR_STATE==STATE_ONLINE)
149 | dogcomController->LogOut();
150 | else if(CURR_STATE==STATE_OFFLINE){
151 | qDebug() << "quiting current instance...";
152 | qApp->quit();
153 | qDebug() << "Restarting Drcom...";
154 | QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
155 | qDebug() << "Restart done.";
156 | }
157 | // else if(CURR_STATE==STATE_LOGGING)
158 | // 正在登录时候退出,假装没看到,不理
159 | }
160 |
161 | void MainWindow::QuitDrcom()
162 | {
163 | // 退出之前恢复重试计数
164 | QSettings s(SETTINGS_FILE_NAME);
165 | s.setValue(ID_RESTART_TIMES, 0);
166 | qDebug() << "reset restartTimes";
167 | qDebug() << "QuitDrcom";
168 |
169 | // TODO : release socket
170 | bQuit=true;
171 | if(CURR_STATE==STATE_ONLINE)
172 | dogcomController->LogOut();
173 | else if(CURR_STATE==STATE_OFFLINE)
174 | // qApp->quit();
175 | QTimer::singleShot(0, qApp, SLOT(quit()));
176 | // else if(CURR_STATE==STATE_LOGGING)
177 | // 正在登录时候退出,假装没看到,不理
178 |
179 | // qApp->quit()调用放到了注销响应那块
180 | }
181 |
182 | void MainWindow::RestartDrcomByUser()
183 | {
184 | // 重启之前恢复重试计数
185 | QSettings s(SETTINGS_FILE_NAME);
186 | s.setValue(ID_RESTART_TIMES, 0);
187 | qDebug() << "reset restartTimes";
188 | RestartDrcom();
189 | }
190 |
191 | void MainWindow::IconActivated(QSystemTrayIcon::ActivationReason reason)
192 | {
193 | switch (reason) {
194 | case QSystemTrayIcon::DoubleClick: {
195 | restoreAction->activate(restoreAction->Trigger);
196 | break;
197 | }
198 | default:
199 | break;
200 | }
201 | }
202 |
203 | void MainWindow::LoadSettings() {
204 | QSettings s(SETTINGS_FILE_NAME);
205 | account = s.value(ID_ACCOUNT, "").toString();
206 | mac_addr = s.value(ID_MAC, "").toString();
207 | bRemember = s.value(ID_REMEMBER, false).toBool();
208 | bAutoLogin = s.value(ID_AUTO_LOGIN, false).toBool();
209 | bHideWindow = s.value(ID_HIDE_WINDOW, false).toBool();
210 | bNotShowWelcome = s.value(ID_NOT_SHOW_WELCOME, false).toBool();
211 | ui->lineEditAccount->setText(account);
212 | SetMAC(mac_addr);
213 | if (bRemember)
214 | ui->checkBoxRemember->setCheckState(Qt::CheckState::Checked);
215 | else
216 | ui->checkBoxRemember->setCheckState(Qt::CheckState::Unchecked);
217 | if (bAutoLogin)
218 | ui->checkBoxAutoLogin->setCheckState(Qt::CheckState::Checked);
219 | else
220 | ui->checkBoxAutoLogin->setCheckState(Qt::CheckState::Unchecked);
221 | if(bHideWindow)
222 | ui->checkBoxHideLoginWindow->setCheckState(Qt::CheckState::Checked);
223 | else
224 | ui->checkBoxHideLoginWindow->setCheckState(Qt::CheckState::Unchecked);
225 | if(bNotShowWelcome)
226 | ui->checkBoxNotShowWelcome->setCheckState(Qt::CheckState::Checked);
227 | else
228 | ui->checkBoxNotShowWelcome->setCheckState(Qt::CheckState::Unchecked);
229 |
230 | // 密码加密处理
231 | QString encryptedPasswd = s.value(ID_PASSWORD, "").toString();
232 | password = DecryptString(encryptedPasswd);
233 | ui->lineEditPass->setText(password);
234 | }
235 |
236 | void MainWindow::SaveSettings() {
237 | QSettings s(SETTINGS_FILE_NAME);
238 | s.setValue(ID_ACCOUNT, account);
239 | s.setValue(ID_MAC, mac_addr);
240 | s.setValue(ID_REMEMBER, bRemember);
241 | s.setValue(ID_AUTO_LOGIN, bAutoLogin);
242 |
243 | // 密码加密处理
244 | QString enctyptedPasswd = EncryptString(password);
245 | s.setValue(ID_PASSWORD, enctyptedPasswd);
246 | }
247 |
248 | void MainWindow::SetMAC(const QString &m)
249 | {
250 | for (int i = 0; i < ui->comboBoxMAC->count(); i++) {
251 | QString s = ui->comboBoxMAC->itemText(i);
252 | if (!s.compare(CUSTOM_MAC))continue;
253 | if (s.indexOf(m) != -1) {
254 | //当前列表中有该mac地址
255 | ui->comboBoxMAC->setCurrentIndex(i);
256 | return;
257 | }
258 | }
259 | // 当前列表中没有该mac地址,填充到输入框中
260 | ui->comboBoxMAC->setCurrentText(CUSTOM_MAC);
261 | ui->lineEditMAC->setText(m);
262 | }
263 |
264 | MainWindow::~MainWindow()
265 | {
266 | qDebug()<<"MainWindow destructor";
267 | delete ui;
268 | delete restartAction;
269 | delete restoreAction;
270 | delete logOutAction;
271 | delete quitAction;
272 | delete trayIconMenu;
273 | delete trayIcon;
274 | delete aboutAction;
275 | delete windowMenu;
276 | delete dogcomController;
277 | delete macValidator;
278 | }
279 |
280 | void MainWindow::on_checkBoxAutoLogin_toggled(bool checked)
281 | {
282 | if (checked) {
283 | ui->checkBoxRemember->setChecked(true);
284 | }
285 | }
286 |
287 | void MainWindow::on_checkBoxRemember_toggled(bool checked)
288 | {
289 | if (!checked) {
290 | ui->checkBoxAutoLogin->setChecked(false);
291 | }
292 | }
293 |
294 | void MainWindow::on_comboBoxMAC_currentTextChanged(const QString &arg1)
295 | {
296 | if (!arg1.compare(CUSTOM_MAC)) {
297 | ui->lineEditMAC->setDisabled(false);
298 | }
299 | else {
300 | ui->lineEditMAC->setDisabled(true);
301 | }
302 | }
303 |
304 | void MainWindow::on_pushButtonLogin_clicked()
305 | {
306 | GetInputs();
307 | if (!account.compare("")
308 | || !password.compare("")
309 | || !mac_addr.compare("")) {
310 | QMessageBox::warning(this, APP_NAME, tr("Input can not be empty!"));
311 | return;
312 | }
313 | if (mac_addr.length() != 17) {
314 | QMessageBox::warning(this, APP_NAME, tr("Illegal MAC address!"));
315 | return;
316 | }
317 | // 输入无误,执行登录操作
318 | // 先禁用输入框和按钮
319 | SetDisableInput(true);
320 | CURR_STATE = STATE_LOGGING;
321 | dogcomController->Login(account, password, mac_addr);
322 | }
323 |
324 | void MainWindow::SetDisableInput(bool yes)
325 | {
326 | ui->lineEditAccount->setDisabled(yes);
327 | ui->lineEditPass->setDisabled(yes);
328 | ui->comboBoxMAC->setDisabled(yes);
329 | ui->lineEditMAC->setDisabled(yes);
330 | ui->checkBoxRemember->setDisabled(yes);
331 | ui->checkBoxAutoLogin->setDisabled(yes);
332 | ui->pushButtonLogin->setDisabled(yes);
333 | if (!yes) {
334 | // 要启用输入框
335 | if (ui->comboBoxMAC->currentText().compare(CUSTOM_MAC)) {
336 | // 当前选的不是自定义mac地址
337 | ui->lineEditMAC->setDisabled(true);
338 | }
339 | }
340 | }
341 |
342 | void MainWindow::SetIcon(bool online)
343 | {
344 | QIcon icon;
345 | QString toolTip;
346 | if (online) {
347 | // 设置彩色图标
348 | icon = QIcon(":/images/online.png");
349 | toolTip = tr("DrCOM JLU Qt -- online");
350 | }
351 | else {
352 | // 设置灰色图标
353 | icon = QIcon(":/images/offline.png");
354 | toolTip = tr("DrCOM JLU Qt -- offline");
355 | }
356 | trayIcon->setIcon(icon);
357 | trayIcon->setToolTip(toolTip);
358 | setWindowIcon(icon);
359 | }
360 |
361 | void MainWindow::GetInputs() {
362 | account = ui->lineEditAccount->text();
363 | password = ui->lineEditPass->text();
364 | if (ui->lineEditMAC->isEnabled()) {
365 | mac_addr = ui->lineEditMAC->text();
366 | }
367 | else {
368 | mac_addr = ui->comboBoxMAC->currentText();
369 | }
370 | mac_addr = mac_addr.remove(17, mac_addr.length()).toUpper();
371 | bRemember = ui->checkBoxRemember->checkState();
372 | bAutoLogin = ui->checkBoxAutoLogin->checkState();
373 | }
374 |
375 | void MainWindow::HandleOffline(int reason)
376 | {
377 | CURR_STATE = STATE_OFFLINE;
378 | ui->pushButtonLogin->setText(tr("Login"));
379 | switch (reason) {
380 | case OFF_USER_LOGOUT: {
381 | if(bQuit){
382 | qApp->quit();
383 | return;
384 | }
385 | if(bRestart){
386 | qApp->quit();
387 | qDebug() << "Restarting Drcom...";
388 | QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
389 | qDebug() << "Restart done.";
390 | return;
391 | }
392 | QMessageBox::information(this, tr("Logout succeed"), tr("Logout succeed"));
393 | break;
394 | }
395 | case OFF_BIND_FAILED: {
396 | QMessageBox::critical(this, tr("Login failed"), tr("Binding port failed. Please check if there are other clients occupying the port"));
397 | break;
398 | }
399 | case OFF_CHALLENGE_FAILED: {
400 | // 弹出一个提示框,带一个直接重启客户端的按钮
401 | QMessageBox msgBox;
402 | msgBox.setText(tr("Login failed") + " " + tr("Challenge failed. Please check your connection:)") + " " +
403 | tr("Attention that you should connect to wifi or wired firstly and then start the drcom client. If you have connected, you may restart drcom to solve the problem.")
404 | + " " + tr("Restart DrCOM?"));
405 | QAbstractButton *pButtonYes = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
406 | msgBox.addButton(tr("Nope"), QMessageBox::NoRole);
407 | msgBox.exec();
408 | if (msgBox.clickedButton() == pButtonYes) {
409 | qDebug() << "Restart DrCOM confirmed in case OFF_CHALLENGE_FAILED";
410 | RestartDrcomByUser();
411 | }
412 | break;
413 | }
414 | case OFF_CHECK_MAC: {
415 | QMessageBox::critical(this, tr("Login failed"), tr("Someone is using this account with wired"));
416 | break;
417 | }
418 | case OFF_SERVER_BUSY: {
419 | QMessageBox::critical(this, tr("Login failed"), tr("The server is busy, please log back in again"));
420 | break;
421 | }
422 | case OFF_WRONG_PASS: {
423 | QMessageBox::critical(this, tr("Login failed"), tr("Account and password not match"));
424 | break;
425 | }
426 | case OFF_NOT_ENOUGH: {
427 | QMessageBox::critical(this, tr("Login failed"), tr("The cumulative time or traffic for this account has exceeded the limit"));
428 | break;
429 | }
430 | case OFF_FREEZE_UP: {
431 | QMessageBox::critical(this, tr("Login failed"), tr("This account is suspended"));
432 | break;
433 | }
434 | case OFF_NOT_ON_THIS_IP: {
435 | QMessageBox::critical(this, tr("Login failed"), tr("IP address does not match, this account can only be used in the specified IP address"));
436 | break;
437 | }
438 | case OFF_NOT_ON_THIS_MAC: {
439 | QMessageBox::critical(this, tr("Login failed"), tr("MAC address does not match, this account can only be used in the specified IP and MAC address"));
440 | break;
441 | }
442 | case OFF_TOO_MUCH_IP: {
443 | QMessageBox::critical(this, tr("Login failed"), tr("This account has too many IP addresses"));
444 | break;
445 | }
446 | case OFF_UPDATE_CLIENT: {
447 | QMessageBox::critical(this, tr("Login failed"), tr("The client version is incorrect"));
448 | break;
449 | }
450 | case OFF_NOT_ON_THIS_IP_MAC: {
451 | QMessageBox::critical(this, tr("Login failed"), tr("This account can only be used on specified MAC and IP address"));
452 | break;
453 | }
454 | case OFF_MUST_USE_DHCP: {
455 | QMessageBox::critical(this, tr("Login failed"), tr("Your PC set up a static IP, please change to DHCP, and then re-login"));
456 | break;
457 | }
458 | case OFF_TIMEOUT: {
459 | // 先尝试自己重启若干次,自个重启还不行的话再提示用户
460 | // 自己重启的话需要用户提前勾选记住密码
461 | if (bRemember) {
462 | QSettings s(SETTINGS_FILE_NAME);
463 | int restartTimes = s.value(ID_RESTART_TIMES, 0).toInt();
464 | qDebug() << "case OFF_TIMEOUT: restartTimes=" << restartTimes;
465 | if (restartTimes <= RETRY_TIMES) {
466 | qDebug() << "case OFF_TIMEOUT: restartTimes++";
467 | s.setValue(ID_RESTART_TIMES, restartTimes + 1);
468 | //尝试自行重启
469 | qDebug() << "trying to restart to reconnect...";
470 | qDebug() << "restarting for the" << restartTimes << "times";
471 | RestartDrcom();
472 | return;
473 | }
474 | }
475 |
476 | // 弹出一个提示框,带一个直接重启客户端的按钮
477 | QMessageBox msgBox;
478 | msgBox.setText(tr("You have been offline") + " " + tr("Time out, please check your connection")
479 | + " " + tr("The DrCOM client will try to restart to solve some unstable problems but the function relies on \"remember me\"")
480 | + " " + tr("Due to some reasons, you should connect to wifi or wired firstly and then start the drcom client. So you may not login until you restart DrCOM :D")
481 | + " " + tr("Restart DrCOM?"));
482 | QAbstractButton *pButtonYes = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
483 | msgBox.addButton(tr("Nope"), QMessageBox::NoRole);
484 | msgBox.exec();
485 | if (msgBox.clickedButton() == pButtonYes) {
486 | qDebug() << "Restart DrCOM confirmed in case OFF_TIMEOUT";
487 | RestartDrcomByUser();
488 | }
489 | break;
490 | }
491 | case OFF_UNKNOWN:
492 | default:
493 | QMessageBox::critical(this, tr("You have been offline"), tr("Unknow reason"));
494 | break;
495 | }
496 | if (reason == OFF_WRONG_PASS) {
497 | // 清除已保存的密码
498 | account = "";
499 | password = "";
500 | bRemember = false;
501 | bAutoLogin = false;
502 | SaveSettings();
503 | }
504 | // 重新启用输入
505 | SetDisableInput(false);
506 | SetIcon(false);
507 | // 禁用注销按钮
508 | DisableLogOutButton(true);
509 | // 显示出窗口
510 | ShowLoginWindow();
511 | }
512 |
513 | void MainWindow::HandleLoggedIn()
514 | {
515 | QSettings s(SETTINGS_FILE_NAME);
516 | int restartTimes = s.value(ID_RESTART_TIMES, 0).toInt();
517 | qDebug() << "HandleLoggedIn: restartTimes=" << restartTimes;
518 |
519 | CURR_STATE = STATE_ONLINE;
520 | // 显示欢迎页
521 | bool bNotShowWelcome = s.value(ID_NOT_SHOW_WELCOME, false).toBool();
522 | if (restartTimes == 0 && !bNotShowWelcome) {
523 | qDebug()<<"open welcome page";
524 | QDesktopServices::openUrl(QUrl("http://login.jlu.edu.cn/notice.php"));
525 | }
526 | else {
527 | // 自行尝试重启的,不显示欢迎页
528 | }
529 | // 登录成功,保存密码
530 | if (bRemember) {
531 | SaveSettings();
532 | }
533 | else {
534 | account = "";
535 | password = "";
536 | SaveSettings();
537 | }
538 | SetIcon(true);
539 | // 启用注销按钮
540 | DisableLogOutButton(false);
541 |
542 | s.setValue(ID_RESTART_TIMES, 0);
543 | qDebug() << "HandleLoggedIn: reset restartTimes";
544 | }
545 |
546 | void MainWindow::HandleIpAddress(const QString &ip)
547 | {
548 | ui->labelIp->setText(ip);
549 | }
550 |
551 | void MainWindow::UserLogOut()
552 | {
553 | // 用户主动注销
554 | dogcomController->LogOut();
555 | // 注销后应该是想重新登录或者换个号,因此显示出用户界面
556 | restoreAction->activate(restoreAction->Trigger);
557 | // 禁用注销按钮
558 | DisableLogOutButton(true);
559 | }
560 |
561 | void MainWindow::DisableLogOutButton(bool yes) {
562 | if (yes) {
563 | logOutAction->setDisabled(true);
564 | }
565 | else {
566 | logOutAction->setEnabled(true);
567 | }
568 | }
569 |
570 | void MainWindow::on_checkBoxNotShowWelcome_toggled(bool checked)
571 | {
572 | QSettings s(SETTINGS_FILE_NAME);
573 | s.setValue(ID_NOT_SHOW_WELCOME, checked);
574 | }
575 |
576 | void MainWindow::on_checkBoxHideLoginWindow_toggled(bool checked)
577 | {
578 | QSettings s(SETTINGS_FILE_NAME);
579 | s.setValue(ID_HIDE_WINDOW, checked);
580 | }
581 |
--------------------------------------------------------------------------------
/dogcom.cpp:
--------------------------------------------------------------------------------
1 | #include "dogcom.h"
2 |
3 | #include
4 | #include "constants.h"
5 | #include
6 | #include
7 | #include
8 | #include "encrypt/md4.h"
9 | #include "encrypt/md5.h"
10 | #include "encrypt/sha1.h"
11 |
12 | DogCom::DogCom(InterruptibleSleeper *s)
13 | {
14 | sleeper = s;
15 | log = true;
16 | }
17 |
18 | void DogCom::Stop()
19 | {
20 | sleeper->Interrupt();
21 | }
22 |
23 | void DogCom::FillConfig(QString a, QString p, QString m)
24 | {
25 | account = a;
26 | password = p;
27 | mac_addr = m;
28 | }
29 |
30 | void DogCom::print_packet(const char* msg, const unsigned char *packet, int length)
31 | {
32 | if (!log)
33 | return;
34 | QString x;
35 | for (int i = 0; i < length; i++)
36 | {
37 | x.append(QString::asprintf("%02x ", packet[i]));
38 | }
39 | qDebug("%s %s", msg, x.toStdString().c_str());
40 | }
41 |
42 | void DogCom::run()
43 | {
44 | // 每一个ReportOffline后边都跟着一个return语句,防止程序逻辑混乱
45 | // qDebug()<<"account:"<Sleep(200); // 0.2 sec
95 | qDebug() << "Wait for 0.2 second done.";
96 | int offLineReason;
97 | if ((offLineReason = dhcp_login(skt, seed, auth_information)) == -1)
98 | {
99 | // 登录成功
100 | emit ReportOnline();
101 | int keepalive_counter = 0;
102 | int first = 1;
103 | while (true)
104 | {
105 | if (!keepalive_1(skt, auth_information))
106 | {
107 | sleeper->Sleep(200); // 0.2 second
108 | if (keepalive_2(skt, &keepalive_counter, &first))
109 | {
110 | continue;
111 | }
112 | qDebug() << "Keepalive in loop.";
113 | if (!sleeper->Sleep(20000))
114 | {
115 | // 先注销再退出,这样DogcomSocket的析构函数一定会执行
116 | // 窗口函数部分处理是用户注销还是用户退出
117 | qDebug() << "Interruptted by user";
118 | emit ReportOffline(OFF_USER_LOGOUT);
119 | return;
120 | }
121 | }
122 | else
123 | {
124 | qDebug() << "ReportOffline OFF_TIMEOUT caused by keepalive_1";
125 | emit ReportOffline(OFF_TIMEOUT);
126 | return;
127 | }
128 | }
129 | }
130 | else
131 | {
132 | // 登录失败,提示用户失败原因
133 | emit ReportOffline(offLineReason);
134 | return;
135 | }
136 | }
137 | }
138 |
139 | bool DogCom::dhcp_challenge(DogcomSocket &socket, unsigned char seed[])
140 | {
141 | qDebug() << "Begin dhcp challenge...";
142 | unsigned char challenge_packet[20], recv_packet[1024];
143 | memset(challenge_packet, 0, 20);
144 | challenge_packet[0] = 0x01;
145 | challenge_packet[1] = 0x02;
146 | challenge_packet[2] = rand() & 0xff;
147 | challenge_packet[3] = rand() & 0xff;
148 | challenge_packet[4] = 0x68;
149 |
150 | qDebug() << "writing to dest...";
151 | if (socket.write((const char *)challenge_packet, 20) <= 0)
152 | {
153 | qCritical() << "Failed to send challenge";
154 | return false;
155 | }
156 | print_packet("[Challenge sent]", challenge_packet, 20);
157 |
158 | qDebug() << "reading from dest...";
159 | if (socket.read((char *)recv_packet) <= 0)
160 | {
161 | qCritical() << "Failed to recv";
162 | return false;
163 | }
164 | qDebug() << "read done";
165 | print_packet("[Challenge recv]", recv_packet, 76);
166 |
167 | if (recv_packet[0] != 0x02)
168 | {
169 | return false;
170 | }
171 | // recv_packet[20]到[24]是ip地址
172 | emit ReportIpAddress(recv_packet[20], recv_packet[21], recv_packet[22], recv_packet[23]);
173 |
174 | memcpy(seed, &recv_packet[4], 4);
175 | qDebug() << "End dhcp challenge";
176 | return true;
177 | }
178 |
179 | int DogCom::dhcp_login(DogcomSocket &socket, unsigned char seed[], unsigned char auth_information[])
180 | {
181 | unsigned int login_packet_size;
182 | unsigned int length_padding = 0;
183 | int JLU_padding = 0;
184 |
185 | if (password.length() > 8)
186 | {
187 | length_padding = password.length() - 8 + (length_padding % 2);
188 | if (password.length() != 16)
189 | {
190 | JLU_padding = password.length() / 4;
191 | }
192 | length_padding = 28 + password.length() - 8 + JLU_padding;
193 | }
194 | login_packet_size = 338 + length_padding;
195 | unsigned char *login_packet = new unsigned char[login_packet_size];
196 | // checksum1[8]改为checksum1[16]
197 | unsigned char recv_packet[1024],
198 | MD5A[16], MACxorMD5A[6], MD5B[16], checksum1[16], checksum2[4];
199 | memset(login_packet, 0, login_packet_size);
200 | memset(recv_packet, 0, 100);
201 |
202 | // build login-packet
203 | login_packet[0] = 0x03;
204 | login_packet[1] = 0x01;
205 | login_packet[2] = 0x00;
206 | login_packet[3] = account.length() + 20;
207 | int MD5A_len = 6 + password.length();
208 | unsigned char *MD5A_str = new unsigned char[MD5A_len];
209 | MD5A_str[0] = 0x03;
210 | MD5A_str[1] = 0x01;
211 | memcpy(MD5A_str + 2, seed, 4);
212 | memcpy(MD5A_str + 6, password.toStdString().c_str(), password.length());
213 | MD5(MD5A_str, MD5A_len, MD5A);
214 | memcpy(login_packet + 4, MD5A, 16);
215 | memcpy(login_packet + 20, account.toStdString().c_str(), account.length());
216 | login_packet[56] = 0x20;
217 | login_packet[57] = 0x03;
218 | uint64_t sum = 0;
219 | uint64_t mac = 0;
220 | // unpack
221 | for (int i = 0; i < 6; i++)
222 | {
223 | sum = (int)MD5A[i] + sum * 256;
224 | }
225 | // unpack
226 | // 将QString转换为unsigned char
227 | unsigned char mac_binary[6];
228 | char __c1, __c2;
229 | __c1 = mac_addr.at(0).toLatin1();
230 | __c2 = mac_addr.at(1).toLatin1();
231 | int __i1, __i2;
232 | auto charToInt = [](char x) {
233 | if (x >= '0' && x <= '9')
234 | return x - '0';
235 | return x - 'A' + 10;
236 | };
237 | __i1 = charToInt(__c1);
238 | __i2 = charToInt(__c2);
239 | mac_binary[0] = (__i1 << 4) + __i2;
240 | for (int i = 1; i < 6; i++)
241 | {
242 | __c1 = mac_addr.at(i * 3).toLatin1();
243 | __c2 = mac_addr.at(i * 3 + 1).toLatin1();
244 | __i1 = charToInt(__c1);
245 | __i2 = charToInt(__c2);
246 | mac_binary[i] = (__i1 << 4) + __i2;
247 | }
248 | // 将QString转换为unsigned char结束
249 | for (int i = 0; i < 6; i++)
250 | {
251 | mac = mac_binary[i] + mac * 256;
252 | }
253 | sum ^= mac;
254 | // pack
255 | for (int i = 6; i > 0; i--)
256 | {
257 | MACxorMD5A[i - 1] = (unsigned char)(sum % 256);
258 | sum /= 256;
259 | }
260 | memcpy(login_packet + 58, MACxorMD5A, sizeof(MACxorMD5A));
261 | int MD5B_len = 9 + password.length();
262 | unsigned char *MD5B_str = new unsigned char[MD5B_len];
263 | memset(MD5B_str, 0, MD5B_len);
264 | MD5B_str[0] = 0x01;
265 | memcpy(MD5B_str + 1, password.toStdString().c_str(), password.length());
266 | memcpy(MD5B_str + password.length() + 1, seed, 4);
267 | MD5(MD5B_str, MD5B_len, MD5B);
268 | memcpy(login_packet + 64, MD5B, 16);
269 | login_packet[80] = 0x01;
270 | unsigned char host_ip[4] = {0};
271 | memcpy(login_packet + 81, host_ip, 4);
272 | unsigned char checksum1_str[101], checksum1_tmp[4] = {0x14, 0x00, 0x07, 0x0b};
273 | memcpy(checksum1_str, login_packet, 97);
274 | memcpy(checksum1_str + 97, checksum1_tmp, 4);
275 | MD5(checksum1_str, 101, checksum1);
276 | memcpy(login_packet + 97, checksum1, 8);
277 | login_packet[105] = 0x01;
278 | memcpy(login_packet + 110, "LIYUANYUAN", 10);
279 | unsigned char PRIMARY_DNS[4];
280 | PRIMARY_DNS[0] = 10;
281 | PRIMARY_DNS[1] = 10;
282 | PRIMARY_DNS[2] = 10;
283 | PRIMARY_DNS[3] = 10;
284 | memcpy(login_packet + 142, PRIMARY_DNS, 4);
285 | unsigned char dhcp_server[4] = {0};
286 | memcpy(login_packet + 146, dhcp_server, 4);
287 | unsigned char OSVersionInfoSize[4] = {0x94};
288 | unsigned char OSMajor[4] = {0x05};
289 | unsigned char OSMinor[4] = {0x01};
290 | unsigned char OSBuild[4] = {0x28, 0x0a};
291 | unsigned char PlatformID[4] = {0x02};
292 | OSVersionInfoSize[0] = 0x94;
293 | OSMajor[0] = 0x06;
294 | OSMinor[0] = 0x02;
295 | OSBuild[0] = 0xf0;
296 | OSBuild[1] = 0x23;
297 | PlatformID[0] = 0x02;
298 | unsigned char ServicePack[40] = {0x33, 0x64, 0x63, 0x37, 0x39, 0x66, 0x35, 0x32, 0x31, 0x32, 0x65, 0x38, 0x31, 0x37,
299 | 0x30, 0x61, 0x63, 0x66, 0x61, 0x39, 0x65, 0x63, 0x39, 0x35, 0x66, 0x31, 0x64, 0x37,
300 | 0x34, 0x39, 0x31, 0x36, 0x35, 0x34, 0x32, 0x62, 0x65, 0x37, 0x62, 0x31};
301 | unsigned char hostname[9] = {0x44, 0x72, 0x43, 0x4f, 0x4d, 0x00, 0xcf, 0x07, 0x68};
302 | memcpy(login_packet + 182, hostname, 9);
303 | memcpy(login_packet + 246, ServicePack, 40);
304 | memcpy(login_packet + 162, OSVersionInfoSize, 4);
305 | memcpy(login_packet + 166, OSMajor, 4);
306 | memcpy(login_packet + 170, OSMinor, 4);
307 | memcpy(login_packet + 174, OSBuild, 4);
308 | memcpy(login_packet + 178, PlatformID, 4);
309 | login_packet[310] = 0x68;
310 | login_packet[311] = 0x00;
311 | int counter = 312;
312 | unsigned int ror_padding = 0;
313 | if (password.length() <= 8)
314 | {
315 | ror_padding = 8 - password.length();
316 | }
317 | else
318 | {
319 | if ((password.length() - 8) % 2)
320 | {
321 | ror_padding = 1;
322 | }
323 | ror_padding = JLU_padding;
324 | }
325 | MD5(MD5A_str, MD5A_len, MD5A);
326 | login_packet[counter + 1] = password.length();
327 | counter += 2;
328 | for (int i = 0, x = 0; i < password.length(); i++)
329 | {
330 | x = (int)MD5A[i] ^ (int)password.at(i).toLatin1();
331 | login_packet[counter + i] = (unsigned char)(((x << 3) & 0xff) + (x >> 5));
332 | }
333 | counter += password.length();
334 | login_packet[counter] = 0x02;
335 | login_packet[counter + 1] = 0x0c;
336 | unsigned char *checksum2_str = new unsigned char[counter + 18]; // [counter + 14 + 4]
337 | memset(checksum2_str, 0, counter + 18);
338 | unsigned char checksum2_tmp[6] = {0x01, 0x26, 0x07, 0x11};
339 | memcpy(checksum2_str, login_packet, counter + 2);
340 | memcpy(checksum2_str + counter + 2, checksum2_tmp, 6);
341 | memcpy(checksum2_str + counter + 8, mac_binary, 6);
342 | sum = 1234;
343 | uint64_t ret = 0;
344 | for (int i = 0; i < counter + 14; i += 4)
345 | {
346 | ret = 0;
347 | for (int j = 4; j > 0; j--)
348 | {
349 | ret = ret * 256 + (int)checksum2_str[i + j - 1];
350 | }
351 | sum ^= ret;
352 | }
353 | sum = (1968 * sum) & 0xffffffff;
354 | for (int j = 0; j < 4; j++)
355 | {
356 | checksum2[j] = (unsigned char)(sum >> (j * 8) & 0xff);
357 | }
358 | memcpy(login_packet + counter + 2, checksum2, 4);
359 | memcpy(login_packet + counter + 8, mac_binary, 6);
360 | login_packet[counter + ror_padding + 14] = 0xe9;
361 | login_packet[counter + ror_padding + 15] = 0x13;
362 | login_packet[counter + ror_padding + 14] = 0x60;
363 | login_packet[counter + ror_padding + 15] = 0xa2;
364 |
365 | qDebug() << "login_packet_size:" << login_packet_size;
366 | socket.write((const char *)login_packet, login_packet_size);
367 | print_packet("[Login sent]", login_packet, login_packet_size);
368 |
369 | if (socket.read((char *)recv_packet) <= 0)
370 | {
371 | qCritical() << "Failed to recv data";
372 | return OFF_TIMEOUT;
373 | }
374 |
375 | if (recv_packet[0] != 0x04)
376 | {
377 | print_packet("[Login recv]", recv_packet, 100);
378 | qDebug() << "<<< Login failed >>>";
379 | if (recv_packet[0] == 0x05)
380 | {
381 | switch (recv_packet[4])
382 | {
383 | case LOGIN_CHECK_MAC:
384 | return OFF_CHECK_MAC;
385 | case LOGIN_SERVER_BUSY:
386 | return OFF_SERVER_BUSY;
387 | case LOGIN_WRONG_PASS:
388 | return OFF_WRONG_PASS;
389 | case LOGIN_NOT_ENOUGH:
390 | return OFF_NOT_ENOUGH;
391 | case LOGIN_FREEZE_UP:
392 | return OFF_FREEZE_UP;
393 | case LOGIN_NOT_ON_THIS_IP:
394 | return OFF_NOT_ON_THIS_IP;
395 | case LOGIN_NOT_ON_THIS_MAC:
396 | return OFF_NOT_ON_THIS_MAC;
397 | case LOGIN_TOO_MUCH_IP:
398 | return OFF_TOO_MUCH_IP;
399 | // 升级客户端这个密码错了就会弹出俩
400 | case LOGIN_UPDATE_CLIENT:
401 | return OFF_WRONG_PASS;
402 | case LOGIN_NOT_ON_THIS_IP_MAC:
403 | return OFF_NOT_ON_THIS_IP_MAC;
404 | case LOGIN_MUST_USE_DHCP:
405 | return OFF_MUST_USE_DHCP;
406 | default:
407 | return OFF_UNKNOWN;
408 | }
409 | }
410 | return OFF_UNKNOWN;
411 | }
412 | else
413 | {
414 | print_packet("[Login recv]", recv_packet, 100);
415 | qDebug() << "<<< Logged in >>>";
416 | }
417 |
418 | memcpy(auth_information, &recv_packet[23], 16);
419 |
420 | return -1;
421 | }
422 |
423 | int DogCom::keepalive_1(DogcomSocket &socket, unsigned char auth_information[])
424 | {
425 | unsigned char keepalive_1_packet1[8] = {0x07, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00};
426 | unsigned char recv_packet1[1024], keepalive_1_packet2[38], recv_packet2[1024];
427 | memset(keepalive_1_packet2, 0, 38);
428 | if (socket.write((const char *)keepalive_1_packet1, 8) <= 0)
429 | {
430 | qCritical() << "Failed to send data";
431 | return 1;
432 | }
433 | qDebug() << "[Keepalive1 sent]";
434 | // print_packet("[Keepalive1 sent]",keepalive_1_packet1,42);
435 | while (1)
436 | {
437 | if (socket.read((char *)recv_packet1) <= 0)
438 | {
439 | qCritical() << "Failed to recv data";
440 | return 1;
441 | }
442 | else
443 | {
444 | qDebug() << "[Keepalive1 challenge_recv]";
445 | // print_packet("[Keepalive1 challenge_recv]",recv_packet1,100);
446 | if (recv_packet1[0] == 0x07)
447 | {
448 | break;
449 | }
450 | else if (recv_packet1[0] == 0x4d)
451 | {
452 | qDebug() << "Get notice packet.";
453 | continue;
454 | }
455 | else
456 | {
457 | qDebug() << "Bad keepalive1 challenge response received.";
458 | return 1;
459 | }
460 | }
461 | }
462 |
463 | unsigned char keepalive1_seed[4] = {0};
464 | int encrypt_type;
465 | unsigned char crc[8] = {0};
466 | memcpy(keepalive1_seed, &recv_packet1[8], 4);
467 | encrypt_type = keepalive1_seed[0] & 3;
468 | gen_crc(keepalive1_seed, encrypt_type, crc);
469 | keepalive_1_packet2[0] = 0xff;
470 | memcpy(keepalive_1_packet2 + 8, keepalive1_seed, 4);
471 | memcpy(keepalive_1_packet2 + 12, crc, 8);
472 | memcpy(keepalive_1_packet2 + 20, auth_information, 16);
473 | keepalive_1_packet2[36] = rand() & 0xff;
474 | keepalive_1_packet2[37] = rand() & 0xff;
475 |
476 | if (socket.write((const char *)keepalive_1_packet2, 42) <= 0){
477 | qCritical()<<"Failed to send data";
478 | return 1;
479 | }
480 |
481 | if (socket.read((char *)recv_packet2) <= 0)
482 | {
483 | qCritical() << "Failed to recv data";
484 | return 1;
485 | }
486 | else
487 | {
488 | qDebug() << "[Keepalive1 recv]";
489 | // print_packet("[Keepalive1 recv]",recv_packet2,100);
490 |
491 | if (recv_packet2[0] != 0x07)
492 | {
493 | qDebug() << "Bad keepalive1 response received.";
494 | return 1;
495 | }
496 | }
497 | return 0;
498 | }
499 |
500 | int DogCom::keepalive_2(DogcomSocket &socket, int *keepalive_counter, int *first)
501 | {
502 | unsigned char keepalive_2_packet[40], recv_packet[1024], tail[4];
503 |
504 | if (*first)
505 | {
506 | // send the file packet
507 | memset(keepalive_2_packet, 0, 40);
508 | keepalive_2_packetbuilder(keepalive_2_packet, *keepalive_counter % 0xFF, *first, 1);
509 | (*keepalive_counter)++;
510 |
511 | if (socket.write((const char *)keepalive_2_packet, 40) <= 0){
512 | qCritical()<<"Failed to send data";
513 | return 1;
514 | }
515 |
516 | qDebug() << "[Keepalive2_file sent]";
517 | // print_packet("[Keepalive2_file sent]",keepalive_2_packet,40);
518 | if (socket.read((char *)recv_packet) <= 0)
519 | {
520 | qCritical() << "Failed to recv data";
521 | return 1;
522 | }
523 | qDebug() << "[Keepalive2_file recv]";
524 | // print_packet("[Keepalive2_file recv]",recv_packet,40);
525 |
526 | if (recv_packet[0] == 0x07)
527 | {
528 | if (recv_packet[2] == 0x10)
529 | {
530 | qDebug() << "Filepacket received.";
531 | }
532 | else if (recv_packet[2] != 0x28)
533 | {
534 | qDebug() << "Bad keepalive2 response received.";
535 | return 1;
536 | }
537 | }
538 | else
539 | {
540 | qDebug() << "Bad keepalive2 response received.";
541 | return 1;
542 | }
543 | }
544 |
545 | // send the first packet
546 | *first = 0;
547 | memset(keepalive_2_packet, 0, 40);
548 | keepalive_2_packetbuilder(keepalive_2_packet, *keepalive_counter % 0xFF, *first, 1);
549 | (*keepalive_counter)++;
550 | socket.write((const char *)keepalive_2_packet, 40);
551 |
552 | qDebug() << "[Keepalive2_A sent]";
553 | // print_packet("[Keepalive2_A sent]",keepalive_2_packet,40);
554 |
555 | if (socket.read((char *)recv_packet) <= 0)
556 | {
557 | qCritical() << "Failed to recv data";
558 | return 1;
559 | }
560 |
561 | if (recv_packet[0] == 0x07)
562 | {
563 | if (recv_packet[2] != 0x28)
564 | {
565 | printf("Bad keepalive2 response received.\n");
566 | return 1;
567 | }
568 | }
569 | else
570 | {
571 | printf("Bad keepalive2 response received.\n");
572 | return 1;
573 | }
574 | memcpy(tail, &recv_packet[16], 4);
575 |
576 | memset(keepalive_2_packet, 0, 40);
577 | keepalive_2_packetbuilder(keepalive_2_packet, *keepalive_counter % 0xFF, *first, 3);
578 | memcpy(keepalive_2_packet + 16, tail, 4);
579 | (*keepalive_counter)++;
580 | socket.write((const char *)keepalive_2_packet, 40);
581 |
582 | qDebug() << "[Keepalive2_C sent]";
583 | // print_packet("[Keepalive2_C sent]",keepalive_2_packet,40);
584 |
585 | if (socket.read((char *)recv_packet) <= 0)
586 | {
587 | qCritical() << "Failed to recv data";
588 | return 1;
589 | }
590 | qDebug() << "[Keepalive2_D recv]";
591 | // print_packet("[Keepalive2_D recv]",recv_packet,40);
592 |
593 | if (recv_packet[0] == 0x07)
594 | {
595 | if (recv_packet[2] != 0x28)
596 | {
597 | qDebug() << "Bad keepalive2 response received.";
598 | return 1;
599 | }
600 | }
601 | else
602 | {
603 | qDebug() << "Bad keepalive2 response received.";
604 | return 1;
605 | }
606 |
607 | return 0;
608 | }
609 |
610 | void DogCom::gen_crc(unsigned char seed[], int encrypt_type, unsigned char crc[])
611 | {
612 | if (encrypt_type == 0)
613 | {
614 | char DRCOM_DIAL_EXT_PROTO_CRC_INIT[4] = {(char)0xc7, (char)0x2f, (char)0x31, (char)0x01};
615 | char gencrc_tmp[4] = {0x7e};
616 | memcpy(crc, DRCOM_DIAL_EXT_PROTO_CRC_INIT, 4);
617 | memcpy(crc + 4, gencrc_tmp, 4);
618 | }
619 | else if (encrypt_type == 1)
620 | {
621 | unsigned char hash[32] = {0};
622 | MD5(seed, 4, hash);
623 | crc[0] = hash[2];
624 | crc[1] = hash[3];
625 | crc[2] = hash[8];
626 | crc[3] = hash[9];
627 | crc[4] = hash[5];
628 | crc[5] = hash[6];
629 | crc[6] = hash[13];
630 | crc[7] = hash[14];
631 | }
632 | else if (encrypt_type == 2)
633 | {
634 | unsigned char hash[32] = {0};
635 | MD4(seed, 4, hash);
636 | crc[0] = hash[1];
637 | crc[1] = hash[2];
638 | crc[2] = hash[8];
639 | crc[3] = hash[9];
640 | crc[4] = hash[4];
641 | crc[5] = hash[5];
642 | crc[6] = hash[11];
643 | crc[7] = hash[12];
644 | }
645 | else if (encrypt_type == 3)
646 | {
647 | unsigned char hash[32] = {0};
648 | SHA1(seed, 4, hash);
649 | crc[0] = hash[2];
650 | crc[1] = hash[3];
651 | crc[2] = hash[9];
652 | crc[3] = hash[10];
653 | crc[4] = hash[5];
654 | crc[5] = hash[6];
655 | crc[6] = hash[15];
656 | crc[7] = hash[16];
657 | }
658 | }
659 |
660 | void DogCom::keepalive_2_packetbuilder(unsigned char keepalive_2_packet[], int keepalive_counter, int filepacket, int type)
661 | {
662 | keepalive_2_packet[0] = 0x07;
663 | keepalive_2_packet[1] = keepalive_counter;
664 | keepalive_2_packet[2] = 0x28;
665 | keepalive_2_packet[4] = 0x0b;
666 | keepalive_2_packet[5] = type;
667 | if (filepacket)
668 | {
669 | keepalive_2_packet[6] = 0x0f;
670 | keepalive_2_packet[7] = 0x27;
671 | }
672 | else
673 | {
674 | unsigned char KEEP_ALIVE_VERSION[2];
675 | KEEP_ALIVE_VERSION[0] = 0xDC;
676 | KEEP_ALIVE_VERSION[1] = 0x02;
677 | memcpy(keepalive_2_packet + 6, KEEP_ALIVE_VERSION, 2);
678 | }
679 | keepalive_2_packet[8] = 0x2f;
680 | keepalive_2_packet[9] = 0x12;
681 | if (type == 3)
682 | {
683 | unsigned char host_ip[4] = {0};
684 | memcpy(keepalive_2_packet + 28, host_ip, 4);
685 | }
686 | }
687 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------