mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			97 Commits
		
	
	
		
			v1.4.1
			...
			e8259b841b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e8259b841b | ||
|  | 939f2bd94a | ||
|  | abffa21268 | ||
|  | 99342372d2 | ||
|  | cd8d794655 | ||
|  | 705b03c0bb | ||
|  | 7bef2ccc68 | ||
|  | bffc5d733b | ||
|  | 8ca2610550 | ||
|  | acf184134a | ||
|  | 4a3f74f1c3 | ||
|  | b39983a0f8 | ||
|  | d6fa32d95d | ||
|  | 7c299f6e68 | ||
|  | 25e8ccd07e | ||
|  | 092123b075 | ||
|  | feb2956cc1 | ||
|  | d356839859 | ||
|  | 2ff39fd14e | ||
|  | c3d300c807 | ||
|  | ef43d10a70 | ||
|  | bd73466e48 | ||
|  | cc2c0ae34e | ||
|  | 9207f7a798 | ||
|  | 2069b7fd8e | ||
|  | f552ef6204 | ||
|  | 2761f7521a | ||
|  | c3939a774b | ||
|  | 49e5242bf2 | ||
|  | 3181d37b20 | ||
|  | 6d788a5e91 | ||
|  | 74c7549444 | ||
|  | c52ccb87f1 | ||
|  | fab25848e1 | ||
|  | 75a24c89cd | ||
|  | 15a73b7dba | ||
|  | 0adeaf9c28 | ||
|  | fe64b2cd19 | ||
|  | 5788474d7e | ||
|  | 3559834762 | ||
|  | f9926b4026 | ||
|  | cb457c4b09 | ||
|  | 5ebfe9b0f4 | ||
|  | 35266dd8bf | ||
|  | bf03d375e0 | ||
|  | 6cf909e34f | ||
|  | 343a6d03c1 | ||
|  | fab8e7591e | ||
|  | 5a485197b7 | ||
|  | 7bc02a4208 | ||
|  | 3067e6e911 | ||
|  | 5db77e8404 | ||
|  | 5714063457 | ||
|  | 41f88ed2e0 | ||
|  | 4fada9bd14 | ||
|  | b57fb993ca | ||
|  | f6d06ed978 | ||
|  | cc67de3a2b | ||
|  | e2d28c83b2 | ||
|  | a489c5881b | ||
|  | 44fd2f1d3f | ||
|  | 9c9b42b87f | ||
|  | fb2b148b72 | ||
|  | 64bec9660a | ||
|  | 0619e91bf5 | ||
|  | 048aa4799f | ||
|  | db0e8b801d | ||
|  | d03bbc339e | ||
|  | 1312d54d08 | ||
|  | 39f99d9143 | ||
|  | 7a4eb0def3 | ||
|  | d9b2094edd | ||
|  | e6118af9f3 | ||
|  | 108b4be3ec | ||
|  | 65c59e47ec | ||
|  | 177da7f3a2 | ||
|  | d5cd90373b | ||
|  | ffa99d3783 | ||
|  | 4940830885 | ||
|  | ad178fe56c | ||
|  | 829298bb84 | ||
|  | 8fe717d844 | ||
|  | dffbc64ef5 | ||
|  | b99c5a8391 | ||
|  | c2f7576d10 | ||
|  | 4327235093 | ||
|  | 0699878b92 | ||
|  | e3aba11506 | ||
|  | ff58258d5c | ||
|  | 991cf25a7b | ||
|  | 02c70150ba | ||
|  | 28d8321958 | ||
|  | e1c9180cae | ||
|  | 57df34fd1e | ||
|  | 04d180e11a | ||
|  | 6d3b127569 | ||
|  | e72b25e415 | 
| @@ -9,8 +9,5 @@ trim_trailing_whitespace=true | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
|  | ||||
| [{VERSION,VERSION_SUFFIX}] | ||||
| insert_final_newline = false | ||||
|  | ||||
| [*.sh] | ||||
| indent_style = tab | ||||
|   | ||||
							
								
								
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ name: Build | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - master | ||||
|       - develop | ||||
|     tags: | ||||
|       - "v*.*.*" | ||||
| @@ -17,7 +18,7 @@ jobs: | ||||
|   build: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         os: [ubuntu-18.04, ubuntu-20.04] | ||||
|         os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04] | ||||
|  | ||||
|     runs-on: ${{ matrix.os }} | ||||
|  | ||||
| @@ -30,6 +31,8 @@ jobs: | ||||
|       - name: Build | ||||
|         run: | | ||||
|           ./scripts/install-ubuntu.sh | ||||
|           # assert no library missing | ||||
|           test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0 | ||||
|  | ||||
|   snapshot-archive-all: | ||||
|     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||
| @@ -51,6 +54,10 @@ jobs: | ||||
|         run: | | ||||
|           ./scripts/snapshot-archive-all.sh | ||||
|  | ||||
|       - name: Verify debian package | ||||
|         run: | | ||||
|           ./scripts/verify-debian-package.sh | ||||
|  | ||||
|       - uses: actions/upload-artifact@v2 | ||||
|         with: | ||||
|           name: snapshot-source-code | ||||
| @@ -93,13 +100,11 @@ jobs: | ||||
|           path: artifacts | ||||
|  | ||||
|       - name: Publish AUR package | ||||
|         env: | ||||
|           VERSION: $(cat ./artifacts/VERSION) | ||||
|         uses: yuezk/github-actions-deploy-aur@update-pkgver | ||||
|         with: | ||||
|           pkgname: globalprotect-openconnect-git | ||||
|           pkgbuild: ./artifacts/aur/PKGBUILD | ||||
|           assets: ./artifacts/aur/*.tar.gz | ||||
|           assets: ./artifacts/aur/gp.install | ||||
|           update_pkgver: true | ||||
|           commit_username: ${{ secrets.AUR_USERNAME }} | ||||
|           commit_email: ${{ secrets.AUR_EMAIL }} | ||||
| @@ -126,7 +131,8 @@ jobs: | ||||
|           files: ./artifacts/obs/* | ||||
|  | ||||
|   snapshot-snap: | ||||
|     if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||
|     # if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} | ||||
|     if: ${{ false }} | ||||
|     needs: snapshot-archive-all | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
| @@ -174,6 +180,10 @@ jobs: | ||||
|         run: | | ||||
|           ./scripts/release-archive-all.sh | ||||
|  | ||||
|       - name: Verify debian package | ||||
|         run: | | ||||
|           ./scripts/verify-debian-package.sh | ||||
|  | ||||
|       - uses: actions/upload-artifact@v2 | ||||
|         with: | ||||
|           name: release-source-code | ||||
| @@ -216,13 +226,11 @@ jobs: | ||||
|           path: artifacts | ||||
|  | ||||
|       - name: Publish AUR package | ||||
|         env: | ||||
|           VERSION: $(cat ./artifacts/VERSION) | ||||
|         uses: yuezk/github-actions-deploy-aur@update-pkgver | ||||
|         with: | ||||
|           pkgname: globalprotect-openconnect-git | ||||
|           pkgbuild: ./artifacts/aur/PKGBUILD | ||||
|           assets: ./artifacts/aur/*.tar.gz | ||||
|           assets: ./artifacts/aur/gp.install | ||||
|           update_pkgver: true | ||||
|           commit_username: ${{ secrets.AUR_USERNAME }} | ||||
|           commit_email: ${{ secrets.AUR_EMAIL }} | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -29,3 +29,5 @@ jobs: | ||||
|       - name: Build | ||||
|         run: | | ||||
|           ./scripts/install-ubuntu.sh | ||||
|           # assert no library missing | ||||
|           test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0 | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -8,6 +8,7 @@ build | ||||
| artifacts | ||||
|  | ||||
| .cmake | ||||
| .idea | ||||
|  | ||||
| # Auto generated DBus files | ||||
| *_adaptor.cpp | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -5,3 +5,6 @@ | ||||
| [submodule "plog"] | ||||
| 	path = 3rdparty/plog | ||||
| 	url = https://github.com/SergiusTheBest/plog.git | ||||
| [submodule "3rdparty/qtkeychain"] | ||||
| 	path = 3rdparty/qtkeychain | ||||
| 	url = git@github.com:frankosterfeld/qtkeychain.git | ||||
|   | ||||
							
								
								
									
										12
									
								
								3rdparty/inih/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								3rdparty/inih/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| cmake_minimum_required(VERSION 3.10.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
| project(inih) | ||||
|  | ||||
| add_library(inih STATIC | ||||
|     ini.h | ||||
|     ini.c | ||||
|     cpp/INIReader.h | ||||
|     cpp/INIReader.cpp | ||||
| ) | ||||
| target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp") | ||||
							
								
								
									
										27
									
								
								3rdparty/inih/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								3rdparty/inih/LICENSE.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
|  | ||||
| The "inih" library is distributed under the New BSD license: | ||||
|  | ||||
| Copyright (c) 2009, Ben Hoyt | ||||
| All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|     * Redistributions of source code must retain the above copyright | ||||
|       notice, this list of conditions and the following disclaimer. | ||||
|     * Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|     * Neither the name of Ben Hoyt nor the names of its contributors | ||||
|       may be used to endorse or promote products derived from this software | ||||
|       without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY | ||||
| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY | ||||
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										116
									
								
								3rdparty/inih/cpp/INIReader.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								3rdparty/inih/cpp/INIReader.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| // Read an INI file into easy-to-access name/value pairs. | ||||
|  | ||||
| // SPDX-License-Identifier: BSD-3-Clause | ||||
|  | ||||
| // Copyright (C) 2009-2020, Ben Hoyt | ||||
|  | ||||
| // inih and INIReader are released under the New BSD license (see LICENSE.txt). | ||||
| // Go to the project home page for more info: | ||||
| // | ||||
| // https://github.com/benhoyt/inih | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cctype> | ||||
| #include <cstdlib> | ||||
| #include "../ini.h" | ||||
| #include "INIReader.h" | ||||
|  | ||||
| using std::string; | ||||
|  | ||||
| INIReader::INIReader(const string& filename) | ||||
| { | ||||
|     _error = ini_parse(filename.c_str(), ValueHandler, this); | ||||
| } | ||||
|  | ||||
| INIReader::INIReader(const char *buffer, size_t buffer_size) | ||||
| { | ||||
|   string content(buffer, buffer_size); | ||||
|   _error = ini_parse_string(content.c_str(), ValueHandler, this); | ||||
| } | ||||
|  | ||||
| int INIReader::ParseError() const | ||||
| { | ||||
|     return _error; | ||||
| } | ||||
|  | ||||
| string INIReader::Get(const string& section, const string& name, const string& default_value) const | ||||
| { | ||||
|     string key = MakeKey(section, name); | ||||
|     // Use _values.find() here instead of _values.at() to support pre C++11 compilers | ||||
|     return _values.count(key) ? _values.find(key)->second : default_value; | ||||
| } | ||||
|  | ||||
| string INIReader::GetString(const string& section, const string& name, const string& default_value) const | ||||
| { | ||||
|     const string str = Get(section, name, ""); | ||||
|     return str.empty() ? default_value : str; | ||||
| } | ||||
|  | ||||
| long INIReader::GetInteger(const string& section, const string& name, long default_value) const | ||||
| { | ||||
|     string valstr = Get(section, name, ""); | ||||
|     const char* value = valstr.c_str(); | ||||
|     char* end; | ||||
|     // This parses "1234" (decimal) and also "0x4D2" (hex) | ||||
|     long n = strtol(value, &end, 0); | ||||
|     return end > value ? n : default_value; | ||||
| } | ||||
|  | ||||
| double INIReader::GetReal(const string& section, const string& name, double default_value) const | ||||
| { | ||||
|     string valstr = Get(section, name, ""); | ||||
|     const char* value = valstr.c_str(); | ||||
|     char* end; | ||||
|     double n = strtod(value, &end); | ||||
|     return end > value ? n : default_value; | ||||
| } | ||||
|  | ||||
| bool INIReader::GetBoolean(const string& section, const string& name, bool default_value) const | ||||
| { | ||||
|     string valstr = Get(section, name, ""); | ||||
|     // Convert to lower case to make string comparisons case-insensitive | ||||
|     std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); | ||||
|     if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") | ||||
|         return true; | ||||
|     else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") | ||||
|         return false; | ||||
|     else | ||||
|         return default_value; | ||||
| } | ||||
|  | ||||
| bool INIReader::HasSection(const string& section) const | ||||
| { | ||||
|     const string key = MakeKey(section, ""); | ||||
|     std::map<string, string>::const_iterator pos = _values.lower_bound(key); | ||||
|     if (pos == _values.end()) | ||||
|         return false; | ||||
|     // Does the key at the lower_bound pos start with "section"? | ||||
|     return pos->first.compare(0, key.length(), key) == 0; | ||||
| } | ||||
|  | ||||
| bool INIReader::HasValue(const string& section, const string& name) const | ||||
| { | ||||
|     string key = MakeKey(section, name); | ||||
|     return _values.count(key); | ||||
| } | ||||
|  | ||||
| string INIReader::MakeKey(const string& section, const string& name) | ||||
| { | ||||
|     string key = section + "=" + name; | ||||
|     // Convert to lower case to make section/name lookups case-insensitive | ||||
|     std::transform(key.begin(), key.end(), key.begin(), ::tolower); | ||||
|     return key; | ||||
| } | ||||
|  | ||||
| int INIReader::ValueHandler(void* user, const char* section, const char* name, | ||||
|                             const char* value) | ||||
| { | ||||
|     if (!name)  // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled | ||||
|         return 1; | ||||
|     INIReader* reader = static_cast<INIReader*>(user); | ||||
|     string key = MakeKey(section, name); | ||||
|     if (reader->_values[key].size() > 0) | ||||
|         reader->_values[key] += "\n"; | ||||
|     reader->_values[key] += value ? value : ""; | ||||
|     return 1; | ||||
| } | ||||
							
								
								
									
										94
									
								
								3rdparty/inih/cpp/INIReader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								3rdparty/inih/cpp/INIReader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| // Read an INI file into easy-to-access name/value pairs. | ||||
|  | ||||
| // SPDX-License-Identifier: BSD-3-Clause | ||||
|  | ||||
| // Copyright (C) 2009-2020, Ben Hoyt | ||||
|  | ||||
| // inih and INIReader are released under the New BSD license (see LICENSE.txt). | ||||
| // Go to the project home page for more info: | ||||
| // | ||||
| // https://github.com/benhoyt/inih | ||||
|  | ||||
| #ifndef INIREADER_H | ||||
| #define INIREADER_H | ||||
|  | ||||
| #include <map> | ||||
| #include <string> | ||||
|  | ||||
| // Visibility symbols, required for Windows DLLs | ||||
| #ifndef INI_API | ||||
| #if defined _WIN32 || defined __CYGWIN__ | ||||
| #	ifdef INI_SHARED_LIB | ||||
| #		ifdef INI_SHARED_LIB_BUILDING | ||||
| #			define INI_API __declspec(dllexport) | ||||
| #		else | ||||
| #			define INI_API __declspec(dllimport) | ||||
| #		endif | ||||
| #	else | ||||
| #		define INI_API | ||||
| #	endif | ||||
| #else | ||||
| #	if defined(__GNUC__) && __GNUC__ >= 4 | ||||
| #		define INI_API __attribute__ ((visibility ("default"))) | ||||
| #	else | ||||
| #		define INI_API | ||||
| #	endif | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| // Read an INI file into easy-to-access name/value pairs. (Note that I've gone | ||||
| // for simplicity here rather than speed, but it should be pretty decent.) | ||||
| class INIReader | ||||
| { | ||||
| public: | ||||
|     // Construct INIReader and parse given filename. See ini.h for more info | ||||
|     // about the parsing. | ||||
|     INI_API explicit INIReader(const std::string& filename); | ||||
|  | ||||
|     // Construct INIReader and parse given buffer. See ini.h for more info | ||||
|     // about the parsing. | ||||
|     INI_API explicit INIReader(const char *buffer, size_t buffer_size); | ||||
|  | ||||
|     // Return the result of ini_parse(), i.e., 0 on success, line number of | ||||
|     // first error on parse error, or -1 on file open error. | ||||
|     INI_API int ParseError() const; | ||||
|  | ||||
|     // Get a string value from INI file, returning default_value if not found. | ||||
|     INI_API std::string Get(const std::string& section, const std::string& name, | ||||
|                     const std::string& default_value) const; | ||||
|  | ||||
|     // Get a string value from INI file, returning default_value if not found, | ||||
|     // empty, or contains only whitespace. | ||||
|     INI_API std::string GetString(const std::string& section, const std::string& name, | ||||
|                     const std::string& default_value) const; | ||||
|  | ||||
|     // Get an integer (long) value from INI file, returning default_value if | ||||
|     // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). | ||||
|     INI_API long GetInteger(const std::string& section, const std::string& name, long default_value) const; | ||||
|  | ||||
|     // Get a real (floating point double) value from INI file, returning | ||||
|     // default_value if not found or not a valid floating point value | ||||
|     // according to strtod(). | ||||
|     INI_API double GetReal(const std::string& section, const std::string& name, double default_value) const; | ||||
|  | ||||
|     // Get a boolean value from INI file, returning default_value if not found or if | ||||
|     // not a valid true/false value. Valid true values are "true", "yes", "on", "1", | ||||
|     // and valid false values are "false", "no", "off", "0" (not case sensitive). | ||||
|     INI_API bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; | ||||
|  | ||||
|     // Return true if the given section exists (section must contain at least | ||||
|     // one name=value pair). | ||||
|     INI_API bool HasSection(const std::string& section) const; | ||||
|  | ||||
|     // Return true if a value exists with the given section and field names. | ||||
|     INI_API bool HasValue(const std::string& section, const std::string& name) const; | ||||
|  | ||||
| private: | ||||
|     int _error; | ||||
|     std::map<std::string, std::string> _values; | ||||
|     static std::string MakeKey(const std::string& section, const std::string& name); | ||||
|     static int ValueHandler(void* user, const char* section, const char* name, | ||||
|                             const char* value); | ||||
| }; | ||||
|  | ||||
| #endif  // INIREADER_H | ||||
							
								
								
									
										298
									
								
								3rdparty/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								3rdparty/inih/ini.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,298 @@ | ||||
| /* inih -- simple .INI file parser | ||||
|  | ||||
| SPDX-License-Identifier: BSD-3-Clause | ||||
|  | ||||
| Copyright (C) 2009-2020, Ben Hoyt | ||||
|  | ||||
| inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||
| home page for more info: | ||||
|  | ||||
| https://github.com/benhoyt/inih | ||||
|  | ||||
| */ | ||||
|  | ||||
| #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) | ||||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| #endif | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "ini.h" | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
| #if INI_CUSTOM_ALLOCATOR | ||||
| #include <stddef.h> | ||||
| void* ini_malloc(size_t size); | ||||
| void ini_free(void* ptr); | ||||
| void* ini_realloc(void* ptr, size_t size); | ||||
| #else | ||||
| #include <stdlib.h> | ||||
| #define ini_malloc malloc | ||||
| #define ini_free free | ||||
| #define ini_realloc realloc | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #define MAX_SECTION 50 | ||||
| #define MAX_NAME 50 | ||||
|  | ||||
| /* Used by ini_parse_string() to keep track of string parsing state. */ | ||||
| typedef struct { | ||||
|     const char* ptr; | ||||
|     size_t num_left; | ||||
| } ini_parse_string_ctx; | ||||
|  | ||||
| /* Strip whitespace chars off end of given string, in place. Return s. */ | ||||
| static char* rstrip(char* s) | ||||
| { | ||||
|     char* p = s + strlen(s); | ||||
|     while (p > s && isspace((unsigned char)(*--p))) | ||||
|         *p = '\0'; | ||||
|     return s; | ||||
| } | ||||
|  | ||||
| /* Return pointer to first non-whitespace char in given string. */ | ||||
| static char* lskip(const char* s) | ||||
| { | ||||
|     while (*s && isspace((unsigned char)(*s))) | ||||
|         s++; | ||||
|     return (char*)s; | ||||
| } | ||||
|  | ||||
| /* Return pointer to first char (of chars) or inline comment in given string, | ||||
|    or pointer to NUL at end of string if neither found. Inline comment must | ||||
|    be prefixed by a whitespace character to register as a comment. */ | ||||
| static char* find_chars_or_comment(const char* s, const char* chars) | ||||
| { | ||||
| #if INI_ALLOW_INLINE_COMMENTS | ||||
|     int was_space = 0; | ||||
|     while (*s && (!chars || !strchr(chars, *s)) && | ||||
|            !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { | ||||
|         was_space = isspace((unsigned char)(*s)); | ||||
|         s++; | ||||
|     } | ||||
| #else | ||||
|     while (*s && (!chars || !strchr(chars, *s))) { | ||||
|         s++; | ||||
|     } | ||||
| #endif | ||||
|     return (char*)s; | ||||
| } | ||||
|  | ||||
| /* Similar to strncpy, but ensures dest (size bytes) is | ||||
|    NUL-terminated, and doesn't pad with NULs. */ | ||||
| static char* strncpy0(char* dest, const char* src, size_t size) | ||||
| { | ||||
|     /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ | ||||
|     size_t i; | ||||
|     for (i = 0; i < size - 1 && src[i]; i++) | ||||
|         dest[i] = src[i]; | ||||
|     dest[i] = '\0'; | ||||
|     return dest; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||
|                      void* user) | ||||
| { | ||||
|     /* Uses a fair bit of stack (use heap instead if you need to) */ | ||||
| #if INI_USE_STACK | ||||
|     char line[INI_MAX_LINE]; | ||||
|     int max_line = INI_MAX_LINE; | ||||
| #else | ||||
|     char* line; | ||||
|     size_t max_line = INI_INITIAL_ALLOC; | ||||
| #endif | ||||
| #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||
|     char* new_line; | ||||
|     size_t offset; | ||||
| #endif | ||||
|     char section[MAX_SECTION] = ""; | ||||
|     char prev_name[MAX_NAME] = ""; | ||||
|  | ||||
|     char* start; | ||||
|     char* end; | ||||
|     char* name; | ||||
|     char* value; | ||||
|     int lineno = 0; | ||||
|     int error = 0; | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
|     line = (char*)ini_malloc(INI_INITIAL_ALLOC); | ||||
|     if (!line) { | ||||
|         return -2; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| #if INI_HANDLER_LINENO | ||||
| #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) | ||||
| #else | ||||
| #define HANDLER(u, s, n, v) handler(u, s, n, v) | ||||
| #endif | ||||
|  | ||||
|     /* Scan through stream line by line */ | ||||
|     while (reader(line, (int)max_line, stream) != NULL) { | ||||
| #if INI_ALLOW_REALLOC && !INI_USE_STACK | ||||
|         offset = strlen(line); | ||||
|         while (offset == max_line - 1 && line[offset - 1] != '\n') { | ||||
|             max_line *= 2; | ||||
|             if (max_line > INI_MAX_LINE) | ||||
|                 max_line = INI_MAX_LINE; | ||||
|             new_line = ini_realloc(line, max_line); | ||||
|             if (!new_line) { | ||||
|                 ini_free(line); | ||||
|                 return -2; | ||||
|             } | ||||
|             line = new_line; | ||||
|             if (reader(line + offset, (int)(max_line - offset), stream) == NULL) | ||||
|                 break; | ||||
|             if (max_line >= INI_MAX_LINE) | ||||
|                 break; | ||||
|             offset += strlen(line + offset); | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         lineno++; | ||||
|  | ||||
|         start = line; | ||||
| #if INI_ALLOW_BOM | ||||
|         if (lineno == 1 && (unsigned char)start[0] == 0xEF && | ||||
|                            (unsigned char)start[1] == 0xBB && | ||||
|                            (unsigned char)start[2] == 0xBF) { | ||||
|             start += 3; | ||||
|         } | ||||
| #endif | ||||
|         start = lskip(rstrip(start)); | ||||
|  | ||||
|         if (strchr(INI_START_COMMENT_PREFIXES, *start)) { | ||||
|             /* Start-of-line comment */ | ||||
|         } | ||||
| #if INI_ALLOW_MULTILINE | ||||
|         else if (*prev_name && *start && start > line) { | ||||
|             /* Non-blank line with leading whitespace, treat as continuation | ||||
|                of previous name's value (as per Python configparser). */ | ||||
|             if (!HANDLER(user, section, prev_name, start) && !error) | ||||
|                 error = lineno; | ||||
|         } | ||||
| #endif | ||||
|         else if (*start == '[') { | ||||
|             /* A "[section]" line */ | ||||
|             end = find_chars_or_comment(start + 1, "]"); | ||||
|             if (*end == ']') { | ||||
|                 *end = '\0'; | ||||
|                 strncpy0(section, start + 1, sizeof(section)); | ||||
|                 *prev_name = '\0'; | ||||
| #if INI_CALL_HANDLER_ON_NEW_SECTION | ||||
|                 if (!HANDLER(user, section, NULL, NULL) && !error) | ||||
|                     error = lineno; | ||||
| #endif | ||||
|             } | ||||
|             else if (!error) { | ||||
|                 /* No ']' found on section line */ | ||||
|                 error = lineno; | ||||
|             } | ||||
|         } | ||||
|         else if (*start) { | ||||
|             /* Not a comment, must be a name[=:]value pair */ | ||||
|             end = find_chars_or_comment(start, "=:"); | ||||
|             if (*end == '=' || *end == ':') { | ||||
|                 *end = '\0'; | ||||
|                 name = rstrip(start); | ||||
|                 value = end + 1; | ||||
| #if INI_ALLOW_INLINE_COMMENTS | ||||
|                 end = find_chars_or_comment(value, NULL); | ||||
|                 if (*end) | ||||
|                     *end = '\0'; | ||||
| #endif | ||||
|                 value = lskip(value); | ||||
|                 rstrip(value); | ||||
|  | ||||
|                 /* Valid name[=:]value pair found, call handler */ | ||||
|                 strncpy0(prev_name, name, sizeof(prev_name)); | ||||
|                 if (!HANDLER(user, section, name, value) && !error) | ||||
|                     error = lineno; | ||||
|             } | ||||
|             else if (!error) { | ||||
|                 /* No '=' or ':' found on name[=:]value line */ | ||||
| #if INI_ALLOW_NO_VALUE | ||||
|                 *end = '\0'; | ||||
|                 name = rstrip(start); | ||||
|                 if (!HANDLER(user, section, name, NULL) && !error) | ||||
|                     error = lineno; | ||||
| #else | ||||
|                 error = lineno; | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|  | ||||
| #if INI_STOP_ON_FIRST_ERROR | ||||
|         if (error) | ||||
|             break; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| #if !INI_USE_STACK | ||||
|     ini_free(line); | ||||
| #endif | ||||
|  | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_file(FILE* file, ini_handler handler, void* user) | ||||
| { | ||||
|     return ini_parse_stream((ini_reader)fgets, file, handler, user); | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse(const char* filename, ini_handler handler, void* user) | ||||
| { | ||||
|     FILE* file; | ||||
|     int error; | ||||
|  | ||||
|     file = fopen(filename, "r"); | ||||
|     if (!file) | ||||
|         return -1; | ||||
|     error = ini_parse_file(file, handler, user); | ||||
|     fclose(file); | ||||
|     return error; | ||||
| } | ||||
|  | ||||
| /* An ini_reader function to read the next line from a string buffer. This | ||||
|    is the fgets() equivalent used by ini_parse_string(). */ | ||||
| static char* ini_reader_string(char* str, int num, void* stream) { | ||||
|     ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; | ||||
|     const char* ctx_ptr = ctx->ptr; | ||||
|     size_t ctx_num_left = ctx->num_left; | ||||
|     char* strp = str; | ||||
|     char c; | ||||
|  | ||||
|     if (ctx_num_left == 0 || num < 2) | ||||
|         return NULL; | ||||
|  | ||||
|     while (num > 1 && ctx_num_left != 0) { | ||||
|         c = *ctx_ptr++; | ||||
|         ctx_num_left--; | ||||
|         *strp++ = c; | ||||
|         if (c == '\n') | ||||
|             break; | ||||
|         num--; | ||||
|     } | ||||
|  | ||||
|     *strp = '\0'; | ||||
|     ctx->ptr = ctx_ptr; | ||||
|     ctx->num_left = ctx_num_left; | ||||
|     return str; | ||||
| } | ||||
|  | ||||
| /* See documentation in header file. */ | ||||
| int ini_parse_string(const char* string, ini_handler handler, void* user) { | ||||
|     ini_parse_string_ctx ctx; | ||||
|  | ||||
|     ctx.ptr = string; | ||||
|     ctx.num_left = strlen(string); | ||||
|     return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, | ||||
|                             user); | ||||
| } | ||||
							
								
								
									
										178
									
								
								3rdparty/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								3rdparty/inih/ini.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | ||||
| /* inih -- simple .INI file parser | ||||
|  | ||||
| SPDX-License-Identifier: BSD-3-Clause | ||||
|  | ||||
| Copyright (C) 2009-2020, Ben Hoyt | ||||
|  | ||||
| inih is released under the New BSD license (see LICENSE.txt). Go to the project | ||||
| home page for more info: | ||||
|  | ||||
| https://github.com/benhoyt/inih | ||||
|  | ||||
| */ | ||||
|  | ||||
| #ifndef INI_H | ||||
| #define INI_H | ||||
|  | ||||
| /* Make this header file easier to include in C++ code */ | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| /* Nonzero if ini_handler callback should accept lineno parameter. */ | ||||
| #ifndef INI_HANDLER_LINENO | ||||
| #define INI_HANDLER_LINENO 0 | ||||
| #endif | ||||
|  | ||||
| /* Visibility symbols, required for Windows DLLs */ | ||||
| #ifndef INI_API | ||||
| #if defined _WIN32 || defined __CYGWIN__ | ||||
| #	ifdef INI_SHARED_LIB | ||||
| #		ifdef INI_SHARED_LIB_BUILDING | ||||
| #			define INI_API __declspec(dllexport) | ||||
| #		else | ||||
| #			define INI_API __declspec(dllimport) | ||||
| #		endif | ||||
| #	else | ||||
| #		define INI_API | ||||
| #	endif | ||||
| #else | ||||
| #	if defined(__GNUC__) && __GNUC__ >= 4 | ||||
| #		define INI_API __attribute__ ((visibility ("default"))) | ||||
| #	else | ||||
| #		define INI_API | ||||
| #	endif | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* Typedef for prototype of handler function. */ | ||||
| #if INI_HANDLER_LINENO | ||||
| typedef int (*ini_handler)(void* user, const char* section, | ||||
|                            const char* name, const char* value, | ||||
|                            int lineno); | ||||
| #else | ||||
| typedef int (*ini_handler)(void* user, const char* section, | ||||
|                            const char* name, const char* value); | ||||
| #endif | ||||
|  | ||||
| /* Typedef for prototype of fgets-style reader function. */ | ||||
| typedef char* (*ini_reader)(char* str, int num, void* stream); | ||||
|  | ||||
| /* Parse given INI-style file. May have [section]s, name=value pairs | ||||
|    (whitespace stripped), and comments starting with ';' (semicolon). Section | ||||
|    is "" if name=value pair parsed before any section heading. name:value | ||||
|    pairs are also supported as a concession to Python's configparser. | ||||
|  | ||||
|    For each name=value pair parsed, call handler function with given user | ||||
|    pointer as well as section, name, and value (data only valid for duration | ||||
|    of handler call). Handler should return nonzero on success, zero on error. | ||||
|  | ||||
|    Returns 0 on success, line number of first error on parse error (doesn't | ||||
|    stop on first error), -1 on file open error, or -2 on memory allocation | ||||
|    error (only when INI_USE_STACK is zero). | ||||
| */ | ||||
| INI_API int ini_parse(const char* filename, ini_handler handler, void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't | ||||
|    close the file when it's finished -- the caller must do that. */ | ||||
| INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes an ini_reader function pointer instead of | ||||
|    filename. Used for implementing custom or string-based I/O (see also | ||||
|    ini_parse_string). */ | ||||
| INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, | ||||
|                      void* user); | ||||
|  | ||||
| /* Same as ini_parse(), but takes a zero-terminated string with the INI data | ||||
| instead of a file. Useful for parsing INI data from a network socket or | ||||
| already in memory. */ | ||||
| INI_API int ini_parse_string(const char* string, ini_handler handler, void* user); | ||||
|  | ||||
| /* Nonzero to allow multi-line value parsing, in the style of Python's | ||||
|    configparser. If allowed, ini_parse() will call the handler with the same | ||||
|    name for each subsequent line parsed. */ | ||||
| #ifndef INI_ALLOW_MULTILINE | ||||
| #define INI_ALLOW_MULTILINE 1 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of | ||||
|    the file. See https://github.com/benhoyt/inih/issues/21 */ | ||||
| #ifndef INI_ALLOW_BOM | ||||
| #define INI_ALLOW_BOM 1 | ||||
| #endif | ||||
|  | ||||
| /* Chars that begin a start-of-line comment. Per Python configparser, allow | ||||
|    both ; and # comments at the start of a line by default. */ | ||||
| #ifndef INI_START_COMMENT_PREFIXES | ||||
| #define INI_START_COMMENT_PREFIXES ";#" | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow inline comments (with valid inline comment characters | ||||
|    specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match | ||||
|    Python 3.2+ configparser behaviour. */ | ||||
| #ifndef INI_ALLOW_INLINE_COMMENTS | ||||
| #define INI_ALLOW_INLINE_COMMENTS 1 | ||||
| #endif | ||||
| #ifndef INI_INLINE_COMMENT_PREFIXES | ||||
| #define INI_INLINE_COMMENT_PREFIXES ";" | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ | ||||
| #ifndef INI_USE_STACK | ||||
| #define INI_USE_STACK 1 | ||||
| #endif | ||||
|  | ||||
| /* Maximum line length for any line in INI file (stack or heap). Note that | ||||
|    this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ | ||||
| #ifndef INI_MAX_LINE | ||||
| #define INI_MAX_LINE 200 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow heap line buffer to grow via realloc(), zero for a | ||||
|    fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is | ||||
|    zero. */ | ||||
| #ifndef INI_ALLOW_REALLOC | ||||
| #define INI_ALLOW_REALLOC 0 | ||||
| #endif | ||||
|  | ||||
| /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK | ||||
|    is zero. */ | ||||
| #ifndef INI_INITIAL_ALLOC | ||||
| #define INI_INITIAL_ALLOC 200 | ||||
| #endif | ||||
|  | ||||
| /* Stop parsing on first error (default is to keep parsing). */ | ||||
| #ifndef INI_STOP_ON_FIRST_ERROR | ||||
| #define INI_STOP_ON_FIRST_ERROR 0 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to call the handler at the start of each new section (with | ||||
|    name and value NULL). Default is to only call the handler on | ||||
|    each name=value pair. */ | ||||
| #ifndef INI_CALL_HANDLER_ON_NEW_SECTION | ||||
| #define INI_CALL_HANDLER_ON_NEW_SECTION 0 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to allow a name without a value (no '=' or ':' on the line) and | ||||
|    call the handler with value NULL in this case. Default is to treat | ||||
|    no-value lines as an error. */ | ||||
| #ifndef INI_ALLOW_NO_VALUE | ||||
| #define INI_ALLOW_NO_VALUE 0 | ||||
| #endif | ||||
|  | ||||
| /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory | ||||
|    allocation functions (INI_USE_STACK must also be 0). These functions must | ||||
|    have the same signatures as malloc/free/realloc and behave in a similar | ||||
|    way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ | ||||
| #ifndef INI_CUSTOM_ALLOCATOR | ||||
| #define INI_CUSTOM_ALLOCATOR 0 | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* INI_H */ | ||||
							
								
								
									
										2
									
								
								3rdparty/qt-unix-signals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								3rdparty/qt-unix-signals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1.0) | ||||
|  | ||||
| project(QtSignals LANGUAGES CXX) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 11) | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
| set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||||
| # Instruct CMake to run moc automatically when needed. | ||||
| set(CMAKE_AUTOMOC ON) | ||||
|   | ||||
							
								
								
									
										1
									
								
								3rdparty/qtkeychain
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								3rdparty/qtkeychain
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule 3rdparty/qtkeychain added at f197cdb935
									
								
							| @@ -1,15 +1,15 @@ | ||||
| cmake_minimum_required(VERSION 3.10.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 11) | ||||
| set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
| set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||||
|  | ||||
| set(CMAKE_AUTOMOC ON) | ||||
| set(CMAKE_AUTORCC ON) | ||||
| set(CMAKE_AUTOUIC ON) | ||||
|  | ||||
| file(STRINGS "VERSION" ver) | ||||
| file(STRINGS "VERSION_SUFFIX" VERSION_SUFFIX) | ||||
| project(GlobalProtect-openconnect VERSION ${ver} LANGUAGES CXX) | ||||
| file(STRINGS "VERSION" version) | ||||
| project(GlobalProtect-openconnect LANGUAGES CXX) | ||||
|  | ||||
| # Set the CMAKE_INSTALL_PREFIX to /usr if not specified | ||||
| if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) | ||||
| @@ -30,7 +30,10 @@ find_package(Qt5 REQUIRED COMPONENTS | ||||
|     DBus | ||||
| ) | ||||
|  | ||||
| find_package(Qt5Keychain REQUIRED) | ||||
|  | ||||
| add_subdirectory(3rdparty/qt-unix-signals) | ||||
| add_subdirectory(3rdparty/inih) | ||||
| add_subdirectory(GPService) | ||||
| add_subdirectory(GPClient) | ||||
| add_dependencies(gpclient gpservice) | ||||
|   | ||||
| @@ -17,13 +17,14 @@ add_executable(gpclient | ||||
|     cdpcommand.cpp | ||||
|     cdpcommandmanager.cpp | ||||
|     enhancedwebview.cpp | ||||
|     enhancedwebpage.cpp | ||||
|     gatewayauthenticator.cpp | ||||
|     gatewayauthenticatorparams.cpp | ||||
|     gpgateway.cpp | ||||
|     gphelper.cpp | ||||
|     loginparams.cpp | ||||
|     main.cpp | ||||
|     normalloginwindow.cpp | ||||
|     standardloginwindow.cpp | ||||
|     portalauthenticator.cpp | ||||
|     portalconfigresponse.cpp | ||||
|     preloginresponse.cpp | ||||
| @@ -31,7 +32,7 @@ add_executable(gpclient | ||||
|     gpclient.cpp | ||||
|     settingsdialog.cpp | ||||
|     gpclient.ui | ||||
|     normalloginwindow.ui | ||||
|     standardloginwindow.ui | ||||
|     settingsdialog.ui | ||||
|     challengedialog.h | ||||
|     challengedialog.cpp | ||||
| @@ -71,7 +72,10 @@ set(SingleApplication_LIBRARY ${BINARY_DIR}/libSingleApplication.a) | ||||
| ExternalProject_Get_Property(plog-${PROJECT_NAME} SOURCE_DIR) | ||||
| set(plog_INCLUDE_DIR "${SOURCE_DIR}/include") | ||||
|  | ||||
| add_dependencies(gpclient SingleApplication-${PROJECT_NAME} plog-${PROJECT_NAME}) | ||||
| add_dependencies(gpclient | ||||
|     SingleApplication-${PROJECT_NAME} | ||||
|     plog-${PROJECT_NAME} | ||||
| ) | ||||
|  | ||||
| target_include_directories(gpclient PRIVATE | ||||
|     ${CMAKE_BINARY_DIR} | ||||
| @@ -79,6 +83,7 @@ target_include_directories(gpclient PRIVATE | ||||
|     ${CMAKE_CURRENT_BINARY_DIR} | ||||
|     ${SingleApplication_INCLUDE_DIR} | ||||
|     ${plog_INCLUDE_DIR} | ||||
|     ${QTKEYCHAIN_INCLUDE_DIRS}/qt5keychain | ||||
| ) | ||||
|  | ||||
| target_link_libraries(gpclient | ||||
| @@ -90,9 +95,11 @@ target_link_libraries(gpclient | ||||
|     Qt5::WebEngineWidgets | ||||
|     Qt5::DBus | ||||
|     QtSignals | ||||
|     ${QTKEYCHAIN_LIBRARIES} | ||||
|     inih | ||||
| ) | ||||
|  | ||||
| if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0) | ||||
| if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0 AND CMAKE_BUILD_TYPE STREQUAL Release) | ||||
|     target_compile_options(gpclient PUBLIC "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.") | ||||
| endif() | ||||
|  | ||||
|   | ||||
| @@ -29,7 +29,7 @@ void CDPCommandManager::initialize(QString endpoint) | ||||
|         reply, &QNetworkReply::finished, | ||||
|         [reply, this]() { | ||||
|             if (reply->error()) { | ||||
|                 PLOGE << "CDP request error"; | ||||
|                 LOGE << "CDP request error"; | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @@ -78,10 +78,10 @@ void CDPCommandManager::onTextMessageReceived(QString message) | ||||
|  | ||||
| void CDPCommandManager::onSocketDisconnected() | ||||
| { | ||||
|     PLOGI << "WebSocket disconnected"; | ||||
|     LOGI << "WebSocket disconnected"; | ||||
| } | ||||
|  | ||||
| void CDPCommandManager::onSocketError(QAbstractSocket::SocketError error) | ||||
| { | ||||
|     PLOGE << "WebSocket error" << error; | ||||
|     LOGE << "WebSocket error" << error; | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ Name=GlobalProtect VPN | ||||
| Comment=A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports SAML auth mode. | ||||
| GenericName=GlobalProtect VPN client, supports SAML auth mode | ||||
| Categories=Network;Dialup; | ||||
| Exec=@CMAKE_INSTALL_PREFIX@/bin/gpclient | ||||
| Exec=env QT_AUTO_SCREEN_SCALE_FACTOR=1 @CMAKE_INSTALL_PREFIX@/bin/gpclient | ||||
| Icon=com.yuezk.qt.gpclient | ||||
| Keywords=GlobalProtect;Openconnect;SAML;connection;VPN; | ||||
| StartupWMClass=gpclient | ||||
|   | ||||
							
								
								
									
										8
									
								
								GPClient/enhancedwebpage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								GPClient/enhancedwebpage.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| #include "enhancedwebpage.h" | ||||
| #include <QWebEngineCertificateError> | ||||
| #include <plog/Log.h> | ||||
|  | ||||
| bool EnhancedWebPage::certificateError(const QWebEngineCertificateError &certificateError) { | ||||
|     LOGI << "An error occurred during certificate verification for " << certificateError.url().toString() << "; " << certificateError.errorDescription(); | ||||
|     return certificateError.isOverridable(); | ||||
| }; | ||||
							
								
								
									
										12
									
								
								GPClient/enhancedwebpage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								GPClient/enhancedwebpage.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #ifndef ENHANCEDWEBPAGE_H | ||||
| #define ENHANCEDWEBPAGE_H | ||||
|  | ||||
| #include <QtWebEngineWidgets/qwebenginepage.h> | ||||
|  | ||||
| class EnhancedWebPage : public QWebEnginePage | ||||
| { | ||||
| protected: | ||||
|     bool certificateError(const QWebEngineCertificateError &certificateError) override; | ||||
| }; | ||||
|  | ||||
| #endif // !ECHANCEDWEBPAG | ||||
| @@ -1,6 +1,7 @@ | ||||
| #include <QtCore/QProcessEnvironment> | ||||
| #include <QtWebEngineWidgets/QWebEngineView> | ||||
|  | ||||
| #include "enhancedwebpage.h" | ||||
| #include "enhancedwebview.h" | ||||
| #include "cdpcommandmanager.h" | ||||
|  | ||||
| @@ -12,14 +13,10 @@ EnhancedWebView::EnhancedWebView(QWidget *parent) | ||||
|    QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived); | ||||
| } | ||||
|  | ||||
| EnhancedWebView::~EnhancedWebView() | ||||
| { | ||||
|     delete cdp; | ||||
| } | ||||
|  | ||||
| void EnhancedWebView::initialize() | ||||
| { | ||||
|     QString port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT); | ||||
|     setPage(new EnhancedWebPage()); | ||||
|     auto port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT); | ||||
|     cdp->initialize("http://127.0.0.1:" + port + "/json"); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,6 @@ class EnhancedWebView : public QWebEngineView | ||||
|     Q_OBJECT | ||||
| public: | ||||
|     explicit EnhancedWebView(QWidget *parent = nullptr); | ||||
|     ~EnhancedWebView(); | ||||
|  | ||||
|     void initialize(); | ||||
|  | ||||
| @@ -24,7 +23,7 @@ private slots: | ||||
|     void onEventReceived(QString eventName, QJsonObject params); | ||||
|  | ||||
| private: | ||||
|     CDPCommandManager *cdp; | ||||
|     CDPCommandManager *cdp { nullptr }; | ||||
| }; | ||||
|  | ||||
| #endif // ENHANCEDWEBVIEW_H | ||||
|   | ||||
| @@ -23,14 +23,9 @@ GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, GatewayAuthen | ||||
|     } | ||||
| } | ||||
|  | ||||
| GatewayAuthenticator::~GatewayAuthenticator() | ||||
| { | ||||
|     delete normalLoginWindow; | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::authenticate() | ||||
| { | ||||
|     PLOGI << "Start gateway authentication..."; | ||||
|     LOGI << "Start gateway authentication..."; | ||||
|  | ||||
|     LoginParams loginParams { params.clientos() }; | ||||
|     loginParams.setUser(params.username()); | ||||
| @@ -43,9 +38,9 @@ void GatewayAuthenticator::authenticate() | ||||
|  | ||||
| void GatewayAuthenticator::login(const LoginParams &loginParams) | ||||
| { | ||||
|     PLOGI << "Trying to login the gateway at " << loginUrl << " with " << loginParams.toUtf8(); | ||||
|     LOGI << QString("Trying to login the gateway at %1, with %2").arg(loginUrl).arg(QString(loginParams.toUtf8())); | ||||
|  | ||||
|     QNetworkReply *reply = createRequest(loginUrl, loginParams.toUtf8()); | ||||
|     auto *reply = createRequest(loginUrl, loginParams.toUtf8()); | ||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished); | ||||
| } | ||||
|  | ||||
| @@ -55,10 +50,10 @@ void GatewayAuthenticator::onLoginFinished() | ||||
|     QByteArray response = reply->readAll(); | ||||
|  | ||||
|     if (reply->error() || response.contains("Authentication failure")) { | ||||
|         PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString()); | ||||
|         LOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString()); | ||||
|  | ||||
|         if (normalLoginWindow) { | ||||
|             normalLoginWindow->setProcessing(false); | ||||
|         if (standardLoginWindow) { | ||||
|             standardLoginWindow->setProcessing(false); | ||||
|             openMessageBox("Gateway login failed.", "Please check your credentials and try again."); | ||||
|         } else { | ||||
|             doAuth(); | ||||
| @@ -68,48 +63,48 @@ void GatewayAuthenticator::onLoginFinished() | ||||
|  | ||||
|     // 2FA | ||||
|     if (response.contains("Challenge")) { | ||||
|         PLOGI << "The server need input the challenge..."; | ||||
|         LOGI << "The server need input the challenge..."; | ||||
|         showChallenge(response); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (normalLoginWindow) { | ||||
|         normalLoginWindow->close(); | ||||
|     if (standardLoginWindow) { | ||||
|         standardLoginWindow->close(); | ||||
|     } | ||||
|  | ||||
|     const QUrlQuery params = gpclient::helper::parseGatewayResponse(response); | ||||
|     const auto params = gpclient::helper::parseGatewayResponse(response); | ||||
|     emit success(params.toString()); | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::doAuth() | ||||
| { | ||||
|     PLOGI << "Perform the gateway prelogin at " << preloginUrl; | ||||
|     LOGI << "Perform the gateway prelogin at " << preloginUrl; | ||||
|  | ||||
|     QNetworkReply *reply = createRequest(preloginUrl); | ||||
|     auto *reply = createRequest(preloginUrl); | ||||
|     connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onPreloginFinished); | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::onPreloginFinished() | ||||
| { | ||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); | ||||
|     auto *reply = qobject_cast<QNetworkReply*>(sender()); | ||||
|  | ||||
|     if (reply->error()) { | ||||
|         PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString()); | ||||
|         LOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString()); | ||||
|  | ||||
|         emit fail("Error occurred on the gateway prelogin interface."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     PLOGI << "Gateway prelogin succeeded."; | ||||
|     LOGI << "Gateway prelogin succeeded."; | ||||
|  | ||||
|     PreloginResponse response = PreloginResponse::parse(reply->readAll()); | ||||
|     auto response = PreloginResponse::parse(reply->readAll()); | ||||
|  | ||||
|     if (response.hasSamlAuthFields()) { | ||||
|         samlAuth(response.samlMethod(), response.samlRequest(), reply->url().toString()); | ||||
|     } else if (response.hasNormalAuthFields()) { | ||||
|         normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage()); | ||||
|     } else { | ||||
|         PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse())); | ||||
|         LOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse())); | ||||
|         emit fail("Unknown response for gateway prelogin interface."); | ||||
|     } | ||||
|  | ||||
| @@ -118,27 +113,23 @@ void GatewayAuthenticator::onPreloginFinished() | ||||
|  | ||||
| void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPassword, QString authMessage) | ||||
| { | ||||
|     PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword); | ||||
|     LOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword); | ||||
|  | ||||
|     normalLoginWindow = new NormalLoginWindow; | ||||
|     normalLoginWindow->setPortalAddress(gateway); | ||||
|     normalLoginWindow->setAuthMessage(authMessage); | ||||
|     normalLoginWindow->setUsernameLabel(labelUsername); | ||||
|     normalLoginWindow->setPasswordLabel(labelPassword); | ||||
|     standardLoginWindow = new StandardLoginWindow {gateway, labelUsername, labelPassword, authMessage}; | ||||
|  | ||||
|     // Do login | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformNormalLogin); | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformStandardLogin); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished); | ||||
|  | ||||
|     normalLoginWindow->show(); | ||||
|     standardLoginWindow->show(); | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) | ||||
| void GatewayAuthenticator::onPerformStandardLogin(const QString &username, const QString &password) | ||||
| { | ||||
|     PLOGI << "Start to perform normal login..."; | ||||
|     LOGI << "Start to perform normal login..."; | ||||
|  | ||||
|     normalLoginWindow->setProcessing(true); | ||||
|     standardLoginWindow->setProcessing(true); | ||||
|     params.setUsername(username); | ||||
|     params.setPassword(password); | ||||
|  | ||||
| @@ -152,19 +143,28 @@ void GatewayAuthenticator::onLoginWindowRejected() | ||||
|  | ||||
| void GatewayAuthenticator::onLoginWindowFinished() | ||||
| { | ||||
|     delete normalLoginWindow; | ||||
|     normalLoginWindow = nullptr; | ||||
|     delete standardLoginWindow; | ||||
|     standardLoginWindow = nullptr; | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl) | ||||
| { | ||||
|     PLOGI << "Trying to perform SAML login with saml-method " << samlMethod; | ||||
|     LOGI << "Trying to perform SAML login with saml-method " << samlMethod; | ||||
|  | ||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; | ||||
|     auto *loginWindow = new SAMLLoginWindow(gateway); | ||||
|  | ||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &GatewayAuthenticator::onSAMLLoginSuccess); | ||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &GatewayAuthenticator::onSAMLLoginFail); | ||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected); | ||||
|     connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> &samlResult) { | ||||
|         this->onSAMLLoginSuccess(samlResult); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|     connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString &error) { | ||||
|         this->onSAMLLoginFail(code, error); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() { | ||||
|         this->onLoginWindowRejected(); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|  | ||||
|     loginWindow->login(samlMethod, samlRequest, preloginUrl); | ||||
| } | ||||
| @@ -172,9 +172,9 @@ void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QSt | ||||
| void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult) | ||||
| { | ||||
|     if (samlResult.contains("preloginCookie")) { | ||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); | ||||
|         LOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); | ||||
|     } else { | ||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); | ||||
|         LOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); | ||||
|     } | ||||
|  | ||||
|     LoginParams loginParams { params.clientos() }; | ||||
| @@ -185,7 +185,7 @@ void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &saml | ||||
|     login(loginParams); | ||||
| } | ||||
|  | ||||
| void GatewayAuthenticator::onSAMLLoginFail(const QString msg) | ||||
| void GatewayAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg) | ||||
| { | ||||
|     emit fail(msg); | ||||
| } | ||||
| @@ -206,13 +206,13 @@ void GatewayAuthenticator::showChallenge(const QString &responseText) | ||||
|  | ||||
|     connect(challengeDialog, &ChallengeDialog::accepted, this, [this] { | ||||
|         params.setPassword(challengeDialog->getChallenge()); | ||||
|         PLOGI << "Challenge submitted, try to re-authenticate..."; | ||||
|         LOGI << "Challenge submitted, try to re-authenticate..."; | ||||
|         authenticate(); | ||||
|     }); | ||||
|  | ||||
|     connect(challengeDialog, &ChallengeDialog::rejected, this, [this] { | ||||
|         if (normalLoginWindow) { | ||||
|             normalLoginWindow->close(); | ||||
|         if (standardLoginWindow) { | ||||
|             standardLoginWindow->close(); | ||||
|         } | ||||
|         emit fail(); | ||||
|     }); | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| #include <QtCore/QObject> | ||||
|  | ||||
| #include "normalloginwindow.h" | ||||
| #include "standardloginwindow.h" | ||||
| #include "challengedialog.h" | ||||
| #include "loginparams.h" | ||||
| #include "gatewayauthenticatorparams.h" | ||||
| @@ -12,23 +12,22 @@ class GatewayAuthenticator : public QObject | ||||
| { | ||||
|     Q_OBJECT | ||||
| public: | ||||
|     explicit GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params); | ||||
|     ~GatewayAuthenticator(); | ||||
|     explicit GatewayAuthenticator(const QString &gateway, GatewayAuthenticatorParams params); | ||||
|  | ||||
|     void authenticate(); | ||||
|  | ||||
| signals: | ||||
|     void success(const QString& authCookie); | ||||
|     void fail(const QString& msg = ""); | ||||
|     void success(const QString &authCookie); | ||||
|     void fail(const QString &msg = ""); | ||||
|  | ||||
| private slots: | ||||
|     void onLoginFinished(); | ||||
|     void onPreloginFinished(); | ||||
|     void onPerformNormalLogin(const QString &username, const QString &password); | ||||
|     void onPerformStandardLogin(const QString &username, const QString &password); | ||||
|     void onLoginWindowRejected(); | ||||
|     void onLoginWindowFinished(); | ||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> &samlResult); | ||||
|     void onSAMLLoginFail(const QString msg); | ||||
|     void onSAMLLoginFail(const QString &code, const QString &msg); | ||||
|  | ||||
| private: | ||||
|     QString gateway; | ||||
| @@ -36,8 +35,8 @@ private: | ||||
|     QString preloginUrl; | ||||
|     QString loginUrl; | ||||
|  | ||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; | ||||
|     ChallengeDialog *challengeDialog{ nullptr }; | ||||
|     StandardLoginWindow *standardLoginWindow { nullptr }; | ||||
|     ChallengeDialog *challengeDialog { nullptr }; | ||||
|  | ||||
|     void login(const LoginParams& loginParams); | ||||
|     void doAuth(); | ||||
|   | ||||
| @@ -64,4 +64,3 @@ void GatewayAuthenticatorParams::setInputStr(const QString &inputStr) | ||||
| { | ||||
|     m_inputStr = inputStr; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -35,19 +35,11 @@ GPClient::GPClient(QWidget *parent, IVpn *vpn) | ||||
|     connect(ov, SIGNAL(error(QString)), this, SLOT(onVPNError(QString))); | ||||
|     connect(ov, SIGNAL(logAvailable(QString)), this, SLOT(onVPNLogAvailable(QString))); | ||||
|  | ||||
|     // Initiallize the context menu of system tray. | ||||
|     // Initialize the context menu of system tray. | ||||
|     initSystemTrayIcon(); | ||||
|     initVpnStatus(); | ||||
| } | ||||
|  | ||||
| GPClient::~GPClient() | ||||
| { | ||||
|     delete ui; | ||||
|     delete vpn; | ||||
|     delete settingsDialog; | ||||
|     delete settingsButton; | ||||
| } | ||||
|  | ||||
| void GPClient::setupSettings() | ||||
| { | ||||
|     settingsButton = new QPushButton(this); | ||||
| @@ -68,15 +60,15 @@ void GPClient::setupSettings() | ||||
|  | ||||
| void GPClient::onSettingsButtonClicked() | ||||
| { | ||||
|     settingsDialog->setExtraArgs(settings::get("extraArgs", "").toString()); | ||||
|     settingsDialog->setClientos(settings::get("clientos", "Linux").toString()); | ||||
|     settingsDialog->setOsVersion(settings::get("os-version", QSysInfo::prettyProductName()).toString()); | ||||
|     settingsDialog->show(); | ||||
| } | ||||
|  | ||||
| void GPClient::onSettingsAccepted() | ||||
| { | ||||
|     settings::save("extraArgs", settingsDialog->extraArgs()); | ||||
|     settings::save("clientos", settingsDialog->clientos()); | ||||
|     settings::save("os-version", settingsDialog->osVersion()); | ||||
| } | ||||
|  | ||||
| void GPClient::on_connectButton_clicked() | ||||
| @@ -114,7 +106,7 @@ void GPClient::initSystemTrayIcon() | ||||
|     connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect); | ||||
|     contextMenu->addMenu(gatewaySwitchMenu); | ||||
|     contextMenu->addSeparator(); | ||||
|     clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset Settings", this, &GPClient::clearSettings); | ||||
|     clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset", this, &GPClient::reset); | ||||
|     quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit); | ||||
|  | ||||
|     systemTrayIcon->show(); | ||||
| @@ -138,7 +130,7 @@ void GPClient::initVpnStatus() { | ||||
|  | ||||
| void GPClient::populateGatewayMenu() | ||||
| { | ||||
|     PLOGI << "Populating the Switch Gateway menu..."; | ||||
|     LOGI << "Populating the Switch Gateway menu..."; | ||||
|  | ||||
|     const QList<GPGateway> gateways = allGateways(); | ||||
|     gatewaySwitchMenu->clear(); | ||||
| @@ -155,7 +147,7 @@ void GPClient::populateGatewayMenu() | ||||
|         if (g.name() == currentGatewayName) { | ||||
|             iconImage = ":/images/radio_selected.png"; | ||||
|         } | ||||
|         gatewaySwitchMenu->addAction(QIcon(iconImage), g.name())->setData(i); | ||||
|         gatewaySwitchMenu->addAction(QIcon(iconImage), QString("%1 (%2)").arg(g.name(), g.address()))->setData(i); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -223,7 +215,7 @@ void GPClient::onGatewayChanged(QAction *action) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const GPGateway g = allGateways().at(index); | ||||
|     const auto g = allGateways().at(index); | ||||
|  | ||||
|     // If the selected gateway is the same as the current gateway | ||||
|     if (g.name() == currentGateway().name()) { | ||||
| @@ -243,10 +235,10 @@ void GPClient::onGatewayChanged(QAction *action) | ||||
|  | ||||
| void GPClient::doConnect() | ||||
| { | ||||
|     PLOGI << "Start connecting..."; | ||||
|     LOGI << "Start connecting..."; | ||||
|  | ||||
|     const QString btnText = ui->connectButton->text(); | ||||
|     const QString portal = this->portal(); | ||||
|     const auto btnText = ui->connectButton->text(); | ||||
|     const auto portal = this->portal(); | ||||
|  | ||||
|     // Display the main window if portal is empty | ||||
|     if (portal.isEmpty()) { | ||||
| @@ -259,16 +251,16 @@ void GPClient::doConnect() | ||||
|  | ||||
|         // Login to the previously saved gateway | ||||
|         if (!currentGateway().name().isEmpty()) { | ||||
|             PLOGI << "Start gateway login using the previously saved gateway..."; | ||||
|             LOGI << "Start gateway login using the previously saved gateway..."; | ||||
|             isQuickConnect = true; | ||||
|             gatewayLogin(); | ||||
|         } else { | ||||
|             // Perform the portal login | ||||
|             PLOGI << "Start portal login..."; | ||||
|             LOGI << "Start portal login..."; | ||||
|             portalLogin(); | ||||
|         } | ||||
|     } else { | ||||
|         PLOGI << "Start disconnecting the VPN..."; | ||||
|         LOGI << "Start disconnecting the VPN..."; | ||||
|  | ||||
|         ui->statusLabel->setText("Disconnecting..."); | ||||
|         updateConnectionStatus(VpnStatus::pending); | ||||
| @@ -279,14 +271,26 @@ void GPClient::doConnect() | ||||
| // Login to the portal interface to get the portal config and preferred gateway | ||||
| void GPClient::portalLogin() | ||||
| { | ||||
|     PortalAuthenticator *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString()); | ||||
|     auto *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString()); | ||||
|  | ||||
|     connect(portalAuth, &PortalAuthenticator::success, this, &GPClient::onPortalSuccess); | ||||
|     connect(portalAuth, &PortalAuthenticator::success, [this, portalAuth](const PortalConfigResponse response, const QString region) { | ||||
|         this->onPortalSuccess(response, region); | ||||
|         portalAuth->deleteLater(); | ||||
|     }); | ||||
|     // Prelogin failed on the portal interface, try to treat the portal as a gateway interface | ||||
|     connect(portalAuth, &PortalAuthenticator::preloginFailed, this, &GPClient::onPortalPreloginFail); | ||||
|     connect(portalAuth, &PortalAuthenticator::portalConfigFailed, this, &GPClient::onPortalConfigFail); | ||||
|     connect(portalAuth, &PortalAuthenticator::preloginFailed, [this, portalAuth](const QString msg) { | ||||
|         this->onPortalPreloginFail(msg); | ||||
|         portalAuth->deleteLater(); | ||||
|     }); | ||||
|     connect(portalAuth, &PortalAuthenticator::portalConfigFailed, [this, portalAuth](const QString msg) { | ||||
|         this->onPortalConfigFail(msg); | ||||
|         portalAuth->deleteLater(); | ||||
|     }); | ||||
|     // Portal login failed | ||||
|     connect(portalAuth, &PortalAuthenticator::fail, this, &GPClient::onPortalFail); | ||||
|     connect(portalAuth, &PortalAuthenticator::fail, [this, portalAuth](const QString &msg) { | ||||
|         this->onPortalFail(msg); | ||||
|         portalAuth->deleteLater(); | ||||
|     }); | ||||
|  | ||||
|     ui->statusLabel->setText("Authenticating..."); | ||||
|     updateConnectionStatus(VpnStatus::pending); | ||||
| @@ -295,11 +299,11 @@ void GPClient::portalLogin() | ||||
|  | ||||
| void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region) | ||||
| { | ||||
|     PLOGI << "Portal authentication succeeded."; | ||||
|     LOGI << "Portal authentication succeeded."; | ||||
|  | ||||
|     // No gateway found in protal configuration | ||||
|     // No gateway found in portal configuration | ||||
|     if (portalConfig.allGateways().size() == 0) { | ||||
|         PLOGI << "No gateway found in portal configuration, treat the portal address as a gateway."; | ||||
|         LOGI << "No gateway found in portal configuration, treat the portal address as a gateway."; | ||||
|         tryGatewayLogin(); | ||||
|         return; | ||||
|     } | ||||
| @@ -314,13 +318,13 @@ void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QS | ||||
|  | ||||
| void GPClient::onPortalPreloginFail(const QString msg) | ||||
| { | ||||
|     PLOGI << "Portal prelogin failed: " << msg; | ||||
|     LOGI << "Portal prelogin failed, treat the portal address as a gateway." << msg; | ||||
|     tryGatewayLogin(); | ||||
| } | ||||
|  | ||||
| void GPClient::onPortalConfigFail(const QString msg) | ||||
| { | ||||
|     PLOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway."; | ||||
|     LOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway."; | ||||
|     tryGatewayLogin(); | ||||
| } | ||||
|  | ||||
| @@ -335,7 +339,7 @@ void GPClient::onPortalFail(const QString &msg) | ||||
|  | ||||
| void GPClient::tryGatewayLogin() | ||||
| { | ||||
|     PLOGI << "Try to preform login on the the gateway interface..."; | ||||
|     LOGI << "Try to perform login on the the gateway interface..."; | ||||
|  | ||||
|     // Treat the portal input as the gateway address | ||||
|     GPGateway g; | ||||
| @@ -354,15 +358,22 @@ void GPClient::tryGatewayLogin() | ||||
| // Login to the gateway | ||||
| void GPClient::gatewayLogin() | ||||
| { | ||||
|     PLOGI << "Performing gateway login..."; | ||||
|     LOGI << "Performing gateway login..."; | ||||
|  | ||||
|     GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig); | ||||
|     params.setClientos(settings::get("clientos", "Linux").toString()); | ||||
|  | ||||
|     GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params); | ||||
|     GatewayAuthenticator *gatewayAuth; | ||||
|     gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params); | ||||
|  | ||||
|     connect(gatewayAuth, &GatewayAuthenticator::success, this, &GPClient::onGatewaySuccess); | ||||
|     connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail); | ||||
|     connect(gatewayAuth, &GatewayAuthenticator::success, [this, gatewayAuth](const QString &authToken) { | ||||
|         this->onGatewaySuccess(authToken); | ||||
|         gatewayAuth->deleteLater(); | ||||
|     }); | ||||
|     connect(gatewayAuth, &GatewayAuthenticator::fail, [this, gatewayAuth](const QString &msg) { | ||||
|         this->onGatewayFail(msg); | ||||
|         gatewayAuth->deleteLater(); | ||||
|     }); | ||||
|  | ||||
|     ui->statusLabel->setText("Authenticating..."); | ||||
|     updateConnectionStatus(VpnStatus::pending); | ||||
| @@ -371,14 +382,14 @@ void GPClient::gatewayLogin() | ||||
|  | ||||
| void GPClient::onGatewaySuccess(const QString &authCookie) | ||||
| { | ||||
|     PLOGI << "Gateway login succeeded, got the cookie " << authCookie; | ||||
|     LOGI << "Gateway login succeeded, got the cookie " << authCookie; | ||||
|  | ||||
|     isQuickConnect = false; | ||||
|     QList<QString> gatewayAddresses; | ||||
|     for (GPGateway &gw : allGateways()) { | ||||
|       gatewayAddresses.push_back(gw.address()); | ||||
|     } | ||||
|     vpn->connect(currentGateway().address(), gatewayAddresses, portalConfig.username(), authCookie, settings::get("extraArgs", "").toString()); | ||||
|     vpn->connect(currentGateway().address(), gatewayAddresses, portalConfig.username(), authCookie); | ||||
|     ui->statusLabel->setText("Connecting..."); | ||||
|     updateConnectionStatus(VpnStatus::pending); | ||||
| } | ||||
| @@ -388,6 +399,7 @@ void GPClient::onGatewayFail(const QString &msg) | ||||
|     // If the quick connect on gateway failed, perform the portal login | ||||
|     if (isQuickConnect && !msg.isEmpty()) { | ||||
|         isQuickConnect = false; | ||||
|         LOGI << "Quick connection failed, trying to portal login..."; | ||||
|         portalLogin(); | ||||
|         return; | ||||
|     } | ||||
| @@ -428,13 +440,19 @@ bool GPClient::connected() const | ||||
|  | ||||
| QList<GPGateway> GPClient::allGateways() const | ||||
| { | ||||
|     const QString gatewaysJson = settings::get(portal() + "_gateways").toString(); | ||||
|     return GPGateway::fromJson(gatewaysJson); | ||||
|  | ||||
|     QList<GPGateway> gateways; | ||||
|  | ||||
|     for (auto g :settings::get_all("_gateways$") ){ | ||||
|  | ||||
|     	gateways.append(GPGateway::fromJson(settings::get(g).toString())); | ||||
|     } | ||||
|     return gateways; | ||||
| } | ||||
|  | ||||
| void GPClient::setAllGateways(QList<GPGateway> gateways) | ||||
| { | ||||
|     PLOGI << "Updating all the gateways..."; | ||||
|     LOGI << "Updating all the gateways..."; | ||||
|  | ||||
|     settings::save(portal() + "_gateways", GPGateway::serialize(gateways)); | ||||
|     populateGatewayMenu(); | ||||
| @@ -454,13 +472,14 @@ GPGateway GPClient::currentGateway() const | ||||
|  | ||||
| void GPClient::setCurrentGateway(const GPGateway gateway) | ||||
| { | ||||
|     PLOGI << "Updating the current gateway to " << gateway.name(); | ||||
|     LOGI << "Updating the current gateway to " << gateway.name(); | ||||
|  | ||||
|     settings::save(portal() + "_selectedGateway", gateway.name()); | ||||
|     ui->portalInput->setText(gateway.address()); | ||||
|     populateGatewayMenu(); | ||||
| } | ||||
|  | ||||
| void GPClient::clearSettings() | ||||
| void GPClient::reset() | ||||
| { | ||||
|     settings::clear(); | ||||
|     populateGatewayMenu(); | ||||
| @@ -496,5 +515,5 @@ void GPClient::onVPNError(QString errorMessage) | ||||
|  | ||||
| void GPClient::onVPNLogAvailable(QString log) | ||||
| { | ||||
|     PLOGI << log; | ||||
|     LOGI << log; | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include "portalconfigresponse.h" | ||||
| #include "settingsdialog.h" | ||||
| #include "vpn.h" | ||||
| #include "gatewayauthenticator.h" | ||||
|  | ||||
| QT_BEGIN_NAMESPACE | ||||
| namespace Ui { class GPClient; } | ||||
| @@ -20,7 +21,6 @@ class GPClient : public QMainWindow | ||||
|  | ||||
| public: | ||||
|     GPClient(QWidget *parent, IVpn *vpn); | ||||
|     ~GPClient(); | ||||
|  | ||||
|     void activate(); | ||||
|     void quit(); | ||||
| @@ -32,6 +32,7 @@ public: | ||||
|     void setCurrentGateway(const GPGateway gateway); | ||||
|  | ||||
|     void doConnect(); | ||||
|     void reset(); | ||||
|  | ||||
| private slots: | ||||
|     void onSettingsButtonClicked(); | ||||
| @@ -99,7 +100,5 @@ private: | ||||
|  | ||||
|     QList<GPGateway> allGateways() const; | ||||
|     void setAllGateways(QList<GPGateway> gateways); | ||||
|  | ||||
|     void clearSettings(); | ||||
| }; | ||||
| #endif // GPCLIENT_H | ||||
|   | ||||
| @@ -7,9 +7,14 @@ | ||||
| #include <QtNetwork/QSslConfiguration> | ||||
| #include <QtNetwork/QSslSocket> | ||||
| #include <plog/Log.h> | ||||
| #include <QWebEngineProfile> | ||||
| #include <QWebEngineCookieStore> | ||||
| #include <keychain.h> | ||||
|  | ||||
| #include "gphelper.h" | ||||
|  | ||||
| using namespace QKeychain; | ||||
|  | ||||
| QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager; | ||||
|  | ||||
| QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) | ||||
| @@ -19,6 +24,7 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) | ||||
|     // Skip the ssl verifying | ||||
|     QSslConfiguration conf = request.sslConfiguration(); | ||||
|     conf.setPeerVerifyMode(QSslSocket::VerifyNone); | ||||
|     conf.setSslOption(QSsl::SslOptionDisableLegacyRenegotiation, false); | ||||
|     request.setSslConfiguration(conf); | ||||
|  | ||||
|     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); | ||||
| @@ -32,13 +38,13 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) | ||||
|  | ||||
| GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName) | ||||
| { | ||||
|     PLOGI << gateways.size() << " gateway(s) avaiable, filter the gateways with rule: " << ruleName; | ||||
|     LOGI << gateways.size() << " gateway(s) available, filter the gateways with rule: " << ruleName; | ||||
|  | ||||
|     GPGateway gateway = gateways.first(); | ||||
|  | ||||
|     for (GPGateway g : gateways) { | ||||
|         if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) { | ||||
|             PLOGI << "Find a preferred gateway: " << g.name(); | ||||
|             LOGI << "Find a preferred gateway: " << g.name(); | ||||
|             gateway = g; | ||||
|         } | ||||
|     } | ||||
| @@ -48,8 +54,8 @@ GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, co | ||||
|  | ||||
| QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml) | ||||
| { | ||||
|     PLOGI << "Start parsing the gateway response..."; | ||||
|     PLOGI << "The gateway response is: " << xml; | ||||
|     LOGI << "Start parsing the gateway response..."; | ||||
|     LOGI << "The gateway response is: " << xml; | ||||
|  | ||||
|     QXmlStreamReader xmlReader{xml}; | ||||
|     QList<QString> args; | ||||
| @@ -112,6 +118,12 @@ QVariant gpclient::helper::settings::get(const QString &key, const QVariant &def | ||||
|     return _settings->value(key, defaultValue); | ||||
| } | ||||
|  | ||||
| QStringList gpclient::helper::settings::get_all(const QString &key, const QVariant &defaultValue) | ||||
| { | ||||
| 	QRegularExpression re(key); | ||||
| 	return 	_settings->allKeys().filter(re); | ||||
| } | ||||
|  | ||||
| void gpclient::helper::settings::save(const QString &key, const QVariant &value) | ||||
| { | ||||
|     _settings->setValue(key, value); | ||||
| @@ -126,4 +138,41 @@ void gpclient::helper::settings::clear() | ||||
|             _settings->remove(key); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     QWebEngineProfile::defaultProfile()->cookieStore()->deleteAllCookies(); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool gpclient::helper::settings::secureSave(const QString &key, const QString &value) { | ||||
|     WritePasswordJob job( QLatin1String("gpclient") ); | ||||
|     job.setAutoDelete( false ); | ||||
|     job.setKey( key ); | ||||
|     job.setTextData( value ); | ||||
|     QEventLoop loop; | ||||
|     job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); | ||||
|     job.start(); | ||||
|     loop.exec(); | ||||
|     if ( job.error() ) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool gpclient::helper::settings::secureGet(const QString &key, QString &value) { | ||||
|     ReadPasswordJob job( QLatin1String("gpclient") ); | ||||
|     job.setAutoDelete( false ); | ||||
|     job.setKey( key ); | ||||
|     QEventLoop loop; | ||||
|     job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) ); | ||||
|     job.start(); | ||||
|     loop.exec(); | ||||
|  | ||||
|     const QString pw = job.textData(); | ||||
|     if ( job.error() ) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     value = pw; | ||||
|     return true; | ||||
| } | ||||
|   | ||||
| @@ -34,8 +34,12 @@ namespace gpclient { | ||||
|             static const QStringList reservedKeys {"extraArgs", "clientos"}; | ||||
|  | ||||
|             QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); | ||||
|             QStringList get_all(const QString &key, const QVariant &defaultValue = QVariant()); | ||||
|             void save(const QString &key, const QVariant &value); | ||||
|             void clear(); | ||||
|  | ||||
|             bool secureSave(const QString &key, const QString &value); | ||||
|             bool secureGet(const QString &key, QString &value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| #include <QtCore/QUrlQuery> | ||||
|  | ||||
| #include "loginparams.h" | ||||
| #include "gphelper.h" | ||||
|  | ||||
| using namespace gpclient::helper; | ||||
|  | ||||
| LoginParams::LoginParams(const QString clientos) | ||||
| { | ||||
| @@ -14,13 +17,18 @@ LoginParams::LoginParams(const QString clientos) | ||||
|     params.addQueryItem("ok", "Login"); | ||||
|     params.addQueryItem("direct", "yes"); | ||||
|     params.addQueryItem("clientVer", "4100"); | ||||
|     params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName())); | ||||
|  | ||||
|     // add the clientos parameter if not empty | ||||
|     if (!clientos.isEmpty()) { | ||||
|         params.addQueryItem("clientos", clientos); | ||||
|     } | ||||
|  | ||||
|     auto osVersion = settings::get("os-version", "").toString(); | ||||
|     if (osVersion.isEmpty()) { | ||||
|         osVersion = QSysInfo::prettyProductName(); | ||||
|     } | ||||
|     params.addQueryItem("os-version", QUrl::toPercentEncoding(osVersion)); | ||||
|  | ||||
|     params.addQueryItem("portal-userauthcookie", ""); | ||||
|     params.addQueryItem("portal-prelogonuserauthcookie", ""); | ||||
|     params.addQueryItem("prelogin-cookie", ""); | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #include <QtCore/QObject> | ||||
| #include <QtCore/QString> | ||||
| #include <QtCore/QDir> | ||||
| #include <QtCore/QStandardPaths> | ||||
| #include <plog/Log.h> | ||||
| #include <plog/Init.h> | ||||
| @@ -15,19 +14,26 @@ | ||||
| #include "sigwatch.h" | ||||
| #include "version.h" | ||||
|  | ||||
| #define QT_AUTO_SCREEN_SCALE_FACTOR "QT_AUTO_SCREEN_SCALE_FACTOR" | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|     plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr); | ||||
|     plog::init(plog::debug, &consoleAppender); | ||||
|  | ||||
|     PLOGI << "GlobalProtect started, version: " << VERSION; | ||||
|     LOGI << "GlobalProtect started, version: " << VERSION; | ||||
|  | ||||
|     QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); | ||||
|     auto port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); | ||||
|     auto hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR)); | ||||
|  | ||||
|     if (port == "") { | ||||
|     if (port.isEmpty()) { | ||||
|         qputenv(ENV_CDP_PORT, "12315"); | ||||
|     } | ||||
|  | ||||
|     if (hidpiSupport.isEmpty()) { | ||||
|         qputenv(QT_AUTO_SCREEN_SCALE_FACTOR, "1"); | ||||
|     } | ||||
|  | ||||
|     SingleApplication app(argc, argv); | ||||
|     app.setQuitOnLastWindowClosed(false); | ||||
|  | ||||
| @@ -39,16 +45,17 @@ int main(int argc, char *argv[]) | ||||
|     parser.addOptions({ | ||||
|       {"json", "Write the result of the handshake with the GlobalConnect server to stdout as JSON and terminate. Useful for scripting."}, | ||||
|       {"now", "Do not show the dialog with the connect button; connect immediately instead."}, | ||||
|       {"start-minimized", "Launch the client minimized."}, | ||||
|       {"reset", "Reset the client's settings."}, | ||||
|     }); | ||||
|     parser.process(app); | ||||
|  | ||||
|     const QStringList positional = parser.positionalArguments(); | ||||
|     const auto positional = parser.positionalArguments(); | ||||
|  | ||||
|     IVpn *vpn = parser.isSet("json") // yes it leaks, but this is cleared on exit anyway | ||||
|     auto *vpn = parser.isSet("json") // yes it leaks, but this is cleared on exit anyway | ||||
|       ? static_cast<IVpn*>(new VpnJson(nullptr)) // Print to stdout and exit | ||||
|       : static_cast<IVpn*>(new VpnDbus(nullptr)); // Contact GPService daemon via dbus | ||||
|     GPClient w(nullptr, vpn); | ||||
|     w.show(); | ||||
|  | ||||
|     if (positional.size() > 0) { | ||||
|       w.portal(positional.at(0)); | ||||
| @@ -69,12 +76,21 @@ int main(int argc, char *argv[]) | ||||
|     sigwatch.watchForSignal(SIGHUP); | ||||
|     QObject::connect(&sigwatch, &UnixSignalWatcher::unixSignal, &w, &GPClient::quit); | ||||
|  | ||||
|     if (parser.isSet("now")) { | ||||
|       w.doConnect(); | ||||
|     } | ||||
|     if (parser.isSet("json")) { | ||||
|         QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit); | ||||
|     } | ||||
|  | ||||
|     if (parser.isSet("reset")) { | ||||
|         w.reset(); | ||||
|     } | ||||
|  | ||||
|     if (parser.isSet("now")) { | ||||
|       w.doConnect(); | ||||
|     } else if (parser.isSet("start-minimized")) { | ||||
|       w.showMinimized(); | ||||
|     } else { | ||||
|       w.show(); | ||||
|     } | ||||
|  | ||||
|     return app.exec(); | ||||
| } | ||||
|   | ||||
| @@ -1,64 +0,0 @@ | ||||
| #include <QtGui/QCloseEvent> | ||||
|  | ||||
| #include "normalloginwindow.h" | ||||
| #include "ui_normalloginwindow.h" | ||||
|  | ||||
| NormalLoginWindow::NormalLoginWindow(QWidget *parent) : | ||||
|     QDialog(parent), | ||||
|     ui(new Ui::NormalLoginWindow) | ||||
| { | ||||
|     ui->setupUi(this); | ||||
|     setWindowTitle("GlobalProtect Login"); | ||||
|     setFixedSize(width(), height()); | ||||
|     setModal(true); | ||||
| } | ||||
|  | ||||
| NormalLoginWindow::~NormalLoginWindow() | ||||
| { | ||||
|     delete ui; | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::setAuthMessage(QString message) | ||||
| { | ||||
|     ui->authMessage->setText(message); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::setUsernameLabel(QString label) | ||||
| { | ||||
|     ui->username->setPlaceholderText(label); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::setPasswordLabel(QString label) | ||||
| { | ||||
|     ui->password->setPlaceholderText(label); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::setPortalAddress(QString portal) | ||||
| { | ||||
|     ui->portalAddress->setText(portal); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::setProcessing(bool isProcessing) | ||||
| { | ||||
|     ui->username->setReadOnly(isProcessing); | ||||
|     ui->password->setReadOnly(isProcessing); | ||||
|     ui->loginButton->setDisabled(isProcessing); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::on_loginButton_clicked() | ||||
| { | ||||
|     const QString username = ui->username->text().trimmed(); | ||||
|     const QString password = ui->password->text().trimmed(); | ||||
|  | ||||
|     if (username.isEmpty() || password.isEmpty()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     emit performLogin(username, password); | ||||
| } | ||||
|  | ||||
| void NormalLoginWindow::closeEvent(QCloseEvent *event) | ||||
| { | ||||
|     event->accept(); | ||||
|     reject(); | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| #ifndef PORTALAUTHWINDOW_H | ||||
| #define PORTALAUTHWINDOW_H | ||||
|  | ||||
| #include <QtWidgets/QDialog> | ||||
|  | ||||
| namespace Ui { | ||||
| class NormalLoginWindow; | ||||
| } | ||||
|  | ||||
| class NormalLoginWindow : public QDialog | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit NormalLoginWindow(QWidget *parent = nullptr); | ||||
|     ~NormalLoginWindow(); | ||||
|  | ||||
|     void setAuthMessage(QString); | ||||
|     void setUsernameLabel(QString); | ||||
|     void setPasswordLabel(QString); | ||||
|     void setPortalAddress(QString); | ||||
|  | ||||
|     void setProcessing(bool isProcessing); | ||||
|  | ||||
| private slots: | ||||
|     void on_loginButton_clicked(); | ||||
|  | ||||
| signals: | ||||
|     void performLogin(QString username, QString password); | ||||
|  | ||||
| private: | ||||
|     Ui::NormalLoginWindow *ui; | ||||
|  | ||||
|     void closeEvent(QCloseEvent *event); | ||||
| }; | ||||
|  | ||||
| #endif // PORTALAUTHWINDOW_H | ||||
| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| #include "portalauthenticator.h" | ||||
| #include "gphelper.h" | ||||
| #include "normalloginwindow.h" | ||||
| #include "standardloginwindow.h" | ||||
| #include "samlloginwindow.h" | ||||
| #include "loginparams.h" | ||||
| #include "preloginresponse.h" | ||||
| @@ -25,12 +25,14 @@ PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& c | ||||
|  | ||||
| PortalAuthenticator::~PortalAuthenticator() | ||||
| { | ||||
|     delete normalLoginWindow; | ||||
|     delete standardLoginWindow; | ||||
| } | ||||
|  | ||||
| void PortalAuthenticator::authenticate() | ||||
| { | ||||
|     PLOGI << "Preform portal prelogin at " << preloginUrl; | ||||
|     attempts++; | ||||
|  | ||||
|     LOGI << QString("(%1/%2) attempts").arg(attempts).arg(MAX_ATTEMPTS) << ", perform portal prelogin at " << preloginUrl; | ||||
|  | ||||
|     QNetworkReply *reply = createRequest(preloginUrl); | ||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished); | ||||
| @@ -38,20 +40,20 @@ void PortalAuthenticator::authenticate() | ||||
|  | ||||
| void PortalAuthenticator::onPreloginFinished() | ||||
| { | ||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); | ||||
|     auto *reply = qobject_cast<QNetworkReply*>(sender()); | ||||
|  | ||||
|     if (reply->error()) { | ||||
|         PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString()); | ||||
|         LOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString()); | ||||
|         emit preloginFailed("Error occurred on the portal prelogin interface."); | ||||
|         delete reply; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     PLOGI << "Portal prelogin succeeded."; | ||||
|     LOGI << "Portal prelogin succeeded."; | ||||
|  | ||||
|     preloginResponse = PreloginResponse::parse(reply->readAll()); | ||||
|  | ||||
|     PLOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region(); | ||||
|     LOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region(); | ||||
|  | ||||
|     if (preloginResponse.hasSamlAuthFields()) { | ||||
|         // Do SAML authentication | ||||
| @@ -60,7 +62,7 @@ void PortalAuthenticator::onPreloginFinished() | ||||
|         // Do normal username/password authentication | ||||
|         tryAutoLogin(); | ||||
|     } else { | ||||
|         PLOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse())); | ||||
|         LOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse())); | ||||
|         emit preloginFailed("Unknown response for portal prelogin interface."); | ||||
|     } | ||||
|  | ||||
| @@ -73,7 +75,7 @@ void PortalAuthenticator::tryAutoLogin() | ||||
|     const QString password = settings::get("password").toString(); | ||||
|  | ||||
|     if (!username.isEmpty() && !password.isEmpty()) { | ||||
|         PLOGI << "Trying auto login using the saved credentials"; | ||||
|         LOGI << "Trying auto login using the saved credentials"; | ||||
|         isAutoLogin = true; | ||||
|         fetchConfig(settings::get("username").toString(), settings::get("password").toString()); | ||||
|     } else { | ||||
| @@ -83,25 +85,21 @@ void PortalAuthenticator::tryAutoLogin() | ||||
|  | ||||
| void PortalAuthenticator::normalAuth() | ||||
| { | ||||
|     PLOGI << "Trying to launch the normal login window..."; | ||||
|     LOGI << "Trying to launch the normal login window..."; | ||||
|  | ||||
|     normalLoginWindow = new NormalLoginWindow; | ||||
|     normalLoginWindow->setPortalAddress(portal); | ||||
|     normalLoginWindow->setAuthMessage(preloginResponse.authMessage()); | ||||
|     normalLoginWindow->setUsernameLabel(preloginResponse.labelUsername()); | ||||
|     normalLoginWindow->setPasswordLabel(preloginResponse.labelPassword()); | ||||
|     standardLoginWindow = new StandardLoginWindow {portal, preloginResponse.labelUsername(), preloginResponse.labelPassword(), preloginResponse.authMessage() }; | ||||
|  | ||||
|     // Do login | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); | ||||
|     connect(normalLoginWindow, &NormalLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); | ||||
|     connect(standardLoginWindow, &StandardLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); | ||||
|  | ||||
|     normalLoginWindow->show(); | ||||
|     standardLoginWindow->show(); | ||||
| } | ||||
|  | ||||
| void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) | ||||
| { | ||||
|     normalLoginWindow->setProcessing(true); | ||||
|     standardLoginWindow->setProcessing(true); | ||||
|     fetchConfig(username, password); | ||||
| } | ||||
|  | ||||
| @@ -112,19 +110,28 @@ void PortalAuthenticator::onLoginWindowRejected() | ||||
|  | ||||
| void PortalAuthenticator::onLoginWindowFinished() | ||||
| { | ||||
|     delete normalLoginWindow; | ||||
|     normalLoginWindow = nullptr; | ||||
|     delete standardLoginWindow; | ||||
|     standardLoginWindow = nullptr; | ||||
| } | ||||
|  | ||||
| void PortalAuthenticator::samlAuth() | ||||
| { | ||||
|     PLOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod(); | ||||
|     LOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod(); | ||||
|  | ||||
|     SAMLLoginWindow *loginWindow = new SAMLLoginWindow; | ||||
|     auto *loginWindow = new SAMLLoginWindow(this->portal); | ||||
|  | ||||
|     connect(loginWindow, &SAMLLoginWindow::success, this, &PortalAuthenticator::onSAMLLoginSuccess); | ||||
|     connect(loginWindow, &SAMLLoginWindow::fail, this, &PortalAuthenticator::onSAMLLoginFail); | ||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); | ||||
|     connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> samlResult) { | ||||
|         this->onSAMLLoginSuccess(samlResult); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|     connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString msg) { | ||||
|         this->onSAMLLoginFail(code, msg); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|     connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() { | ||||
|         this->onLoginWindowRejected(); | ||||
|         loginWindow->deleteLater(); | ||||
|     }); | ||||
|  | ||||
|     loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl); | ||||
| } | ||||
| @@ -132,17 +139,22 @@ void PortalAuthenticator::samlAuth() | ||||
| void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult) | ||||
| { | ||||
|     if (samlResult.contains("preloginCookie")) { | ||||
|         PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); | ||||
|         LOGI << "SAML login succeeded, got the prelogin-cookie"; | ||||
|     } else { | ||||
|         PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie"); | ||||
|         LOGI << "SAML login succeeded, got the portal-userauthcookie"; | ||||
|     } | ||||
|  | ||||
|     fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie")); | ||||
| } | ||||
|  | ||||
| void PortalAuthenticator::onSAMLLoginFail(const QString msg) | ||||
| void PortalAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg) | ||||
| { | ||||
|     if (code == "ERR002" && attempts < MAX_ATTEMPTS) { | ||||
|         LOGI << "Failed to authenticate, trying to re-authenticate..."; | ||||
|         authenticate(); | ||||
|     } else { | ||||
|         emitFail(msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie) | ||||
| @@ -158,9 +170,9 @@ void PortalAuthenticator::fetchConfig(QString username, QString password, QStrin | ||||
|     this->username = username; | ||||
|     this->password = password; | ||||
|  | ||||
|     PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username; | ||||
|     LOGI << "Fetching the portal config from " << configUrl; | ||||
|  | ||||
|     QNetworkReply *reply = createRequest(configUrl, loginParams.toUtf8()); | ||||
|     auto *reply = createRequest(configUrl, loginParams.toUtf8()); | ||||
|     connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished); | ||||
| } | ||||
|  | ||||
| @@ -169,11 +181,11 @@ void PortalAuthenticator::onFetchConfigFinished() | ||||
|     QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); | ||||
|  | ||||
|     if (reply->error()) { | ||||
|         PLOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString()); | ||||
|         LOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString()); | ||||
|  | ||||
|         // Login failed, enable the fields of the normal login window | ||||
|         if (normalLoginWindow) { | ||||
|             normalLoginWindow->setProcessing(false); | ||||
|         if (standardLoginWindow) { | ||||
|             standardLoginWindow->setProcessing(false); | ||||
|             openMessageBox("Portal login failed.", "Please check your credentials and try again."); | ||||
|         } else if (isAutoLogin) { | ||||
|             isAutoLogin = false; | ||||
| @@ -184,7 +196,7 @@ void PortalAuthenticator::onFetchConfigFinished() | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     PLOGI << "Fetch the portal config succeeded."; | ||||
|     LOGI << "Fetch the portal config succeeded."; | ||||
|     PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll()); | ||||
|  | ||||
|     // Add the username & password to the response object | ||||
| @@ -192,10 +204,10 @@ void PortalAuthenticator::onFetchConfigFinished() | ||||
|     response.setPassword(password); | ||||
|  | ||||
|     // Close the login window | ||||
|     if (normalLoginWindow) { | ||||
|         PLOGI << "Closing the NormalLoginWindow..."; | ||||
|     if (standardLoginWindow) { | ||||
|         LOGI << "Closing the StandardLoginWindow..."; | ||||
|  | ||||
|         normalLoginWindow->close(); | ||||
|         standardLoginWindow->close(); | ||||
|     } | ||||
|  | ||||
|     emit success(response, preloginResponse.region()); | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include <QtCore/QObject> | ||||
|  | ||||
| #include "portalconfigresponse.h" | ||||
| #include "normalloginwindow.h" | ||||
| #include "standardloginwindow.h" | ||||
| #include "samlloginwindow.h" | ||||
| #include "preloginresponse.h" | ||||
|  | ||||
| @@ -30,10 +30,12 @@ private slots: | ||||
|     void onLoginWindowRejected(); | ||||
|     void onLoginWindowFinished(); | ||||
|     void onSAMLLoginSuccess(const QMap<QString, QString> samlResult); | ||||
|     void onSAMLLoginFail(const QString msg); | ||||
|     void onSAMLLoginFail(const QString &code, const QString &msg); | ||||
|     void onFetchConfigFinished(); | ||||
|  | ||||
| private: | ||||
|     static const auto MAX_ATTEMPTS{ 5 }; | ||||
|  | ||||
|     QString portal; | ||||
|     QString clientos; | ||||
|     QString preloginUrl; | ||||
| @@ -41,11 +43,12 @@ private: | ||||
|     QString username; | ||||
|     QString password; | ||||
|  | ||||
|     int attempts{ 0 }; | ||||
|     PreloginResponse preloginResponse; | ||||
|  | ||||
|     bool isAutoLogin { false }; | ||||
|     bool isAutoLogin{ false }; | ||||
|  | ||||
|     NormalLoginWindow *normalLoginWindow{ nullptr }; | ||||
|     StandardLoginWindow *standardLoginWindow { nullptr }; | ||||
|  | ||||
|     void tryAutoLogin(); | ||||
|     void normalAuth(); | ||||
|   | ||||
| @@ -17,7 +17,7 @@ PortalConfigResponse::~PortalConfigResponse() | ||||
|  | ||||
| PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) | ||||
| { | ||||
|     PLOGI << "Start parsing the portal configuration..."; | ||||
|     LOGI << "Start parsing the portal configuration..."; | ||||
|  | ||||
|     QXmlStreamReader xmlReader(xml); | ||||
|     PortalConfigResponse response; | ||||
| @@ -29,17 +29,17 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) | ||||
|         QString name = xmlReader.name().toString(); | ||||
|  | ||||
|         if (name == xmlUserAuthCookie) { | ||||
|             PLOGI << "Start reading " << name; | ||||
|             LOGI << "Start reading " << name; | ||||
|             response.setUserAuthCookie(xmlReader.readElementText()); | ||||
|         } else if (name == xmlPrelogonUserAuthCookie) { | ||||
|             PLOGI << "Start reading " << name; | ||||
|             LOGI << "Start reading " << name; | ||||
|             response.setPrelogonUserAuthCookie(xmlReader.readElementText()); | ||||
|         } else if (name == xmlGateways) { | ||||
|             response.setAllGateways(parseGateways(xmlReader)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     PLOGI << "Finished parsing portal configuration."; | ||||
|     LOGI << "Finished parsing portal configuration."; | ||||
|  | ||||
|     return response; | ||||
| } | ||||
| @@ -61,7 +61,7 @@ QString PortalConfigResponse::password() const | ||||
|  | ||||
| QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) | ||||
| { | ||||
|     PLOGI << "Start parsing the gateways from portal configuration..."; | ||||
|     LOGI << "Start parsing the gateways from portal configuration..."; | ||||
|  | ||||
|     QList<GPGateway> gateways; | ||||
|  | ||||
| @@ -78,58 +78,59 @@ QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader | ||||
|         // Parse the gateways -> external -> list -> entry | ||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { | ||||
|             GPGateway g; | ||||
|             QString address = xmlReader.attributes().value("name").toString(); | ||||
|             g.setAddress(address); | ||||
|             g.setPriorityRules(parsePriorityRules(xmlReader)); | ||||
|             g.setName(parseGatewayName(xmlReader)); | ||||
|             parseGateway(xmlReader, g); | ||||
|             gateways.append(g); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     PLOGI << "Finished parsing the gateways."; | ||||
|     LOGI << "Finished parsing the gateways."; | ||||
|  | ||||
|     return gateways; | ||||
| } | ||||
|  | ||||
| QMap<QString, int> PortalConfigResponse::parsePriorityRules(QXmlStreamReader &xmlReader) | ||||
| { | ||||
|     PLOGI << "Start parsing the priority rules..."; | ||||
| void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) { | ||||
|     LOGI << "Start parsing gateway..."; | ||||
|  | ||||
|     QMap<QString, int> priorityRules; | ||||
|  | ||||
|     while ((xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) && !xmlReader.hasError()) { | ||||
|         xmlReader.readNext(); | ||||
|  | ||||
|         if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { | ||||
|             QString ruleName = xmlReader.attributes().value("name").toString(); | ||||
|             // Read the priority tag | ||||
|             while (xmlReader.name() != "priority"){ | ||||
|                 xmlReader.readNext(); | ||||
|             } | ||||
|             int ruleValue = xmlReader.readElementText().toUInt(); | ||||
|             priorityRules.insert(ruleName, ruleValue); | ||||
|         } | ||||
|     auto finished = false; | ||||
|     while (!finished) { | ||||
|         if (reader.name() == "entry" && reader.isStartElement()) { | ||||
|             auto address = reader.attributes().value("name").toString(); | ||||
|             gateway.setAddress(address); | ||||
|         } else if (reader.name() == "description" && reader.isStartElement()) { // gateway name | ||||
|             gateway.setName(reader.readElementText()); | ||||
|         } else if (reader.name() == "priority-rule" && reader.isStartElement()) { // priority rules | ||||
|             parsePriorityRule(reader, gateway); | ||||
|         } | ||||
|  | ||||
|     PLOGI << "Finished parsing the priority rules."; | ||||
|  | ||||
|     return priorityRules; | ||||
|         auto result = reader.readNext(); | ||||
|         finished = result == QXmlStreamReader::Invalid || (reader.name() == "entry" && reader.isEndElement()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader) | ||||
| { | ||||
|     PLOGI << "Start parsing the gateway name..."; | ||||
| void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) { | ||||
|     LOGI << "Start parsing priority rule..."; | ||||
|  | ||||
|     while (xmlReader.name() != "description" || !xmlReader.isEndElement()) { | ||||
|         xmlReader.readNext(); | ||||
|         if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) { | ||||
|             PLOGI << "Finished parsing the gateway name"; | ||||
|             return xmlReader.readElementText(); | ||||
|     QMap<QString, int> priorityRules; | ||||
|     auto finished = false; | ||||
|  | ||||
|     while (!finished) { | ||||
|         // Parse the priority-rule -> entry | ||||
|         if (reader.name() == "entry" && reader.isStartElement()) { | ||||
|             auto ruleName = reader.attributes().value("name").toString(); | ||||
|             // move to the priority value | ||||
|             while (reader.readNextStartElement()) { | ||||
|                 if (reader.name() == "priority") { | ||||
|                     auto priority = reader.readElementText().toInt(); | ||||
|                     priorityRules.insert(ruleName, priority); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         auto result = reader.readNext(); | ||||
|         finished = result == QXmlStreamReader::Invalid || (reader.name() == "priority-rule" && reader.isEndElement()); | ||||
|     } | ||||
|  | ||||
|     PLOGE << "Error: <description> tag not found"; | ||||
|     return ""; | ||||
|     gateway.setPriorityRules(priorityRules); | ||||
| } | ||||
|  | ||||
| QString PortalConfigResponse::userAuthCookie() const | ||||
| @@ -137,11 +138,6 @@ QString PortalConfigResponse::userAuthCookie() const | ||||
|     return m_userAuthCookie; | ||||
| } | ||||
|  | ||||
| QString PortalConfigResponse::prelogonUserAuthCookie() const | ||||
| { | ||||
|     return m_prelogonAuthCookie; | ||||
| } | ||||
|  | ||||
| QList<GPGateway> PortalConfigResponse::allGateways() const | ||||
| { | ||||
|     return m_gateways; | ||||
|   | ||||
| @@ -19,7 +19,6 @@ public: | ||||
|     const QString &username() const; | ||||
|     QString password() const; | ||||
|     QString userAuthCookie() const; | ||||
|     QString prelogonUserAuthCookie() const; | ||||
|     QList<GPGateway> allGateways() const; | ||||
|     void setAllGateways(QList<GPGateway> gateways); | ||||
|  | ||||
| @@ -44,8 +43,9 @@ private: | ||||
|     void setPrelogonUserAuthCookie(const QString cookie); | ||||
|  | ||||
|     static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader); | ||||
|     static QMap<QString, int> parsePriorityRules(QXmlStreamReader &xmlReader); | ||||
|     static QString parseGatewayName(QXmlStreamReader &xmlReader); | ||||
|     static void parseGateway(QXmlStreamReader &reader, GPGateway &gateway); | ||||
|     static void parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway); | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif // PORTALCONFIGRESPONSE_H | ||||
|   | ||||
| @@ -23,7 +23,7 @@ PreloginResponse::PreloginResponse() | ||||
|  | ||||
| PreloginResponse PreloginResponse::parse(const QByteArray& xml) | ||||
| { | ||||
|     PLOGI << "Start parsing the prelogin response..."; | ||||
|     LOGI << "Start parsing the prelogin response..."; | ||||
|  | ||||
|     QXmlStreamReader xmlReader(xml); | ||||
|     PreloginResponse response; | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| #include <QtWidgets/QVBoxLayout> | ||||
| #include <QtWebEngineWidgets/QWebEngineProfile> | ||||
| #include <QtWebEngineWidgets/QWebEngineView> | ||||
| #include <QWebEngineCookieStore> | ||||
| #include <plog/Log.h> | ||||
|  | ||||
| #include "INIReader.h" | ||||
| #include "samlloginwindow.h" | ||||
|  | ||||
| SAMLLoginWindow::SAMLLoginWindow(QWidget *parent) | ||||
| SAMLLoginWindow::SAMLLoginWindow(QString portal, QWidget *parent) | ||||
|     : QDialog(parent) | ||||
|     , webView(new EnhancedWebView(this)) | ||||
| { | ||||
| @@ -15,17 +17,24 @@ SAMLLoginWindow::SAMLLoginWindow(QWidget *parent) | ||||
|  | ||||
|     QVBoxLayout *verticalLayout = new QVBoxLayout(this); | ||||
|     webView->setUrl(QUrl("about:blank")); | ||||
|     // webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); | ||||
|     webView->setAttribute(Qt::WA_DeleteOnClose); | ||||
|     verticalLayout->addWidget(webView); | ||||
|  | ||||
|     webView->initialize(); | ||||
|     connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived); | ||||
|     connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished); | ||||
| } | ||||
|  | ||||
| SAMLLoginWindow::~SAMLLoginWindow() | ||||
| { | ||||
|     delete webView; | ||||
|     // Portal | ||||
|     this->portal = portal; | ||||
|  | ||||
|     // Show the login window automatically when exceeds the MAX_WAIT_TIME | ||||
|     QTimer::singleShot(MAX_WAIT_TIME, this, [this]() { | ||||
|         if (failed) { | ||||
|             return; | ||||
|         } | ||||
|         LOGI << "MAX_WAIT_TIME exceeded, display the login window."; | ||||
|         this->show(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::closeEvent(QCloseEvent *event) | ||||
| @@ -34,47 +43,55 @@ void SAMLLoginWindow::closeEvent(QCloseEvent *event) | ||||
|     reject(); | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloingUrl) | ||||
| void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloginUrl) | ||||
| { | ||||
|     webView->page()->profile()->cookieStore()->deleteSessionCookies(); | ||||
|  | ||||
|     if (samlMethod == "POST") { | ||||
|         webView->setHtml(samlRequest, preloingUrl); | ||||
|         webView->setHtml(samlRequest, preloginUrl); | ||||
|     } else if (samlMethod == "REDIRECT") { | ||||
|         LOGI << "Redirect to " << samlRequest; | ||||
|         webView->load(samlRequest); | ||||
|     } else { | ||||
|         PLOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; | ||||
|         emit fail("Unknown saml-auth-method, got " + samlMethod); | ||||
|         LOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; | ||||
|         failed = true; | ||||
|         emit fail("ERR001", "Unknown saml-auth-method, got " + samlMethod); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::onResponseReceived(QJsonObject params) | ||||
| { | ||||
|     QString type = params.value("type").toString(); | ||||
|     const auto type = params.value("type").toString(); | ||||
|     // Skip non-document response | ||||
|     if (type != "Document") { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     QJsonObject response = params.value("response").toObject(); | ||||
|     QJsonObject headers = response.value("headers").toObject(); | ||||
|     auto response = params.value("response").toObject(); | ||||
|     auto headers = response.value("headers").toObject(); | ||||
|  | ||||
|     const QString username = headers.value("saml-username").toString(); | ||||
|     const QString preloginCookie = headers.value("prelogin-cookie").toString(); | ||||
|     const QString userAuthCookie = headers.value("portal-userauthcookie").toString(); | ||||
|     LOGI << "Trying to receive authentication cookie from " << response.value("url").toString(); | ||||
|  | ||||
|     LOGI << "Response received from " << response.value("url").toString(); | ||||
|     const auto username = headers.value("saml-username").toString(); | ||||
|     const auto preloginCookie = headers.value("prelogin-cookie").toString(); | ||||
|     const auto userAuthCookie = headers.value("portal-userauthcookie").toString(); | ||||
|  | ||||
|     this->checkSamlResult(username, preloginCookie, userAuthCookie); | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie) | ||||
| { | ||||
|     LOGI << "Checking the authentication result..."; | ||||
|  | ||||
|     if (!username.isEmpty()) { | ||||
|         LOGI << "Got username from SAML response headers " << username; | ||||
|         samlResult.insert("username", username); | ||||
|     } | ||||
|  | ||||
|     if (!preloginCookie.isEmpty()) { | ||||
|         LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie; | ||||
|         samlResult.insert("preloginCookie", preloginCookie); | ||||
|     } | ||||
|  | ||||
|     if (!userAuthCookie.isEmpty()) { | ||||
|         LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie; | ||||
|         samlResult.insert("userAuthCookie", userAuthCookie); | ||||
|     } | ||||
|  | ||||
| @@ -88,12 +105,61 @@ void SAMLLoginWindow::onResponseReceived(QJsonObject params) | ||||
|  | ||||
|         emit success(samlResult); | ||||
|         accept(); | ||||
|     } else { | ||||
|         this->show(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::onLoadFinished() | ||||
| { | ||||
|      LOGI << "Load finished " << this->webView->page()->url().toString(); | ||||
|      LOGI << "Load finished " << webView->page()->url().toString(); | ||||
|      webView->page()->toHtml([this] (const QString &html) { this->handleHtml(html); }); | ||||
|      QMap<QString, QString> credentials = this->loadCredentials(); | ||||
|      webView->page()->runJavaScript("document.getElementById('username').value='" + credentials["username"] + "';"); | ||||
|      webView->page()->runJavaScript("document.getElementById('password').value='" + credentials["password"] + "';"); | ||||
| } | ||||
|  | ||||
| void SAMLLoginWindow::handleHtml(const QString &html) | ||||
| { | ||||
|     // try to check the html body and extract from there | ||||
|     const auto samlAuthStatus = parseTag("saml-auth-status", html); | ||||
|  | ||||
|     if (samlAuthStatus == "1") { | ||||
|         const auto preloginCookie = parseTag("prelogin-cookie", html); | ||||
|         const auto username = parseTag("saml-username", html); | ||||
|         const auto userAuthCookie = parseTag("portal-userauthcookie", html); | ||||
|  | ||||
|         checkSamlResult(username, preloginCookie, userAuthCookie); | ||||
|     } else if (samlAuthStatus == "-1") { | ||||
|         LOGI << "SAML authentication failed..."; | ||||
|         failed = true; | ||||
|         emit fail("ERR002", "Authentication failed, please try again."); | ||||
|     } else { | ||||
|         show(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| QString SAMLLoginWindow::parseTag(const QString &tag, const QString &html) { | ||||
|     const QRegularExpression expression(QString("<%1>(.*)</%1>").arg(tag)); | ||||
|     return expression.match(html).captured(1); | ||||
| } | ||||
|  | ||||
| QMap<QString, QString> SAMLLoginWindow::loadCredentials() | ||||
| { | ||||
|     std::string home = getenv("HOME"); | ||||
|     std::string iniFile = home + "/.gpclient-credentials"; | ||||
|     INIReader reader(iniFile); | ||||
|  | ||||
|     QMap<QString, QString> credentials; | ||||
|     if (reader.ParseError() < 0) { | ||||
|         LOGE << "File '" << iniFile << "' not found."; | ||||
|         return credentials; | ||||
|     } | ||||
|  | ||||
|     if (reader.HasSection(this->portal.toStdString())) { | ||||
|         credentials.insert(QString("username"), QString::fromStdString(reader.Get(this->portal.toStdString(), "username", ""))); | ||||
|         credentials.insert(QString("password"), QString::fromStdString(reader.Get(this->portal.toStdString(), "password", ""))); | ||||
|     } else { | ||||
|         LOGE << "No credentials found for '" << this->portal.toStdString() << "' in '" << iniFile << "'"; | ||||
|     } | ||||
|  | ||||
|     return credentials; | ||||
| } | ||||
| @@ -12,24 +12,32 @@ class SAMLLoginWindow : public QDialog | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit SAMLLoginWindow(QWidget *parent = nullptr); | ||||
|     ~SAMLLoginWindow(); | ||||
|     explicit SAMLLoginWindow(QString portal, QWidget *parent = nullptr); | ||||
|  | ||||
|     void login(const QString samlMethod, const QString samlRequest, const QString preloingUrl); | ||||
|     void login(const QString samlMethod, const QString samlRequest, const QString preloginUrl); | ||||
|     QMap<QString, QString> loadCredentials(); | ||||
|  | ||||
| signals: | ||||
|     void success(QMap<QString, QString> samlResult); | ||||
|     void fail(const QString msg); | ||||
|     void fail(const QString code, const QString msg); | ||||
|  | ||||
| private slots: | ||||
|     void onResponseReceived(QJsonObject params); | ||||
|     void onLoadFinished(); | ||||
|     void checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie); | ||||
|  | ||||
| private: | ||||
|     EnhancedWebView *webView; | ||||
|     static const auto MAX_WAIT_TIME { 10 * 1000 }; | ||||
|  | ||||
|     bool failed { false }; | ||||
|     EnhancedWebView *webView { nullptr }; | ||||
|     QMap<QString, QString> samlResult; | ||||
|     QString portal; | ||||
|  | ||||
|     void closeEvent(QCloseEvent *event); | ||||
|     void handleHtml(const QString &html); | ||||
|  | ||||
|     static QString parseTag(const QString &tag, const QString &html); | ||||
| }; | ||||
|  | ||||
| #endif // SAMLLOGINWINDOW_H | ||||
|   | ||||
| @@ -32,3 +32,11 @@ QString SettingsDialog::clientos() | ||||
| { | ||||
|     return ui->clientosInput->text(); | ||||
| } | ||||
|  | ||||
| void SettingsDialog::setOsVersion(QString osVersion) { | ||||
|     ui->osVersionInput->setText(osVersion); | ||||
| } | ||||
|  | ||||
| QString SettingsDialog::osVersion() { | ||||
|     return ui->osVersionInput->text(); | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,9 @@ public: | ||||
|     void setClientos(QString clientos); | ||||
|     QString clientos(); | ||||
|  | ||||
|     void setOsVersion(QString osVersion); | ||||
|     QString osVersion(); | ||||
|  | ||||
| private: | ||||
|     Ui::SettingsDialog *ui; | ||||
| }; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>488</width> | ||||
|     <height>177</height> | ||||
|     <height>220</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
| @@ -33,15 +33,18 @@ | ||||
|    </item> | ||||
|    <item row="0" column="1"> | ||||
|     <widget class="QPlainTextEdit" name="extraArgsInput"> | ||||
|      <property name="readOnly"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|      <property name="placeholderText"> | ||||
|       <string extracomment="Tokens with spaces can be surrounded by double quotes">e.g. --name=value --script="vpn-slice xxx"</string> | ||||
|       <string>The configuration has been moved to "/etc/gpservice/gp.conf"</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="1" column="0"> | ||||
|     <widget class="QLabel" name="label_2"> | ||||
|      <property name="text"> | ||||
|       <string>Value of "clientos":</string> | ||||
|       <string>clientos:</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
| @@ -52,7 +55,7 @@ | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="2" column="1"> | ||||
|    <item row="3" column="1"> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
| @@ -62,6 +65,16 @@ | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="2" column="1"> | ||||
|     <widget class="QLineEdit" name="osVersionInput"/> | ||||
|    </item> | ||||
|    <item row="2" column="0"> | ||||
|     <widget class="QLabel" name="label_3"> | ||||
|      <property name="text"> | ||||
|       <string>os-version:</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources> | ||||
|   | ||||
							
								
								
									
										60
									
								
								GPClient/standardloginwindow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								GPClient/standardloginwindow.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| #include <QtGui/QCloseEvent> | ||||
|  | ||||
| #include "standardloginwindow.h" | ||||
| #include "ui_standardloginwindow.h" | ||||
| #include "gphelper.h" | ||||
|  | ||||
| using namespace gpclient::helper; | ||||
|  | ||||
| StandardLoginWindow::StandardLoginWindow(const QString &portalAddress, const QString &labelUsername, | ||||
|                                          const QString &labelPassword, const QString &authMessage) : | ||||
|         QDialog(nullptr), | ||||
|         ui(new Ui::StandardLoginWindow) { | ||||
|     ui->setupUi(this); | ||||
|     ui->portalAddress->setText(portalAddress); | ||||
|     ui->username->setPlaceholderText(labelUsername); | ||||
|     ui->password->setPlaceholderText(labelPassword); | ||||
|     ui->authMessage->setText(authMessage); | ||||
|  | ||||
|     autocomplete(); | ||||
|  | ||||
|     setWindowTitle("GlobalProtect Login"); | ||||
|     setFixedSize(width(), height()); | ||||
|     setModal(true); | ||||
| } | ||||
|  | ||||
| void StandardLoginWindow::autocomplete() { | ||||
|     QString username, password; | ||||
|     settings::secureGet("username", username); | ||||
|     settings::secureGet("password", password); | ||||
|  | ||||
|     if (!username.isEmpty() && !password.isEmpty()) { | ||||
|         ui->username->setText(username); | ||||
|         ui->password->setText(password); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void StandardLoginWindow::setProcessing(bool isProcessing) { | ||||
|     ui->username->setReadOnly(isProcessing); | ||||
|     ui->password->setReadOnly(isProcessing); | ||||
|     ui->loginButton->setDisabled(isProcessing); | ||||
| } | ||||
|  | ||||
| void StandardLoginWindow::on_loginButton_clicked() { | ||||
|     const QString username = ui->username->text().trimmed(); | ||||
|     const QString password = ui->password->text().trimmed(); | ||||
|  | ||||
|     if (username.isEmpty() || password.isEmpty()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     settings::secureSave("username", username); | ||||
|     settings::secureSave("password", password); | ||||
|  | ||||
|     emit performLogin(username, password); | ||||
| } | ||||
|  | ||||
| void StandardLoginWindow::closeEvent(QCloseEvent *event) { | ||||
|     event->accept(); | ||||
|     reject(); | ||||
| } | ||||
							
								
								
									
										34
									
								
								GPClient/standardloginwindow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								GPClient/standardloginwindow.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #ifndef STANDARDLOGINWINDOW_H | ||||
| #define STANDARDLOGINWINDOW_H | ||||
|  | ||||
| #include <QtWidgets/QDialog> | ||||
|  | ||||
| namespace Ui { | ||||
|     class StandardLoginWindow; | ||||
| } | ||||
|  | ||||
| class StandardLoginWindow : public QDialog { | ||||
| Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit StandardLoginWindow(const QString &portalAddress, const QString &labelUsername, | ||||
|                                  const QString &labelPassword, const QString &authMessage); | ||||
|  | ||||
|     void setProcessing(bool isProcessing); | ||||
|  | ||||
| private slots: | ||||
|  | ||||
|     void on_loginButton_clicked(); | ||||
|  | ||||
| signals: | ||||
|  | ||||
|     void performLogin(QString username, QString password); | ||||
|  | ||||
| private: | ||||
|     Ui::StandardLoginWindow *ui; | ||||
|  | ||||
|     void closeEvent(QCloseEvent *event); | ||||
|     void autocomplete(); | ||||
| }; | ||||
|  | ||||
| #endif // STANDARDLOGINWINDOW_H | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>NormalLoginWindow</class> | ||||
|  <widget class="QDialog" name="NormalLoginWindow"> | ||||
|  <class>StandardLoginWindow</class> | ||||
|  <widget class="QDialog" name="StandardLoginWindow"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
| @@ -8,7 +8,7 @@ class IVpn | ||||
| public: | ||||
|     virtual ~IVpn() = default; | ||||
|  | ||||
|     virtual void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd, const QString &extraArgs) = 0; | ||||
|     virtual void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) = 0; | ||||
|     virtual void disconnect() = 0; | ||||
|     virtual int status() = 0; | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #include "vpn_dbus.h" | ||||
|  | ||||
| void VpnDbus::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd, const QString &extraArgs) { | ||||
|     inner->connect(preferredServer, username, passwd, extraArgs); | ||||
| void VpnDbus::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) { | ||||
|     inner->connect(preferredServer, username, passwd); | ||||
| } | ||||
|  | ||||
| void VpnDbus::disconnect() { | ||||
|   | ||||
| @@ -20,7 +20,7 @@ public: | ||||
|     QObject::connect(inner, &com::yuezk::qt::GPService::logAvailable, this, &VpnDbus::logAvailable); | ||||
|   } | ||||
|  | ||||
|   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd, const QString &extraArgs); | ||||
|   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd); | ||||
|   void disconnect(); | ||||
|   int status(); | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include <QJsonObject> | ||||
| #include <QJsonArray> | ||||
|  | ||||
| void VpnJson::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd, const QString &extraArgs) { | ||||
| void VpnJson::connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd) { | ||||
|     QJsonArray sl; | ||||
|     for (const QString &srv : servers) { | ||||
|       sl.push_back(QJsonValue(srv)); | ||||
|   | ||||
| @@ -10,7 +10,7 @@ class VpnJson : public QObject, public IVpn | ||||
| public: | ||||
|   VpnJson(QObject *parent) : QObject(parent) {} | ||||
|  | ||||
|   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd, const QString &extraArgs); | ||||
|   void connect(const QString &preferredServer, const QList<QString> &servers, const QString &username, const QString &passwd); | ||||
|   void disconnect(); | ||||
|   int status(); | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,12 @@ project(GPService) | ||||
|  | ||||
| set(gpservice_GENERATED_SOURCES) | ||||
|  | ||||
| execute_process(COMMAND logname OUTPUT_VARIABLE CMAKE_LOGNAME) | ||||
| string(STRIP "${CMAKE_LOGNAME}" CMAKE_LOGNAME) | ||||
|  | ||||
| message(STATUS "CMAKE_LOGNAME: ${CMAKE_LOGNAME}") | ||||
|  | ||||
| configure_file(dbus/com.yuezk.qt.GPService.conf.in dbus/com.yuezk.qt.GPService.conf) | ||||
| configure_file(dbus/com.yuezk.qt.GPService.service.in dbus/com.yuezk.qt.GPService.service) | ||||
| configure_file(systemd/gpservice.service.in systemd/gpservice.service) | ||||
|  | ||||
| @@ -22,6 +28,7 @@ qt5_add_dbus_adaptor( | ||||
| ) | ||||
|  | ||||
| add_executable(gpservice | ||||
|     gpservice.h | ||||
|     gpservice.cpp | ||||
|     main.cpp | ||||
|     ${gpservice_GENERATED_SOURCES} | ||||
| @@ -58,13 +65,15 @@ target_link_libraries(gpservice | ||||
|     Qt5::Network | ||||
|     Qt5::DBus | ||||
|     QtSignals | ||||
|     inih | ||||
| ) | ||||
|  | ||||
| target_compile_definitions(gpservice PUBLIC QAPPLICATION_CLASS=QCoreApplication) | ||||
|  | ||||
| install(TARGETS gpservice DESTINATION bin) | ||||
| install(FILES "dbus/com.yuezk.qt.GPService.conf" DESTINATION share/dbus-1/system.d ) | ||||
| install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.conf" DESTINATION share/dbus-1/system.d ) | ||||
| install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.service" DESTINATION share/dbus-1/system-services) | ||||
| install(FILES "gp.conf" DESTINATION /etc/gpservice) | ||||
|  | ||||
| if("$ENV{DEBIAN_PACKAGE}") | ||||
|     # Install the systemd unit files to /lib/systemd/system for debian package | ||||
|   | ||||
							
								
								
									
										17
									
								
								GPService/gp.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								GPService/gp.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| # Configuration file for GlobalProtect-openconnect | ||||
| # | ||||
| # Description: | ||||
| # | ||||
| # Each section is a VPN gateway address, and [*] is a special section that defines the default configuration. | ||||
| # See https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration for more details. | ||||
| # | ||||
| # Example: | ||||
| # | ||||
| # [*] | ||||
| # openconnect-args=<value> | ||||
| # | ||||
| # [vpn1.company.com] | ||||
| # openconnect-args=--script=/path/to/vpnc-script | ||||
|  | ||||
| [*] | ||||
| openconnect-args= | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include <QtCore/QRegularExpressionMatch> | ||||
| #include <QtDBus/QtDBus> | ||||
|  | ||||
| #include "INIReader.h" | ||||
| #include "gpservice.h" | ||||
| #include "gpserviceadaptor.h" | ||||
|  | ||||
| @@ -33,16 +34,30 @@ GPService::~GPService() | ||||
|  | ||||
| QString GPService::findBinary() | ||||
| { | ||||
|     for (int i = 0; i < binaryPaths->length(); i++) { | ||||
|         if (QFileInfo::exists(binaryPaths[i])) { | ||||
|             return binaryPaths[i]; | ||||
|     for (auto& binaryPath : binaryPaths) { | ||||
|         if (QFileInfo::exists(binaryPath)) { | ||||
|             return binaryPath; | ||||
|         } | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| QString GPService::extraOpenconnectArgs(const QString &gateway) | ||||
| { | ||||
|     INIReader reader("/etc/gpservice/gp.conf"); | ||||
|  | ||||
|     if (reader.ParseError() < 0) { | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|     std::string defaultArgs = reader.Get("*", "openconnect-args", ""); | ||||
|     std::string extraArgs = reader.Get(gateway.toStdString(), "openconnect-args", defaultArgs); | ||||
|  | ||||
|     return QString::fromStdString(extraArgs); | ||||
| } | ||||
|  | ||||
| /* Port from https://github.com/qt/qtbase/blob/11d1dcc6e263c5059f34b44d531c9ccdf7c0b1d6/src/corelib/io/qprocess.cpp#L2115 */ | ||||
| QStringList GPService::splitCommand(QString command) | ||||
| QStringList GPService::splitCommand(const QString &command) | ||||
| { | ||||
|     QStringList args; | ||||
|     QString tmp; | ||||
| @@ -92,7 +107,7 @@ void GPService::quit() | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GPService::connect(QString server, QString username, QString passwd, QString extraArgs) | ||||
| void GPService::connect(QString server, QString username, QString passwd) | ||||
| { | ||||
|     if (vpnStatus != GPService::VpnNotConnected) { | ||||
|         log("VPN status is: " + QVariant::fromValue(vpnStatus).toString()); | ||||
| @@ -110,6 +125,9 @@ void GPService::connect(QString server, QString username, QString passwd, QStrin | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const QString extraArgs = extraOpenconnectArgs(server); | ||||
|     log(QString("Got extra OpenConnect args for server: %1, %2").arg(server, extraArgs.isEmpty() ? "<empty>" : extraArgs)); | ||||
|  | ||||
|     QStringList args; | ||||
|     args << QCoreApplication::arguments().mid(1) | ||||
|          << "--protocol=gp" | ||||
| @@ -118,7 +136,7 @@ void GPService::connect(QString server, QString username, QString passwd, QStrin | ||||
|          << "--cookie-on-stdin" | ||||
|          << server; | ||||
|  | ||||
|     log("Start process with arugments: " + args.join(" ")); | ||||
|     log("Start process with arguments: " + args.join(", ")); | ||||
|  | ||||
|     openconnect->start(bin, args); | ||||
|     openconnect->write((passwd + "\n").toUtf8()); | ||||
| @@ -181,7 +199,9 @@ void GPService::onProcessStdout() | ||||
|     QString output = openconnect->readAllStandardOutput(); | ||||
|  | ||||
|     log(output); | ||||
|     if (output.indexOf("Connected as") >= 0 || output.indexOf("Configured as") >= 0) { | ||||
|     if (output.indexOf("Connected as") >= 0 || | ||||
|         output.indexOf("Configured as") >= 0 || | ||||
|         output.indexOf("Configurado como") >= 0) { | ||||
|         vpnStatus = GPService::VpnConnected; | ||||
|         emit connected(); | ||||
|     } | ||||
|   | ||||
| @@ -4,14 +4,13 @@ | ||||
| #include <QtCore/QObject> | ||||
| #include <QtCore/QProcess> | ||||
|  | ||||
| static const QString binaryPaths[] { | ||||
|     "/usr/local/bin/openconnect", | ||||
|     "/usr/local/sbin/openconnect", | ||||
|     "/usr/bin/openconnect", | ||||
|     "/usr/sbin/openconnect", | ||||
|     "/opt/bin/openconnect", | ||||
|     "/opt/sbin/openconnect" | ||||
| }; | ||||
| static QList<QString> binaryPaths = QList<QString>() << | ||||
|     "/usr/local/bin/openconnect" << | ||||
|      "/usr/local/sbin/openconnect" << | ||||
|      "/usr/bin/openconnect" << | ||||
|      "/usr/sbin/openconnect" << | ||||
|      "/opt/bin/openconnect" << | ||||
|      "/opt/sbin/openconnect"; | ||||
|  | ||||
| class GPService : public QObject | ||||
| { | ||||
| @@ -37,7 +36,7 @@ signals: | ||||
|     void logAvailable(QString log); | ||||
|  | ||||
| public slots: | ||||
|     void connect(QString server, QString username, QString passwd, QString extraArgs); | ||||
|     void connect(QString server, QString username, QString passwd); | ||||
|     void disconnect(); | ||||
|     int status(); | ||||
|  | ||||
| @@ -56,7 +55,8 @@ private: | ||||
|     void log(QString msg); | ||||
|     bool isValidVersion(QString &bin); | ||||
|     static QString findBinary(); | ||||
|     static QStringList splitCommand(QString command); | ||||
|     static QString extraOpenconnectArgs(const QString &gateway); | ||||
|     static QStringList splitCommand(const QString &command); | ||||
| }; | ||||
|  | ||||
| #endif // GLOBALPROTECTSERVICE_H | ||||
|   | ||||
							
								
								
									
										35
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								README.md
									
									
									
									
									
								
							| @@ -42,7 +42,7 @@ Add the repository in the above table and install it with your favorite package | ||||
| ```sh | ||||
| sudo add-apt-repository ppa:yuezk/globalprotect-openconnect | ||||
| sudo apt-get update | ||||
| sudo apt install globalprotect-openconnect | ||||
| sudo apt-get install globalprotect-openconnect | ||||
| ``` | ||||
|  | ||||
| > For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`. | ||||
| @@ -78,7 +78,8 @@ sudo dnf install globalprotect-openconnect | ||||
| - openSUSE Leap | ||||
|  | ||||
|   ```sh   | ||||
|   sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/openSUSE_Leap_15.2/home:yuezk.repo | ||||
|   sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/15.4/home:yuezk.repo | ||||
|    | ||||
|   sudo zypper ref | ||||
|   sudo zypper install globalprotect-openconnect | ||||
|   ``` | ||||
| @@ -97,6 +98,14 @@ git clone https://github.com/yuezk/GlobalProtect-openconnect.git | ||||
| cd GlobalProtect-openconnect | ||||
| ``` | ||||
|  | ||||
| ### MX Linux | ||||
| The following instructions are for **MX-21.2.1_x64 KDE**. | ||||
|  | ||||
| ```sh | ||||
| sudo apt install qttools5-dev libsecret-1-dev libqt5keychain1 | ||||
| ./scripts/install-debian.sh | ||||
| ``` | ||||
|  | ||||
| ### Ubuntu/Mint | ||||
|  | ||||
| > **⚠️ REQUIRED for Ubuntu 18.04 ⚠️** | ||||
| @@ -105,7 +114,7 @@ cd GlobalProtect-openconnect | ||||
| > | ||||
| > ```sh | ||||
| > sudo add-apt-repository ppa:dwmw2/openconnect | ||||
| > sudo apt update | ||||
| > sudo apt-get update | ||||
| > ``` | ||||
|  | ||||
| Build and install with: | ||||
| @@ -138,6 +147,7 @@ Install the Qt5 dependencies and OpenConnect: | ||||
| - QtWebSockets | ||||
| - QtDBus | ||||
| - openconnect v8.x | ||||
| - qtkeychain | ||||
|  | ||||
| ...then build and install with: | ||||
|  | ||||
| @@ -165,13 +175,7 @@ Once the software is installed, you can run `gpclient` to start the UI. | ||||
|  | ||||
| ## Passing the Custom Parameters to `OpenConnect` CLI | ||||
|  | ||||
| Custom parameters can be appended to the `OpenConnect` CLI with the following settings. | ||||
|  | ||||
| > Tokens with spaces can be surrounded by double quotes; three consecutive double quotes represent the quote character itself. | ||||
|  | ||||
| <p align="center"> | ||||
|   <img src="https://user-images.githubusercontent.com/3297602/130319209-744be02b-d657-4f49-a76d-d2c81b5c46d5.png" /> | ||||
| <p> | ||||
| See [Configuration](https://github.com/yuezk/GlobalProtect-openconnect/wiki/Configuration) | ||||
|  | ||||
| ## Display the system tray icon on Gnome 40 | ||||
|  | ||||
| @@ -182,18 +186,9 @@ Install the [AppIndicator and KStatusNotifierItem Support](https://extensions.gn | ||||
| <p> | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Future plan | ||||
|  | ||||
| - [x] Improve the release process | ||||
| - [ ] Process bugs and feature requests | ||||
| - [ ] Support for bypassing the `gpclient` parameters | ||||
| - [ ] Support the CLI mode | ||||
|    | ||||
|    | ||||
| ## Troubleshooting | ||||
|  | ||||
| The application logs can be found at: `~/.cache/GlobalProtect-openconnect/gpclient.log` | ||||
| Run `gpclient` in the Terminal and collect the logs. | ||||
|  | ||||
| ## [License](./LICENSE) | ||||
| GPLv3 | ||||
|   | ||||
							
								
								
									
										59
									
								
								cmake/FindNetworkManager.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								cmake/FindNetworkManager.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| # - Try to find NetworkManager | ||||
| # Once done this will define | ||||
| # | ||||
| #  NETWORKMANAGER_FOUND - system has NetworkManager | ||||
| #  NETWORKMANAGER_INCLUDE_DIRS - the NetworkManager include directories | ||||
| #  NETWORKMANAGER_LIBRARIES - the libraries needed to use NetworkManager | ||||
| #  NETWORKMANAGER_CFLAGS - Compiler switches required for using NetworkManager | ||||
| #  NETWORKMANAGER_VERSION - version number of NetworkManager | ||||
|  | ||||
| # Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org> | ||||
| # Copyright (c) 2007, Will Stephenson, <wstephenson@kde.org> | ||||
| # Copyright (c) 2015-2018, Jan Grulich, <jgrulich@redhat.com> | ||||
|  | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions | ||||
| # are met: | ||||
| # 1. Redistributions of source code must retain the above copyright | ||||
| #    notice, this list of conditions and the following disclaimer. | ||||
| # 2. Redistributions in binary form must reproduce the above copyright | ||||
| #    notice, this list of conditions and the following disclaimer in the | ||||
| #    documentation and/or other materials provided with the distribution. | ||||
| # 3. Neither the name of the University nor the names of its contributors | ||||
| #    may be used to endorse or promote products derived from this software | ||||
| #    without specific prior written permission. | ||||
| # | ||||
| # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||
| # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||
| # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
| # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
| # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
| # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
| # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
| # SUCH DAMAGE. | ||||
|  | ||||
| IF (NETWORKMANAGER_INCLUDE_DIRS) | ||||
|     # in cache already | ||||
|     SET(NetworkManager_FIND_QUIETLY TRUE) | ||||
| ENDIF (NETWORKMANAGER_INCLUDE_DIRS) | ||||
|  | ||||
| IF (NOT WIN32) | ||||
|     find_package(PkgConfig) | ||||
|     PKG_SEARCH_MODULE(NETWORKMANAGER libnm) | ||||
|     IF (NETWORKMANAGER_FOUND) | ||||
|         IF (NetworkManager_FIND_VERSION AND ("${NETWORKMANAGER_VERSION}" VERSION_LESS "${NetworkManager_FIND_VERSION}")) | ||||
|             MESSAGE(FATAL_ERROR "NetworkManager ${NETWORKMANAGER_VERSION} is too old, need at least ${NetworkManager_FIND_VERSION}") | ||||
|         ELSE () | ||||
|             IF (NOT NetworkManager_FIND_QUIETLY) | ||||
|                 MESSAGE(STATUS "Found NetworkManager: ${NETWORKMANAGER_LIBRARY_DIRS}") | ||||
|             ENDIF () | ||||
|         ENDIF () | ||||
|     ELSE () | ||||
|         MESSAGE(FATAL_ERROR "Could NOT find NetworkManager, check FindPkgConfig output above!") | ||||
|     ENDIF () | ||||
| ENDIF (NOT WIN32) | ||||
|  | ||||
| MARK_AS_ADVANCED(NETWORKMANAGER_INCLUDE_DIRS) | ||||
							
								
								
									
										2
									
								
								cmakew
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								cmakew
									
									
									
									
									
								
							| @@ -36,7 +36,7 @@ fi | ||||
| cmake_base="./.cmake" | ||||
| cmake_bin="${cmake_base}/cmake-$cmake_version/bin/cmake" | ||||
|  | ||||
| # download cmake if neccessary | ||||
| # download cmake if necessary | ||||
| if [ ! -f "$cmake_bin" ]; then | ||||
|     download_link="" | ||||
|  | ||||
|   | ||||
							
								
								
									
										110
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										110
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,113 @@ | ||||
| globalprotect-openconnect (1.4.9-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.8 –> 1.4.9 | ||||
|   * fix: update cmake version | ||||
|   * fix: correct the package name | ||||
|   * fix: use the dev package | ||||
|   * fix: use qtkeychain package | ||||
|   * fix: add qt5-tools | ||||
|   * fix: add libsecret-1-dev | ||||
|   * fix: add pkg-config | ||||
|   * fix: use cmake 3.16 | ||||
|   * fix: add missing build dependency | ||||
|   * ci: fix CI | ||||
|   * Merge branch 'master' into develop | ||||
|   * feat: expose os-version to settings | ||||
|   * Add two missing dependencies for building on debian (#198) | ||||
|   * ci: assert no library missing | ||||
|   * fix: update qtkeychain | ||||
|   * ci: run gpclient after build | ||||
|   * fix: add qtkeychain | ||||
|   * chore: update CMake file | ||||
|   * Added install instructions for MX Linux. (#190) | ||||
|   * Credentials autocompleting (secure version) (#179) | ||||
|   * Read all saved Gateways (for selecting in Systray) (#181) | ||||
|   * copy install script for debian (#180) | ||||
|   * add es and pt support to shange status when connected to vpn (#162) | ||||
|   * fix: improve the cli support | ||||
|   * feat: add --reset option to gpclient | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 08 Jan 2023 20:58:32 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.8-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.7 –> 1.4.8 | ||||
|   * fix: fix compile error | ||||
|   * refactor: simplify the code | ||||
|   * chore: use auto to declare variables | ||||
|   * chore: use c++ 17 | ||||
|   * fix: clear cookies when click the Reset button | ||||
|   * fix: refine the authentication workflow | ||||
|   * chore: PLOG -> LOG | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 12 Jun 2022 20:28:58 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.7-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.6 –> 1.4.7 | ||||
|   * fix: release resources when properly | ||||
|   * fix: add support for parsing tokens from HTML | ||||
|   * handle html comment for saml result with okta 2fa (#156) | ||||
|   * chore: use auto to declare variable | ||||
|   * chore: simplify readme | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Tue, 07 Jun 2022 21:46:04 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.6-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.5 –> 1.4.6 | ||||
|   * feat: display address in gateway menu item | ||||
|   * fix: fix bug of parsing the portal response | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Wed, 01 Jun 2022 23:55:50 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.5-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.4 –> 1.4.5 | ||||
|   * chore: refine vscode settings | ||||
|   * fix: rollback dbus configuration | ||||
|   * feat: add option to start minimized | ||||
|   * packaging: fix postinst for debian | ||||
|   * packaging: add postinst for debian | ||||
|   * test: test debian packaging | ||||
|   * ci: fix the folder path | ||||
|   * chore: apt -> apt-get | ||||
|   * ci: verify debian package | ||||
|   * Revert "Revert "fix: improve the dbus security"" | ||||
|   * fix: improve the portal config parsing | ||||
|   * Revert "fix: improve the dbus security" | ||||
|   * fix: improve the dbus security | ||||
|   * fix: free resources in slots | ||||
|   * chore: refine cmake files | ||||
|   * fix: support high DPI screen | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Sun, 29 May 2022 21:15:40 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.4-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.3 –> 1.4.4 | ||||
|   * fix: support the HighDPI displays | ||||
|   * [misc] update the build script | ||||
|   * [ci] Enable build job for master branch | ||||
|   * [ci] Add ubuntu 22.04 | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Sat, 14 May 2022 19:21:14 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.3-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.2 –> 1.4.3 | ||||
|   * refine AUR packaging | ||||
|   * Prepare release 1.4.3 (#149) | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Mon, 09 May 2022 22:20:54 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.2-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.1 –> 1.4.2 | ||||
|   * Clear SSL_OP_LEGACY_SERVER_CONNECT (#146) | ||||
|  | ||||
|  -- Kevin Yue <k3vinyue@gmail.com>  Fri, 06 May 2022 22:18:19 +0800 | ||||
|  | ||||
| globalprotect-openconnect (1.4.1-1) unstable; urgency=medium | ||||
|  | ||||
|   * Updated VERSION, Bumped 1.4.0 –> 1.4.1 | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -2,12 +2,12 @@ Source: globalprotect-openconnect | ||||
| Section: net | ||||
| Priority: optional | ||||
| Maintainer: Kevin Yue <k3vinyue@gmail.com> | ||||
| Build-Depends: cmake (>=3.10), debhelper (>=11~), qtbase5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9) | ||||
| Build-Depends: cmake (>=3.10), pkg-config, debhelper (>=11~), qtbase5-dev, qttools5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9), qt5keychain-dev | ||||
| Standards-Version: 4.1.4 | ||||
| Homepage: https://github.com/yuezk/GlobalProtect-openconnect | ||||
|  | ||||
| Package: globalprotect-openconnect | ||||
| Architecture: any | ||||
| Multi-Arch: foreign | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9) | ||||
| Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9), libqt5keychain1 | ||||
| Description: A GlobalProtect VPN client (GUI) based on OpenConnect. | ||||
|   | ||||
| @@ -1,26 +1,33 @@ | ||||
| # Maintainer: Keinv Yue <yuezk001@gmail.com> | ||||
|  | ||||
| pkgname=globalprotect-openconnect | ||||
| pkgver=0 | ||||
| _pkgver="1.4.9" | ||||
| _commit="acf184134a2ff19e4a39528bd6a7fbbafa4cf017" | ||||
| pkgname=globalprotect-openconnect-git | ||||
| pkgver=${_pkgver} | ||||
| pkgrel=1 | ||||
| pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode." | ||||
| pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode. (development version)" | ||||
| arch=(x86_64 aarch64) | ||||
| url="https://github.com/yuezk/GlobalProtect-openconnect" | ||||
| license=('GPL3') | ||||
| depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets) | ||||
| makedepends=(cmake) | ||||
| provides=('gpclient' 'gpservice') | ||||
| backup=( | ||||
|     etc/gpservice/gp.conf | ||||
| ) | ||||
| install=gp.install | ||||
| depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5) | ||||
| makedepends=(git cmake) | ||||
| conflicts=('globalprotect-openconnect') | ||||
| provides=('globalprotect-openconnect' 'gpclient' 'gpservice') | ||||
|  | ||||
| source=("${pkgname}.tar.gz") | ||||
| source=(git+https://github.com/yuezk/GlobalProtect-openconnect#commit=${_commit}) | ||||
| sha256sums=('SKIP') | ||||
|  | ||||
| pkgver() { | ||||
|     cd $srcdir/$pkgname-*/ | ||||
|     cat VERSION VERSION_SUFFIX | ||||
| prepare() { | ||||
|     cd GlobalProtect-openconnect | ||||
|     echo "${_pkgver}" > VERSION | ||||
| } | ||||
|  | ||||
| build() { | ||||
|     cd $srcdir/$pkgname-*/ | ||||
|     cd GlobalProtect-openconnect | ||||
|     cmake -B build \ | ||||
|         -DCMAKE_BUILD_TYPE=Release \ | ||||
|         -DCMAKE_CXX_FLAGS_RELEASE=-s | ||||
| @@ -28,6 +35,6 @@ build() { | ||||
| } | ||||
|  | ||||
| package() { | ||||
|     cd $srcdir/$pkgname-*/ | ||||
|     cd GlobalProtect-openconnect | ||||
|     make DESTDIR="$pkgdir/" install -C build | ||||
| } | ||||
|   | ||||
| @@ -1,28 +1,33 @@ | ||||
| # Maintainer: Keinv Yue <yuezk001@gmail.com> | ||||
| 
 | ||||
| _pkgver="{VERSION}" | ||||
| _commit="{COMMIT}" | ||||
| pkgname=globalprotect-openconnect-git | ||||
| _pkgname=globalprotect-openconnect | ||||
| pkgver=0 | ||||
| pkgver=${_pkgver} | ||||
| pkgrel=1 | ||||
| pkgdesc="A GlobalProtect VPN client (GUI) for Linux based on Openconnect and built with Qt5, supports SAML auth mode. (development version)" | ||||
| arch=(x86_64 aarch64) | ||||
| url="https://github.com/yuezk/GlobalProtect-openconnect" | ||||
| license=('GPL3') | ||||
| depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets) | ||||
| makedepends=(cmake) | ||||
| backup=( | ||||
|     etc/gpservice/gp.conf | ||||
| ) | ||||
| install=gp.install | ||||
| depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5) | ||||
| makedepends=(git cmake) | ||||
| conflicts=('globalprotect-openconnect') | ||||
| provides=('gpclient' 'gpservice') | ||||
| provides=('globalprotect-openconnect' 'gpclient' 'gpservice') | ||||
| 
 | ||||
| source=("${_pkgname}.tar.gz") | ||||
| source=(git+https://github.com/yuezk/GlobalProtect-openconnect#commit=${_commit}) | ||||
| sha256sums=('SKIP') | ||||
| 
 | ||||
| pkgver() { | ||||
|     cd $srcdir/$_pkgname-*/ | ||||
|     cat VERSION VERSION_SUFFIX | ||||
| prepare() { | ||||
|     cd GlobalProtect-openconnect | ||||
|     echo "${_pkgver}" > VERSION | ||||
| } | ||||
| 
 | ||||
| build() { | ||||
|     cd $srcdir/${_pkgname}-*/ | ||||
|     cd GlobalProtect-openconnect | ||||
|     cmake -B build \ | ||||
|         -DCMAKE_BUILD_TYPE=Release \ | ||||
|         -DCMAKE_CXX_FLAGS_RELEASE=-s | ||||
| @@ -30,6 +35,6 @@ build() { | ||||
| } | ||||
| 
 | ||||
| package() { | ||||
|     cd $srcdir/${_pkgname}-*/ | ||||
|     cd GlobalProtect-openconnect | ||||
|     make DESTDIR="$pkgdir/" install -C build | ||||
| } | ||||
							
								
								
									
										8
									
								
								packaging/aur/gp.install
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								packaging/aur/gp.install
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| post_install() { | ||||
|     systemctl enable gpservice.service | ||||
|     systemctl restart gpservice.service | ||||
| } | ||||
|  | ||||
| post_upgrade() { | ||||
|     post_install | ||||
| } | ||||
| @@ -1,3 +1,113 @@ | ||||
| ------------------------------------------------------------------- | ||||
| Sun Jan  8 12:58:32 UTC 2023 - k3vinyue@gmail.com - 1.4.9 | ||||
|  | ||||
| - Update to 1.4.9 | ||||
|   * Updated VERSION, Bumped 1.4.8 –> 1.4.9 | ||||
|   * fix: update cmake version | ||||
|   * fix: correct the package name | ||||
|   * fix: use the dev package | ||||
|   * fix: use qtkeychain package | ||||
|   * fix: add qt5-tools | ||||
|   * fix: add libsecret-1-dev | ||||
|   * fix: add pkg-config | ||||
|   * fix: use cmake 3.16 | ||||
|   * fix: add missing build dependency | ||||
|   * ci: fix CI | ||||
|   * Merge branch 'master' into develop | ||||
|   * feat: expose os-version to settings | ||||
|   * Add two missing dependencies for building on debian (#198) | ||||
|   * ci: assert no library missing | ||||
|   * fix: update qtkeychain | ||||
|   * ci: run gpclient after build | ||||
|   * fix: add qtkeychain | ||||
|   * chore: update CMake file | ||||
|   * Added install instructions for MX Linux. (#190) | ||||
|   * Credentials autocompleting (secure version) (#179) | ||||
|   * Read all saved Gateways (for selecting in Systray) (#181) | ||||
|   * copy install script for debian (#180) | ||||
|   * add es and pt support to change status when connected to vpn (#162) | ||||
|   * fix: improve the cli support | ||||
|   * feat: add --reset option to gpclient | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Sun Jun 12 12:28:58 UTC 2022 - k3vinyue@gmail.com - 1.4.8 | ||||
|  | ||||
| - Update to 1.4.8 | ||||
|   * Updated VERSION, Bumped 1.4.7 –> 1.4.8 | ||||
|   * fix: fix compile error | ||||
|   * refactor: simplify the code | ||||
|   * chore: use auto to declare variables | ||||
|   * chore: use c++ 17 | ||||
|   * fix: clear cookies when click the Reset button | ||||
|   * fix: refine the authentication workflow | ||||
|   * chore: PLOG -> LOG | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Tue Jun  7 13:46:04 UTC 2022 - k3vinyue@gmail.com - 1.4.7 | ||||
|  | ||||
| - Update to 1.4.7 | ||||
|   * Updated VERSION, Bumped 1.4.6 –> 1.4.7 | ||||
|   * fix: release resources when properly | ||||
|   * fix: add support for parsing tokens from HTML | ||||
|   * handle html comment for saml result with okta 2fa (#156) | ||||
|   * chore: use auto to declare variable | ||||
|   * chore: simplify readme | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Wed Jun  1 15:55:50 UTC 2022 - k3vinyue@gmail.com - 1.4.6 | ||||
|  | ||||
| - Update to 1.4.6 | ||||
|   * Updated VERSION, Bumped 1.4.5 –> 1.4.6 | ||||
|   * feat: display address in gateway menu item | ||||
|   * fix: fix bug of parsing the portal response | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Sun May 29 13:15:40 UTC 2022 - k3vinyue@gmail.com - 1.4.5 | ||||
|  | ||||
| - Update to 1.4.5 | ||||
|   * Updated VERSION, Bumped 1.4.4 –> 1.4.5 | ||||
|   * chore: refine vscode settings | ||||
|   * fix: rollback dbus configuration | ||||
|   * feat: add option to start minimized | ||||
|   * packaging: fix postinst for debian | ||||
|   * packaging: add postinst for debian | ||||
|   * test: test debian packaging | ||||
|   * ci: fix the folder path | ||||
|   * chore: apt -> apt-get | ||||
|   * ci: verify debian package | ||||
|   * Revert "Revert "fix: improve the dbus security"" | ||||
|   * fix: improve the portal config parsing | ||||
|   * Revert "fix: improve the dbus security" | ||||
|   * fix: improve the dbus security | ||||
|   * fix: free resources in slots | ||||
|   * chore: refine cmake files | ||||
|   * fix: support high DPI screen | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Sat May 14 11:21:14 UTC 2022 - k3vinyue@gmail.com - 1.4.4 | ||||
|  | ||||
| - Update to 1.4.4 | ||||
|   * Updated VERSION, Bumped 1.4.3 –> 1.4.4 | ||||
|   * fix: support the HighDPI displays | ||||
|   * [misc] update the build script | ||||
|   * [ci] Enable build job for master branch | ||||
|   * [ci] Add ubuntu 22.04 | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Mon May  9 14:20:54 UTC 2022 - k3vinyue@gmail.com - 1.4.3 | ||||
|  | ||||
| - Update to 1.4.3 | ||||
|   * Updated VERSION, Bumped 1.4.2 –> 1.4.3 | ||||
|   * refine AUR packaging | ||||
|   * Prepare release 1.4.3 (#149) | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Fri May  6 14:18:19 UTC 2022 - k3vinyue@gmail.com - 1.4.2 | ||||
|  | ||||
| - Update to 1.4.2 | ||||
|   * Updated VERSION, Bumped 1.4.1 –> 1.4.2 | ||||
|   * Clear SSL_OP_LEGACY_SERVER_CONNECT (#146) | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Thu Mar  3 13:58:59 UTC 2022 - k3vinyue@gmail.com - 1.4.1 | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| Name:           globalprotect-openconnect | ||||
| Version:        1.4.1 | ||||
| Version:        1.4.9 | ||||
| Release:        1 | ||||
| Summary:        A GlobalProtect VPN client powered by OpenConnect | ||||
| Group:          Productivity/Networking/PPP | ||||
| @@ -8,7 +8,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-build | ||||
| License:        GPL-3.0 | ||||
| URL:            https://github.com/yuezk/GlobalProtect-openconnect | ||||
| Source0:        %{name}.tar.gz | ||||
| BuildRequires:  cmake cmake(Qt5) cmake(Qt5Gui) cmake(Qt5WebEngine) cmake(Qt5WebSockets) cmake(Qt5DBus) | ||||
| BuildRequires:  cmake cmake(Qt5) cmake(Qt5Gui) cmake(Qt5WebEngine) cmake(Qt5WebSockets) cmake(Qt5DBus) cmake(Qt5Keychain) | ||||
| BuildRequires:  systemd-rpm-macros | ||||
| Requires:       openconnect >= 8.0 | ||||
| Conflicts:      globalprotect-openconnect-snapshot | ||||
| @@ -54,7 +54,7 @@ A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Q | ||||
| %if 0%{?suse_version} | ||||
|     %service_del_postun gpservice.service | ||||
| %else | ||||
|     %systemd_postun gpservice.service | ||||
|     %systemd_postun_with_restart gpservice.service | ||||
| %endif | ||||
|  | ||||
|  | ||||
| @@ -88,7 +88,9 @@ A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Q | ||||
| %{_datadir}/dbus-1/system.d/com.yuezk.qt.GPService.conf | ||||
| %{_datadir}/icons/hicolor/scalable/apps/com.yuezk.qt.gpclient.svg | ||||
| %{_datadir}/metainfo/com.yuezk.qt.gpclient.metainfo.xml | ||||
| %config %{_sysconfdir}/gpservice/gp.conf | ||||
|  | ||||
| %dir %{_sysconfdir}/gpservice | ||||
| %dir %{_datadir}/icons/hicolor | ||||
| %dir %{_datadir}/icons/hicolor/scalable | ||||
| %dir %{_datadir}/icons/hicolor/scalable/apps | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| VERSION=$(cat VERSION VERSION_SUFFIX) | ||||
| VERSION="$(cat VERSION)" | ||||
|  | ||||
| rm -rf ./artifacts && mkdir -p ./artifacts/{obs,aur,flatpak} | ||||
|  | ||||
| @@ -18,8 +18,8 @@ cp -r ./packaging/obs ./artifacts | ||||
| cp ./artifacts/*.tar.gz ./artifacts/obs/globalprotect-openconnect.tar.gz | ||||
|  | ||||
| # Prepare the AUR package | ||||
| cp ./packaging/aur/PKGBUILD-git ./artifacts/aur/PKGBUILD | ||||
| cp ./artifacts/*.tar.gz ./artifacts/aur/globalprotect-openconnect.tar.gz | ||||
| cp ./packaging/aur/PKGBUILD ./artifacts/aur/PKGBUILD | ||||
| cp ./packaging/aur/gp.install ./artifacts/aur/gp.install | ||||
|  | ||||
| # Prepare the flatpak package | ||||
| cp ./packaging/flatpak/com.yuezk.qt.gpclient.yml ./artifacts/flatpak | ||||
|   | ||||
							
								
								
									
										13
									
								
								scripts/install-debian.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								scripts/install-debian.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| sudo apt-get update | ||||
| sudo apt-get install -y \ | ||||
|     build-essential \ | ||||
|     qtbase5-dev \ | ||||
|     libqt5websockets5-dev \ | ||||
|     qtwebengine5-dev \ | ||||
|     qttools5-dev \ | ||||
|     qt5keychain-dev \ | ||||
|     openconnect \ | ||||
|  | ||||
| ./scripts/install.sh | ||||
| @@ -4,6 +4,7 @@ sudo dnf install -y \ | ||||
|     qt5-qtbase-devel \ | ||||
|     qt5-qtwebengine-devel \ | ||||
|     qt5-qtwebsockets-devel \ | ||||
|     qtkeychain-qt5-devel \ | ||||
|     openconnect | ||||
|  | ||||
| ./scripts/install.sh | ||||
| @@ -4,6 +4,7 @@ sudo zypper install -y \ | ||||
|     libqt5-qtbase-devel \ | ||||
|     libqt5-qtwebsockets-devel \ | ||||
|     libqt5-qtwebengine-devel \ | ||||
|     qtkeychain-qt5-devel \ | ||||
|     openconnect | ||||
|  | ||||
| ./scripts/install.sh | ||||
| @@ -1,11 +1,13 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| sudo apt update | ||||
| sudo apt install -y \ | ||||
| sudo apt-get update | ||||
| sudo apt-get install -y \ | ||||
|     build-essential \ | ||||
|     qtbase5-dev \ | ||||
|     libqt5websockets5-dev \ | ||||
|     qtwebengine5-dev \ | ||||
|     qttools5-dev \ | ||||
|     qt5keychain-dev \ | ||||
|     openconnect | ||||
|  | ||||
| ./scripts/install.sh | ||||
| @@ -1,53 +0,0 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| VERSION="$(cat VERSION VERSION_SUFFIX)" | ||||
| OLD_REVISION="1" | ||||
|  | ||||
| PPA_REPO="ppa:yuezk/globalprotect-openconnect-snapshot" | ||||
|  | ||||
| while [[ $# -gt 0 ]]; do | ||||
|     key="$1" | ||||
|  | ||||
|     case $key in | ||||
|         --stable) | ||||
|             PPA_REPO="ppa:yuezk/globalprotect-openconnect" | ||||
|             shift | ||||
|             ;; | ||||
|         "18.04") | ||||
|             DISTRIBUTION="18.04" | ||||
|             DISTRIBUTION_NAME="bionic" | ||||
|             shift | ||||
|             ;; | ||||
|         "20.04") | ||||
|             DISTRIBUTION="20.04" | ||||
|             DISTRIBUTION_NAME="focal" | ||||
|             shift | ||||
|             ;; | ||||
|         "21.04") | ||||
|             DISTRIBUTION="21.04" | ||||
|             DISTRIBUTION_NAME="hirsute" | ||||
|             shift | ||||
|             ;; | ||||
|         "21.10") | ||||
|             DISTRIBUTION="21.10" | ||||
|             DISTRIBUTION_NAME="impish" | ||||
|             shift | ||||
|             ;; | ||||
|         *) | ||||
|             echo "Unkown options $key" | ||||
|             exit 1 | ||||
|             ;; | ||||
|     esac | ||||
| done | ||||
|  | ||||
| [ -z $DISTRIBUTION ] && echo "The distribuation is required" && exit 1; | ||||
|  | ||||
| NEW_REVISION="ppa1~ubuntu${DISTRIBUTION}" | ||||
|  | ||||
| sed -i"" "1s/${VERSION}-${OLD_REVISION}/${VERSION}-${NEW_REVISION}/;1s/unstable/${DISTRIBUTION_NAME}/" debian/changelog | ||||
| debmake | ||||
| debuild -S -sa \ | ||||
|     -k"${PPA_GPG_KEYID}" \ | ||||
|     -p"gpg --batch --passphrase ${PPA_GPG_PASSPHRASE} --pinentry-mode loopback" | ||||
|  | ||||
| dput $PPA_REPO ../globalprotect-openconnect_${VERSION}-${NEW_REVISION}_source.changes | ||||
| @@ -2,14 +2,13 @@ | ||||
|  | ||||
| OLD_VERSION=$(git tag --sort=-v:refname --list "v[0-9]*" | head -n 1 | cut -c 2-) | ||||
| NEW_VERSION="$(cat VERSION)" | ||||
| FULL_VERSION="$(cat VERSION VERSION_SUFFIX)" | ||||
| HISTORY_ENTRIES=$(git log --format="  * %s" v${OLD_VERSION}.. | cat -n | sort -uk2 | sort -n | cut -f2-) | ||||
|  | ||||
| function update_debian_changelog() { | ||||
| 	local OLD_CHANGELOG=$(cat debian/changelog) | ||||
|  | ||||
| 	cat > debian/changelog <<-EOF | ||||
| 	globalprotect-openconnect (${FULL_VERSION}-1) unstable; urgency=medium | ||||
| 	globalprotect-openconnect (${NEW_VERSION}-1) unstable; urgency=medium | ||||
|  | ||||
| 	${HISTORY_ENTRIES} | ||||
|  | ||||
| @@ -24,17 +23,24 @@ function update_rpm_changelog() { | ||||
|  | ||||
| 	cat > packaging/obs/globalprotect-openconnect.changes <<-EOF | ||||
| 	------------------------------------------------------------------- | ||||
| 	$(LC_ALL=en.US date -u "+%a %b %e %T %Z %Y") - k3vinyue@gmail.com - ${FULL_VERSION} | ||||
| 	$(LC_ALL=en.US date -u "+%a %b %e %T %Z %Y") - k3vinyue@gmail.com - ${NEW_VERSION} | ||||
|  | ||||
| 	- Update to ${FULL_VERSION} | ||||
| 	- Update to ${NEW_VERSION} | ||||
| 	${HISTORY_ENTRIES} | ||||
|  | ||||
| 	${OLD_CHANGELOG} | ||||
| 	EOF | ||||
| } | ||||
|  | ||||
| function generate_pkgbuild() { | ||||
| 	local commit_id="$(git rev-parse HEAD)" | ||||
| 	local version="$(cat VERSION)" | ||||
| 	sed -e "s/{COMMIT}/${commit_id}/" -e "s/{VERSION}/${version}/" packaging/aur/PKGBUILD.in > packaging/aur/PKGBUILD | ||||
| } | ||||
|  | ||||
| # Update rpm version | ||||
| sed -i"" -re "s/(Version:\s+).+/\1${FULL_VERSION}/" packaging/obs/globalprotect-openconnect.spec | ||||
| sed -i"" -re "s/(Version:\s+).+/\1${NEW_VERSION}/" packaging/obs/globalprotect-openconnect.spec | ||||
|  | ||||
| update_rpm_changelog | ||||
| update_debian_changelog | ||||
| generate_pkgbuild | ||||
|   | ||||
| @@ -2,9 +2,6 @@ | ||||
|  | ||||
| VERSION=$(cat VERSION) | ||||
|  | ||||
| # Clear the VERSION_SUFFIX | ||||
| cat /dev/null > VERSION_SUFFIX | ||||
|  | ||||
| # Update packaging, e.g., version, changelog, etc. | ||||
| ./scripts/prepare-packaging.sh | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,3 @@ mv ./artifacts/obs/globalprotect-openconnect-rpmlintrc ./artifacts/obs/globalpro | ||||
| sed -i"" -re "s/(Name:\s+).+/\1globalprotect-openconnect-snapshot/" \ | ||||
|     -re "s/(Conflicts:\s+).+/\1globalprotect-openconnect/" \ | ||||
|     ./artifacts/obs/globalprotect-openconnect-snapshot.spec | ||||
|  | ||||
| # Update the AUR package | ||||
| cp ./packaging/aur/PKGBUILD-git ./artifacts/aur/PKGBUILD | ||||
| @@ -1,3 +1,4 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| git describe --tags --match "v$(cat VERSION)" | sed -r -e 's/v([^-]+)-/+snapshot/' -e 's/-/./' > VERSION_SUFFIX | ||||
| VERSION="v$(cat VERSION)" | ||||
| git describe --tags --match "${VERSION}" | sed -re 's/^v([^-]+)-([^-]+)-(.+)/\1+\2snapshot.\3/' > VERSION | ||||
							
								
								
									
										19
									
								
								scripts/verify-debian-package.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								scripts/verify-debian-package.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| sudo apt-get update | ||||
| sudo apt-get install -y \ | ||||
| 	build-essential \ | ||||
| 	qtbase5-dev \ | ||||
| 	libqt5websockets5-dev \ | ||||
| 	qtwebengine5-dev \ | ||||
| 	qt5keychain-dev \ | ||||
| 	cmake \ | ||||
| 	qttools5-dev \ | ||||
| 	debhelper | ||||
|  | ||||
| mkdir -p build | ||||
|  | ||||
| cp ./artifacts/*.tar.gz build/ && cd build | ||||
| tar -xzf *.tar.gz && cd globalprotect-openconnect-*/ | ||||
|  | ||||
| dpkg-buildpackage -us -uc | ||||
| @@ -66,7 +66,7 @@ parts: | ||||
|     override-pull: | | ||||
|       snapcraftctl pull | ||||
|  | ||||
|       VERSION=$(cat VERSION VERSION_SUFFIX) | ||||
|       VERSION=$(cat VERSION) | ||||
|       GRADE="stable" | ||||
|  | ||||
|       if echo "$VERSION" | grep -q "snapshot" | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| #define VERSION "@GlobalProtect-openconnect_VERSION@@VERSION_SUFFIX@" | ||||
| #define VERSION "@version@" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user