├── .gitignore ├── DogcomSocket.cpp ├── DogcomSocket.h ├── DrCOM_JLU_Qt.pro ├── DrCOM_JLU_Qt.qrc ├── LICENSE ├── README.md ├── constants.h ├── dogcom.cpp ├── dogcom.h ├── dogcomcontroller.cpp ├── dogcomcontroller.h ├── encrypt ├── EncryptData.cpp ├── EncryptData.h ├── md4.cpp ├── md4.h ├── md5.cpp ├── md5.h ├── sha1.cpp └── sha1.h ├── images ├── icon.ico ├── offline.png └── online.png ├── interruptiblesleeper.cpp ├── interruptiblesleeper.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── singleinstance ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Windows.md ├── singleapplication.cpp ├── singleapplication.h ├── singleapplication.pri ├── singleapplication_p.cpp └── singleapplication_p.h └── ts ├── DrCOM_zh_CN.qm └── DrCOM_zh_CN.ts /.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/ -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DrCOM_JLU_Qt.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/offline.png 4 | images/online.png 5 | ts/DrCOM_zh_CN.qm 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 6 | # 功能对比 7 | | 功能 | 官方 | 本版 | 说明 | 8 | |----------------------|------|------|------------------------------------------------------------------| 9 | | 记住密码 自动登录 | √ | √ | | 10 | | 密文保存密码 | √ | √ | Windows平台上采用 Windows 提供的 数据保护 API(DPAPI),保护仅当前账户能够解密数据,其他平台暂时使用简单的XOR加密 | 11 | | <<<已知问题>>> | | | | 12 | | 多语言支持 | √ | | 或许不会改进了... | 13 | | 被顶掉 | √ | | 警告!巨大缺陷!本版掉线后会自动重启重新登录!所以顶不掉!待改进 | 14 | | 释放socket | √ | | 不是每次关机前都能保证释放socket,导致有时候会报端口已占用错误,待改进 | 15 | | <<<优势>>> | | | | 16 | | 打开速度 | 慢 | 快 | 我也不知道为什么官版打开那么慢 | 17 | | 单实例 | | √ | 开机自启慢的话可以直接打开不会报错说已经在运行 | 18 | | 快速注销 | | √ | 官方版是真·注销,本版是直接关闭socket,所以不需要等20s的发包周期 | 19 | | 托盘图标无bug | | √ | 不知道你们有没有碰到过官方win版托盘有俩图标的bug | 20 | | 可选不弹出校园网之窗 | | √ | | 21 | | 完全隐藏登录窗口 | | √ | | 22 | | 适配高分屏 | | √ | | 23 | | 快速重启客户端 | | √ | 有时候重启功能不好使,点了重启当前退了没有蹦出来新的,待改进 | 24 | | win版不需要管理员 | | √ | | 25 | | linux版最小化到托盘 | | √ | | 26 | | linux版不需要root | | √ | | 27 | | 不限制NAT | | √ | 并不支持有违校方意愿的做法,请自行承担后果 | 28 | 29 | # 注意事项 30 | - 掉线后客户端自动重启重连尝试三次。自动重启登录成功后不弹窗口只最小化到托盘。注:自动重启功能依赖于“记住我”选项的勾选,否则没有账户密码自行重启也并没有什么用 31 | - 连接JLU.PC登录的时候mac地址随便填就可以,或者随便选一个网卡也可以,只有有线网要求mac地址和网络中心的一致 32 | 33 | # 截图 34 | > WIN: 35 | 36 | ![n9c6aQ.png](https://s2.ax1x.com/2019/09/02/n9c6aQ.png) 37 | 38 | > UBUNTU: 39 | 40 | ![nCtJ2Q.png](https://s2.ax1x.com/2019/09/02/nCtJ2Q.png) 41 | 42 | > Ubuntu 18不显示托盘图标的bug的解决方案: 43 | > [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) 44 | 45 | # 感谢 46 | 47 | **图标作者** 48 | > [https://github.com/lyj3516](https://github.com/lyj3516) 49 | 50 | **jlu的drcom协议细节** 51 | > [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) 52 | 53 | **唯一实例** 54 | > [https://github.com/itay-grudev/SingleApplication](https://github.com/itay-grudev/SingleApplication) 55 | 56 | # 特别感谢 57 | **登录部分复制了jlu部分代码** 58 | > [https://github.com/mchome/dogcom](https://github.com/mchome/dogcom) 59 | 60 | # 许可证 61 | 62 | [GNU Affero General Public License v3.0](https://github.com/code4lala/drcom-jlu-qt/blob/master/LICENSE) 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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.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 | -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/e0ae76749122247105afc3bc9ab46d2952d393ea/images/icon.ico -------------------------------------------------------------------------------- /images/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/e0ae76749122247105afc3bc9ab46d2952d393ea/images/offline.png -------------------------------------------------------------------------------- /images/online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/e0ae76749122247105afc3bc9ab46d2952d393ea/images/online.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ts/DrCOM_zh_CN.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4lala/drcom-jlu-qt/e0ae76749122247105afc3bc9ab46d2952d393ea/ts/DrCOM_zh_CN.qm -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------