├── .dep.inc ├── LICENSE ├── Makefile ├── README ├── build ├── client ├── Client.cpp ├── Client.h ├── Conf.cpp ├── Conf.h ├── Detect.h └── Detect_linux.cpp ├── gears.json ├── nbproject ├── Makefile-Debug.mk ├── Makefile-Release.mk ├── Makefile-impl.mk ├── Makefile-variables.mk ├── configurations.xml └── project.xml ├── precomp ├── precomp.cpp └── precomp.h ├── run └── utils ├── EzCurl.cpp ├── EzCurl.h ├── Logging.h ├── StringUtils.h ├── Timer.h ├── json11 ├── compat.h ├── json11.cpp ├── json11.hpp └── json11help.h ├── optionparser ├── JsonOptionParser.h └── optionparser.h └── os_spec ├── StringUtils_linux.h ├── StringUtils_osx.h └── StringUtils_windows.h /.dep.inc: -------------------------------------------------------------------------------- 1 | # This code depends on make tool being used 2 | DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES})) 3 | ifneq (${DEPFILES},) 4 | include ${DEPFILES} 5 | endif 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Gears Client End User License Agreement 2 | 3 | This End User License Agreement ("EULA") is a legal and enforceable agreement between you (“You”), You being either an individual or a single legal entity, and OPSWAT, Inc. (“OPSWAT”) for use of Gears Client component of the Gears Cloud service described at . In the absence of a separate written agreement between You (or an authorized representative of a legal entity on behalf of which you have downloaded the Gears Client) and OPSWAT which, by its terms, explicitly modifies or replaces this EULA, You agree to be bound by all of following terms of this EULA and the Gears Cloud Terms of Service referenced below. 4 | 5 | 1. Background 6 | OPSWAT develops, maintains and provides access to Gears Cloud a solution that enables Admin Users (defined in the Gears Cloud Terms of Service located at , terms of which are incorporated by reference to this EULA) to monitor and manage the security state of Devices (also defined in the Gears Cloud Terms of Service) on which Gears Clients are installed. 7 | 8 | 2. License Grant 9 | OPSWAT grants to You a nonexclusive, nontransferable, non-sublicensable, revocable and limited license to access and use the Gears Client solely for Your internal purposes. 10 | 11 | 3. Restrictions 12 | Unless expressly otherwise set forth in this EULA, You will not: (a) modify, translate or create derivative works of the Software; (b) decompile, reverse engineer or reverse assemble any portion of the Software or attempt to discover any source code or underlying ideas or algorithms of the Gears Client; (c) sell, assign, sublicense, rent, lease, loan, provide, distribute or otherwise transfer all or any portion of the Gears Client; (d) make, have made, reproduce or copy the Gears Client; (e) remove or alter any trademark, logo, copyright or other proprietary notices associated with the Gears Client; and (f) cause or permit any other party to do any of the foregoing. 13 | 14 | 4. Disclaimer of Warranties 15 | Gears CLIENT IS PROVIDED ON AN “AS IS” OR “AS AVAILABLE” BASIS WITHOUT ANY REPRESENTATIONS, WARRANTIES, COVENANTS OR CONDITIONS OF ANY KIND. OPSWAT AND ITS SUPPLIERS DO NOT WARRANT THAT Gears CLIENT WILL BE FREE FROM ALL BUGS, ERRORS, OR OMISSIONS. OPSWAT AND ITS SUPPLIERS DISCLAIM ANY AND ALL OTHER WARRANTIES AND REPRESENTATIONS (EXPRESS OR IMPLIED, ORAL OR WRITTEN) WITH RESPECT Gears CLIENT WHETHER ALLEGED TO ARISE BY OPERATION OF LAW, BY REASON OF CUSTOM OR USAGE IN THE TRADE, BY COURSE OF DEALING OR OTHERWISE, INCLUDING ANY AND ALL (I) WARRANTIES OF MERCHANTABILITY, (II) WARRANTIES OF FITNESS OR SUITABILITY FOR ANY PURPOSE (WHETHER OR NOT OPSWAT KNOWS, HAS REASON TO KNOW, HAS BEEN ADVISED, OR IS OTHERWISE AWARE OF ANY SUCH PURPOSE), AND (III) WARRANTIES OF NONINFRINGEMENT OR CONDITION OF TITLE. YOU ACKNOWLEDGE AND AGREE THAT YOU RELIED ON NO WARRANTIES. 16 | 17 | 5. Indemnification 18 | You will indemnify, defend, and hold harmless OPSWAT from and against all liabilities, damages, and costs (including settlement costs and reasonable attorneys' fees) arising out of a third party claim regarding Your use or misuse of Gears Client. 19 | 20 | 6. WAIVER OF CONSEQUENTIAL DAMAGES AND LIMITATION OF LIABILITY 21 | 6.1 WAIVER OF CONSEQUENTIAL DAMAGES. IN NO EVENT WILL OPSWAT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, STATUTORY, PUNITIVE, INDIRECT OR EXEMPLARY DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO ANY LOST PROFITS AND LOST SAVINGS, HOWEVER CAUSED, WHETHER FOR BREACH OR REPUDIATION OF CONTRACT, TORT, BREACH OF WARRANTY, NEGLIGENCE, OR OTHERWISE, WHETHER OR NOT OPSWAT WAS ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. THE FOREGOING LIMITATION SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY LIMITED WARRANTY SET FORTH IN THIS TOS. 22 | 6.2 LIMITATION OF LIABILITY. OPSWAT (INCLUDING FOR THE PURPOSES OF THIS SECTION THEIR RESPECTIVE DIRECTORS, OFFICERS, AGENTS, EMPLOYEES, CONTRACTORS, AND LICENSORS) SHALL NOT BE LIABLE TO CUSTOMER OR ANY THIRD PARTY FOR ANY INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES ARISING FROM OR RELATED TO THE USE, MISUSE OR INABILITY TO USE Gears CLIENT, Gears CLOUD OR ANY OPSWAT PRODUCT (INCLUDING BUT NOT LIMITED TO LOSS OF USE OR GOODWILL, INTERRUPTION OF BUSINESS, LOSS OF PROFITS OR REVENUE, AND COST OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES), REGARDLESS OF THE FORM OF ACTION WHETHER, IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, EVEN IF CUSTOMER OR SUCH OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN NO EVENT SHALL OPSWAT LIABILITY TO CUSTOMER OR ANY THIRD PARTY ARISING FROM OR RELATING TO THIS EULA EXCEED THE AMOUNT PAID (AND THEN OWED) BY CUSTOMER TO OPSWAT UNDER THIS EULA WITHIN THE THREE MONTHS PRECEDING THE DATE THE LOSS AROSE. THE EXISTENCE OF ONE OR MORE CLAIMS WILL NOT ENLARGE THIS LIMIT. 23 | 24 | 7. Third Party Suppliers 25 | Gears Client may include software or other code distributed under license from third party suppliers (“Third Party Component”). You acknowledge that such third party suppliers disclaim and make no representation or warranty with respect to the Software or any portion thereof and assume no liability for any claim that may arise with respect to the Software or Your use or inability to use the same. Further, if Gears Client You have installed includes the following Third Party Software, You additionally agree to the specific terms associated with each Third Party Component, as follows: 26 | 27 | /* 28 | * The Lean Mean C++ Option Parser 29 | * 30 | * Copyright (C) 2012 Matthias S. Benkmann 31 | * 32 | * The "Software" in the following 2 paragraphs refers to this file containing 33 | * the code to The Lean Mean C++ Option Parser. 34 | * The "Software" does NOT refer to any other files which you 35 | * may have received alongside this file (e.g. as part of a larger project that 36 | * incorporates The Lean Mean C++ Option Parser). 37 | * 38 | * Permission is hereby granted, free of charge, to any person obtaining a copy 39 | * of this software, to deal in the Software without restriction, including 40 | * without limitation the rights to use, copy, modify, merge, publish, 41 | * distribute, sublicense, and/or sell copies of the Software, and to permit 42 | * persons to whom the Software is furnished to do so, subject to the following 43 | * conditions: 44 | * The above copyright notice and this permission notice shall be included in 45 | * all copies or substantial portions of the Software. 46 | * 47 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 53 | * SOFTWARE. 54 | */ 55 | 56 | /* json11 57 | * 58 | * Copyright (c) 2013 Dropbox, Inc. 59 | * 60 | * Permission is hereby granted, free of charge, to any person obtaining a copy 61 | * of this software and associated documentation files (the "Software"), to deal 62 | * in the Software without restriction, including without limitation the rights 63 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | * copies of the Software, and to permit persons to whom the Software is 65 | * furnished to do so, subject to the following conditions: 66 | * 67 | * The above copyright notice and this permission notice shall be included in 68 | * all copies or substantial portions of the Software. 69 | * 70 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | * THE SOFTWARE. 77 | */ 78 | 79 | /* 80 | * Copyright 2001-2004 Unicode, Inc. 81 | * 82 | * Disclaimer 83 | * 84 | * This source code is provided as is by Unicode, Inc. No claims are 85 | * made as to fitness for any particular purpose. No warranties of any 86 | * kind are expressed or implied. The recipient agrees to determine 87 | * applicability of information provided. If this file has been 88 | * purchased on magnetic or optical media from Unicode, Inc., the 89 | * sole remedy for any claim will be exchange of defective media 90 | * within 90 days of receipt. 91 | * 92 | * Limitations on Rights to Redistribute This Code 93 | * 94 | * Unicode, Inc. hereby grants the right to freely use the information 95 | * supplied in this file in the creation of products supporting the 96 | * Unicode Standard, and to make copies of this file in any form 97 | * for internal or external distribution as long as this notice 98 | * remains attached. 99 | */ 100 | 101 | /* curl 102 | * COPYRIGHT AND PERMISSION NOTICE 103 | * 104 | * Copyright (c) 1996 - 2015, Daniel Stenberg, . 105 | * 106 | * All rights reserved. 107 | * 108 | * Permission to use, copy, modify, and distribute this software for any purpose 109 | * with or without fee is hereby granted, provided that the above copyright 110 | * notice and this permission notice appear in all copies. 111 | * 112 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 113 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 114 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN 115 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 116 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 117 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 118 | * OR OTHER DEALINGS IN THE SOFTWARE. 119 | * 120 | * Except as contained in this notice, the name of a copyright holder shall not 121 | * be used in advertising or otherwise to promote the sale, use or other dealings 122 | * in this Software without prior written authorization of the copyright holder. 123 | */ 124 | 125 | 8. Termination 126 | You can stop using Gears Client at any time by following standard removal instructions for software applications developed for Microsoft Windows, Linux and Apple Mac OS X operating systems. OPSWAT reserves the right to suspend or end the Gears Cloud at any time, with or without cause, and with or without notice. For example, OPSWAT may suspend or terminate Gears Cloud use if You are not complying with this EULA or the Gears Cloud Terms of Service, or if use of Gears Client or Gears Cloud in any way that would cause OPSWAT legal liability or disrupt others’ use of the Gears Cloud. 127 | 128 | 9. Updates 129 | OPSWAT reserves the right, in its sole discretion, to make unscheduled deployments of updates or enhancements to Gears Client or Gears Cloud service. You acknowledge and understand that during such deployments, certain functionality of Gears Client or Gears Cloud may be unavailable and outages may occur. 130 | 131 | 10. General Provisions 132 | 10.1 Choice of Law; Venue. This EULA will be governed by and construed according to the laws of the State of California, excluding California’s choice of law principles. In the event of any dispute or claim arising out of this EULA, You hereby submit to exclusive venue in, and the exclusive jurisdiction of, the federal and state courts, as applicable, located in San Francisco, California (except that a party may enforce a judgment in any court of competent jurisdiction). 133 | 10.2 Waiver. No waiver will be implied from conduct or failure to enforce rights. No waiver will be effective unless in writing signed by the party against whom the waiver is asserted, and such waiver shall only be effective as to the particular act identified in the written waiver. 134 | 10.3 Severability. If any part of this EULA is found invalid or unenforceable (the “Invalid Clause”), that part will be enforced to the maximum extent permitted by law, and the remainder of this EULA will be enforced to the extent such enforcement, when viewed in light of the full or partial exclusion of the Invalid Clause, is consistent with the parties’ intent. 135 | 10.4 Relationship of Parties. The parties to this EULA are independent contractors. There is no relationship of agency, partnership, joint venture, employment or franchise between the parties. Neither party has the authority to bind the other or to incur any obligation on its behalf. Neither party shall have, nor shall it represent that it has, any power, right or authority to bind the other party, or to assume or create any obligation or responsibility, express or implied, on behalf of the other party or in the other party’s name, except as herein expressly permitted. 136 | 10.5 Freedom of Action. This EULA shall not be construed to prohibit or restrict either party from, without breach of the party’s obligations hereunder, developing, making, having made, using, leasing, licensing, buying, selling or otherwise disposing of or dealing with in any manner any products or services whatsoever, now or in the future. 137 | 10.6 Entire EULA. This EULA represents the entire EULA between the parties relating to its subject matter and supersedes all prior representations, discussions, negotiations and EULAs, whether written or oral. Questions regarding this EULA should be directed to sales@opswat.com. 138 | 139 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # There exist several targets which are by default empty and which can be 3 | # used for execution of your targets. These targets are usually executed 4 | # before and after some main targets. They are: 5 | # 6 | # .build-pre: called before 'build' target 7 | # .build-post: called after 'build' target 8 | # .clean-pre: called before 'clean' target 9 | # .clean-post: called after 'clean' target 10 | # .clobber-pre: called before 'clobber' target 11 | # .clobber-post: called after 'clobber' target 12 | # .all-pre: called before 'all' target 13 | # .all-post: called after 'all' target 14 | # .help-pre: called before 'help' target 15 | # .help-post: called after 'help' target 16 | # 17 | # Targets beginning with '.' are not intended to be called on their own. 18 | # 19 | # Main targets can be executed directly, and they are: 20 | # 21 | # build build a specific configuration 22 | # clean remove built files from a configuration 23 | # clobber remove all built files 24 | # all build all configurations 25 | # help print help mesage 26 | # 27 | # Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and 28 | # .help-impl are implemented in nbproject/makefile-impl.mk. 29 | # 30 | # Available make variables: 31 | # 32 | # CND_BASEDIR base directory for relative paths 33 | # CND_DISTDIR default top distribution directory (build artifacts) 34 | # CND_BUILDDIR default top build directory (object files, ...) 35 | # CONF name of current configuration 36 | # CND_PLATFORM_${CONF} platform name (current configuration) 37 | # CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) 38 | # CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) 39 | # CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) 40 | # CND_PACKAGE_DIR_${CONF} directory of package (current configuration) 41 | # CND_PACKAGE_NAME_${CONF} name of package (current configuration) 42 | # CND_PACKAGE_PATH_${CONF} path to package (current configuration) 43 | # 44 | # NOCDDL 45 | 46 | 47 | # Environment 48 | MKDIR=mkdir 49 | CP=cp 50 | CCADMIN=CCadmin 51 | 52 | 53 | # build 54 | build: .build-post 55 | 56 | .build-pre: 57 | # Add your pre 'build' code here... 58 | 59 | .build-post: .build-impl 60 | # Add your post 'build' code here... 61 | 62 | 63 | # clean 64 | clean: .clean-post 65 | 66 | .clean-pre: 67 | # Add your pre 'clean' code here... 68 | 69 | .clean-post: .clean-impl 70 | rm -Rf *.o 71 | .clean-impl: .clean-pre 72 | 73 | # clobber 74 | clobber: .clobber-post 75 | 76 | .clobber-pre: 77 | # Add your pre 'clobber' code here... 78 | 79 | .clobber-post: .clobber-impl 80 | # Add your post 'clobber' code here... 81 | 82 | 83 | # all 84 | all: .all-post 85 | 86 | .all-pre: 87 | # Add your pre 'all' code here... 88 | 89 | .all-post: .all-impl 90 | # Add your post 'all' code here... 91 | 92 | 93 | # build tests 94 | build-tests: 95 | 96 | .build-tests-pre: 97 | # Add your pre 'build-tests' code here... 98 | 99 | .build-tests-post: .build-tests-impl 100 | # Add your post 'build-tests' code here... 101 | 102 | 103 | # run tests 104 | test: 105 | 106 | .test-pre: 107 | # Add your pre 'test' code here... 108 | 109 | .test-post: .test-impl 110 | # Add your post 'test' code here... 111 | 112 | 113 | # help 114 | help: .help-post 115 | 116 | .help-pre: 117 | # Add your pre 'help' code here... 118 | 119 | .help-post: .help-impl 120 | # Add your post 'help' code here... 121 | 122 | 123 | 124 | # include project implementation makefile 125 | include nbproject/Makefile-impl.mk 126 | 127 | # include project make variables 128 | include nbproject/Makefile-variables.mk 129 | 130 | CND_BUILDDIR=obj 131 | 132 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | OPSWAT Gears Agent - Community Version 3 | 4 | 5 | 6 | What is it? 7 | ----------- 8 | 9 | OPSWAT Gears is a monitoring and management product that provides 10 | device visibility and compliance reporting for corporate-owned 11 | and BYOD devices. It collects basic information about the system. 12 | It does not collect personal or sensitive information. All data 13 | is transmitted using TLS encryption to Gears servers where it is 14 | used to compute device compliance against a security policy baseline. 15 | 16 | This is the open source version of the end-point agent. 17 | It provides almost all of the same functionality as the closed- 18 | source version except it does not include a copy of the OESIS 19 | libraries for detection and manageability, specifically for 20 | antivirus-related features and firewall-related features. 21 | 22 | 23 | The Latest Version 24 | ------------------ 25 | 26 | The open-source version of the app does not auto-update. The latest 27 | version is available in the source code repository. 28 | 29 | 30 | Documentation 31 | ------------- 32 | 33 | Light documentation is available online by visiting: 34 | https://www.opswatgears.com/ 35 | https://gears.opswat.com/developers/docs 36 | 37 | See detect/Detect_Linux.cpp for information about implementing 38 | your own reporting to Gears Cloud Platform. 39 | 40 | 41 | Installation 42 | ------------ 43 | 44 | Please run ./build to build gearsd 45 | Then run ./run with your Gears registration code as the first parameter 46 | 47 | 48 | Licensing 49 | --------- 50 | 51 | Please see the file called LICENSE. 52 | 53 | 54 | What the agent can do 55 | ---------------------- 56 | 57 | o Register to Gears cloud by short code 58 | o Send ping command to say hello to Gears cloud 59 | o Send health report to the cloud. 60 | o Retire removing itself from cloud. 61 | 62 | 63 | Code structure 64 | -------------- 65 | 66 | . 67 | |-- "README" -> this file 68 | |-- "build" -> build gearsd 69 | |-- "run" -> run with your Gears Shortcode as first parameter 70 | |-- "LICENSE" -> license file 71 | |-- "gears.json" -> gears config setting file 72 | |-- "client" 73 | | |-- "Client.cpp" -> including main function, executing command, running agent, registering client, sending keepalive, sending health report 74 | | |-- "Client.h" -> defining log dir and configuration file name 75 | | |-- "Conf.cpp" -> used to set logging location, get/set gears settings from gears.json file 76 | | |-- "Conf.h" 77 | | |-- "Detect.h" 78 | | `-- "Detect_linux.cpp" -> provide info about implementing your own reporting to Gears Cloud Platform. 79 | |-- "nbproject" -> netbean project directory 80 | |-- "precomp" -> including all necessary header files for using precompiled headers 81 | `-- "utils" 82 | |-- "EzCurl.cpp" -> wrapper class using curl library to connect and send information to cloud 83 | |-- "EzCurl.h" 84 | |-- "json11" -> json library 85 | | |-- "compat.h" 86 | | |-- "json11.cpp" 87 | | |-- "json11help.h" 88 | | `-- "json11.hpp" 89 | |-- "Logging.h" -> used to create and make log info more attractive, easy to read by using json format 90 | |-- "optionparser" -> used to parse the command line, run gearsd --help for more detail 91 | | |-- "JsonOptionParser.h" 92 | | `-- "optionparser.h" 93 | |-- "StringUtils.h" -> common string utils 94 | `-- "Timer.h" -> class used to make timer for each routine including register, keepalive, health report 95 | 96 | 97 | Contacts 98 | -------- 99 | 100 | o If you want to be informed about new releases, general news and 101 | more about OPSWAT Gears, follow the blog at: 102 | https://www.opswatgears.com/blog 103 | 104 | o If you want support for running OPSWAT Gears please see the 105 | resources at: 106 | https://www.opswatgears.com/support 107 | 108 | 109 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | 4 | make clean 5 | make 6 | 7 | -------------------------------------------------------------------------------- /client/Client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Client.h" 4 | #include "Conf.h" 5 | #include "Detect.h" 6 | 7 | #include "../utils/EzCurl.h" 8 | #include "../utils/Logging.h" 9 | #include "../utils/Timer.h" 10 | 11 | #include "../utils/json11/json11.hpp" 12 | #include "../utils/json11/json11help.h" 13 | #include "../utils/optionparser/JsonOptionParser.h" 14 | 15 | using namespace std; 16 | using namespace option; 17 | using namespace json11; 18 | using namespace Curl; 19 | 20 | struct HttpRequest 21 | { 22 | string method; 23 | string url; 24 | Json::object headers; 25 | Json body; 26 | }; 27 | 28 | struct HttpResponse 29 | { 30 | uint16_t responseStatus; 31 | uint32_t errorCode; 32 | Json::object body; 33 | }; 34 | 35 | HttpResponse performHttpRequest(const HttpRequest& httpRequest) 36 | { 37 | HttpResponse httpResponse; 38 | Curl::headers_t headers = JsonToMap(httpRequest.headers); 39 | 40 | headers["Content-Type"] = "application/json"; 41 | Curl::httpRequest req = { httpRequest.method, httpRequest.url, { headers, httpRequest.body.is_null()?"":httpRequest.body.dump() } }; 42 | traceInfo("request request: " << httpRequest.method << " " << httpRequest.url, 0); 43 | traceJson("request headers", Json(httpRequest.headers)); 44 | traceJson("request body", httpRequest.body); 45 | CurlLib::getInstance()->Request(req); 46 | 47 | if ( (httpResponse.errorCode = req.errorCode) != 0) 48 | { 49 | logError("CurlLib Error: " << req.error, req.errorCode); 50 | httpResponse.body["err"] = req.error; 51 | return httpResponse; 52 | } 53 | 54 | string parseErr; 55 | if (req.response.body.length() > 0) 56 | httpResponse.body = Json::parse(req.response.body, parseErr).object_items(); 57 | 58 | traceJson("response headers", Json(req.response.headers)); 59 | 60 | if (parseErr.length() != 0) 61 | { 62 | httpResponse.body = Json::object(); 63 | httpResponse.body["err"] = parseErr; 64 | httpResponse.body["text"] = req.response.body; 65 | } 66 | 67 | traceJson("Response body", httpResponse.body); 68 | 69 | httpResponse.responseStatus = req.respCode; 70 | return httpResponse; 71 | } 72 | 73 | bool isHttpSuccessful(HttpResponse response, const char* msg) 74 | { 75 | 76 | if (response.errorCode != 0) 77 | { 78 | logError("Failed " << msg, response.errorCode); 79 | return false; 80 | } 81 | 82 | if (isStatusCode200s(response.responseStatus)) 83 | { 84 | logWrite(LogLevel::info, response.responseStatus, "Successful response code for " << msg, nullptr); 85 | return true; 86 | } 87 | 88 | logError("Failed " << msg << " due to status code", response.responseStatus); 89 | return false; 90 | } 91 | 92 | bool sendKeepAlive() 93 | { 94 | Json::object settings = GetSettings(); 95 | Json::object headers; 96 | 97 | CopySelectedObject(headers, settings, { "Gears-Access-Token", "Gears-Access-Key" }); 98 | 99 | string url = settings["Gears-Server"].string_value() + "/api/v2/accounts/" + settings["license_key"].string_value() + "/devices/" 100 | + settings["device_id"].string_value() + "/ping"; 101 | 102 | logMessage("Sending KeepAlive message"); 103 | isHttpSuccessful(performHttpRequest( { "POST", url, headers, Json::object{} } ), "send KeepAlive to server"); 104 | 105 | return true; // return true so timer will resent report 106 | } 107 | 108 | struct AutoDetect 109 | { 110 | Detect* getInstance() { return Detect::getInstance(); } 111 | ~AutoDetect() { getInstance()->release(); } 112 | }; 113 | 114 | bool reportData() 115 | { 116 | Json::object request = AutoDetect().getInstance()->getFullReport(); 117 | Json::object settings = GetSettings(); 118 | Json::object headers; 119 | 120 | CopySelectedObject(headers, settings, { "Gears-Access-Token", "Gears-Access-Key" }); 121 | CopySelectedObject(request, settings, { "device_name", "agent_version" }); 122 | 123 | string url = settings["Gears-Server"].string_value() + "/api/v2/accounts/" + settings["license_key"].string_value() + "/devices/" 124 | + settings["device_id"].string_value() + "/report/health"; 125 | 126 | logMessage("Sending soh message"); 127 | isHttpSuccessful(performHttpRequest( { "PUT", url, headers, request } ), "send soh to server"); 128 | 129 | return true; // return true so timer will resent report 130 | } 131 | 132 | bool registerClient() 133 | { 134 | AutoDetect detect; 135 | 136 | Json::object request = detect.getInstance()->getDeviceInfo(); 137 | Json::object info = detect.getInstance()->getVersionInfo(); 138 | Json::object settings = GetSettings(); 139 | 140 | string typeMsg = VerifyObjectType(settings, { { "agent_version", Json::STRING }, { "Gears-Auth", Json::STRING } }); 141 | if (!typeMsg.empty()) 142 | { 143 | logWrite(LogLevel::warn, 0, typeMsg, nullptr); 144 | return true; // return true so timer will retry 145 | } 146 | 147 | Json::object jsonHeaders; 148 | CopySelectedObject(request, settings, { "device_name", "agent_version" }); 149 | CopySelectedObject(jsonHeaders, settings, { "Gears-Auth" }); 150 | 151 | if (request["device_name"].string_value().empty() && !info["device_name"].string_value().empty()) 152 | request["device_name"] = info["device_name"]; 153 | 154 | string url = settings["Gears-Server"].string_value() + "/api/v2/accounts/devices/registration_code"; 155 | 156 | logMessage("registerClient"); 157 | HttpResponse httpResponse = performHttpRequest( { "POST", url, jsonHeaders, request } ); 158 | if (!isHttpSuccessful(httpResponse, "send Register with server")) 159 | return true; 160 | 161 | typeMsg = VerifyObjectType(httpResponse.body, { { "license_key", Json::STRING }, { "device_id", Json::STRING }, { "access_key", Json::STRING }, { "access_token", Json::STRING } }); 162 | if (!typeMsg.empty()) 163 | { 164 | logError(typeMsg, 0); 165 | return true; 166 | } 167 | 168 | CopySelectedObject(settings, httpResponse.body, { "device_id", "license_key"} ); 169 | settings["Gears-Access-Key"] = httpResponse.body["access_key"]; 170 | settings["Gears-Access-Token"] = httpResponse.body["access_token"]; 171 | 172 | return !SaveSettings(settings); // return false to stop timer 173 | } 174 | 175 | int runAgent(const Json & parameters) 176 | { 177 | if (GetSettings()["Gears-Access-Key"].string_value().empty()) // if no key then need register 178 | Timer RegisterTimer(GetSettings()["Ping-Freq"].int_value() * 1000, false, ®isterClient); 179 | 180 | Timer PingTimer(GetSettings()["Ping-Freq"].int_value() * 1000, true, &sendKeepAlive); 181 | Timer Report(GetSettings()["Report-Freq"].int_value() * 1000, false, &reportData); 182 | return 0; 183 | } 184 | 185 | int retireClient(const Json & parameters) 186 | { 187 | Json::object settings = GetSettings(); 188 | Json::object headers; 189 | 190 | CopySelectedObject(headers, settings, { "Gears-Access-Token", "Gears-Access-Key" }); 191 | 192 | string url = settings["Gears-Server"].string_value() + "/api/v2/accounts/" + settings["license_key"].string_value() + "/devices/" 193 | + settings["device_id"].string_value(); 194 | 195 | if (isHttpSuccessful(performHttpRequest( { "DELETE", url, headers, nullptr } ), "retiring client")) 196 | { 197 | // remove old access key 198 | if (settings.find("Gears-Access-Key") != settings.end()) 199 | settings.erase(settings.find("Gears-Access-Key")); 200 | SaveSettings(settings); 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | int replaceSettings(const Json & parameters) 207 | { 208 | Json::object settings = parameters["setting-replace"].object_items(); 209 | SaveSettings(settings); 210 | return 0; 211 | } 212 | 213 | int updateSettings(const Json & parameters) 214 | { 215 | Json::object json = GetSettings(); 216 | JsonReplace(json, parameters["setting"].object_items()); 217 | JsonCleanNull(json); 218 | SaveSettings(json); 219 | return 0; 220 | } 221 | 222 | template 223 | int runParamters(T runFunction, const Json & parameters) 224 | { 225 | logWrite(LogLevel::info, 0, "Startup parameters", parameters); 226 | return runFunction(parameters); 227 | } 228 | 229 | enum clientOptionIndex { RETIRE = option::optionIndex::FIRST, UPDATE, REPLACE, DAEMON, CONF, LOG }; 230 | 231 | const option::Descriptor usage[] = 232 | { 233 | {UNKNOWN, 0, "" , "", JsonArg::None, "USAGE: gearsd [options]\n\n" 234 | "Options:" }, 235 | {HELP, 0, "" , "help", JsonArg::None, " --help \tPrint usage and exit." }, 236 | {RETIRE, 1, "", "retire", JsonArg::None, " --retire \tRetires gearsd from cloud." }, 237 | {UPDATE, 0, "", "setting", JsonArg::Required, " --setting {} \tAdds new json settings on startup." }, 238 | {DAEMON, 1, "", "run", JsonArg::None, " --run \tRun gearsd continuously." }, 239 | {CONF, 1, "", "setting-file", JsonArg::Required, " --setting-file \tSettings file." }, 240 | {0,0,0,0,0,0} 241 | }; 242 | 243 | int Run(int argc, char* argv[]) 244 | { 245 | Json cmdline = option::ParseCmdline(usage, argc, argv); 246 | 247 | if (!cmdline["error"].is_null()) 248 | { 249 | cout << cmdline["usage"].string_value(); 250 | cout << cmdline["error"].string_value(); 251 | return 1; 252 | } 253 | 254 | if (cmdline["parameters"]["setting-file"].is_string()) 255 | SetSettingsFile(cmdline["parameters"]["setting-file"].string_value()); 256 | else 257 | SetSettingsFile(GEARS_CONFIGURATION_FILE_PATH); 258 | 259 | SetLoggingLocation(GEARS_CONFIGURATION_LOGGING_DIR); 260 | SetLogLevel(GetSettings()["Log-Level"].string_value()); 261 | 262 | int ret = 1; 263 | if (cmdline["parameters"]["setting"].is_object()) 264 | { 265 | runParamters<>(updateSettings, cmdline["parameters"]); 266 | ret = 0; 267 | } 268 | 269 | if (cmdline["parameters"]["run"].bool_value()) 270 | return runParamters<>(runAgent, cmdline["parameters"]); 271 | else if (cmdline["parameters"]["retire"].bool_value()) 272 | return runParamters<>(retireClient, cmdline["parameters"]); 273 | 274 | if (ret != 0) 275 | { 276 | cout << cmdline["usage"].string_value(); 277 | return ret; 278 | } 279 | 280 | return 0; 281 | } 282 | 283 | int main(int argc, char* argv[]) 284 | { 285 | return Run(argc, argv); 286 | } 287 | -------------------------------------------------------------------------------- /client/Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef WIN32 4 | 5 | #define GEARS_CONFIGURATION_LOGGING_DIR "/var/log/gears/" 6 | #define GEARS_CONFIGURATION_FILE_DIR "/etc/gears/" 7 | #define GEARS_CONFIGURATION_FILE_NAME "gears.json" 8 | #define CURL_CA_BUNDLE NULL 9 | 10 | #else 11 | 12 | #define GEARS_CONFIGURATION_LOGGING_DIR "./" 13 | #define GEARS_CONFIGURATION_FILE_DIR "./" 14 | #define GEARS_CONFIGURATION_FILE_NAME "gears.json" 15 | #define CURL_CA_BUNDLE "curl-ca-bundle.crt" 16 | 17 | #endif 18 | 19 | #define GEARS_CONFIGURATION_FILE_PATH GEARS_CONFIGURATION_FILE_DIR GEARS_CONFIGURATION_FILE_NAME 20 | -------------------------------------------------------------------------------- /client/Conf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../utils/Logging.h" 4 | #include "../utils/json11/json11help.h" 5 | #include "Conf.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | using namespace json11; 13 | 14 | static Json::object jsonDefault = JsonParse(R"( 15 | { 16 | "Ping-Freq": 60, 17 | "Report-Freq": 3600 18 | } 19 | )").object_items(); 20 | 21 | 22 | static std::mutex mutexSettingsFile; 23 | static string settingsFile; 24 | static Json::object lastSettings; 25 | 26 | void SetSettingsFile(string s) 27 | { 28 | std::lock_guard guard(mutexSettingsFile); 29 | settingsFile = s; 30 | } 31 | 32 | bool SaveSettings(Json::object& settings) 33 | { 34 | std::lock_guard guard(mutexSettingsFile); 35 | 36 | if (settings.size() <= 0) 37 | return false; // don't wipe out file on {} object 38 | 39 | if (lastSettings == settings) 40 | return true; // being the same is not an error 41 | 42 | std::ofstream file(settingsFile); 43 | if (file) 44 | { 45 | file << Json(settings).dump(); 46 | file.close(); 47 | logWrite(LogLevel::info, 0, "Save settings", lastSettings = settings); // store new settings into lastSettings 48 | return true; 49 | } 50 | 51 | logError(string("Failed to open: ") << settingsFile << " msg: " << strerror(errno), errno); 52 | 53 | return false; 54 | } 55 | 56 | Json::object GetSettings() 57 | { 58 | std::lock_guard guard(mutexSettingsFile); 59 | 60 | std::ifstream file(settingsFile); 61 | 62 | if (file) 63 | { 64 | std::stringstream buffer; 65 | buffer << file.rdbuf(); 66 | file.close(); 67 | string err; 68 | Json::object json = Json::parse(buffer.str().c_str(), err).object_items(); 69 | 70 | if (err.length() <= 0) 71 | { 72 | json.insert(jsonDefault.begin(), jsonDefault.end()); 73 | if (lastSettings != json) 74 | logWrite(LogLevel::info, 0, "Settings", lastSettings = json); // store new settings into lastSettings 75 | return lastSettings; 76 | } 77 | else 78 | { 79 | logError("Failed to open: " << settingsFile << " json error: " << err, 0); 80 | } 81 | 82 | } 83 | else 84 | logError("Failed to open: " << settingsFile << " msg: " << strerror(errno), errno); 85 | 86 | // on error wait a default ping timeout for error resolution otherwise no other wait timeouts can be loaded 87 | std::this_thread::sleep_for(std::chrono::milliseconds(jsonDefault["Ping-Freq"].int_value() * 1000)); 88 | return Json().object_items(); 89 | } 90 | 91 | 92 | static string logDir; 93 | string GetLoggingLocation() 94 | { 95 | return logDir; 96 | } 97 | 98 | void SetLoggingLocation(string s) 99 | { 100 | logDir = s; 101 | } 102 | -------------------------------------------------------------------------------- /client/Conf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utils/json11/json11.hpp" 4 | 5 | std::string GetLoggingLocation(); 6 | void SetLoggingLocation(std::string s); 7 | 8 | void SetSettingsFile(std::string s); 9 | bool SaveSettings(json11::Json::object& settings); 10 | json11::Json::object GetSettings(); 11 | -------------------------------------------------------------------------------- /client/Detect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utils/json11/json11.hpp" 4 | #include "../utils/json11/json11help.h" 5 | #include "../utils/Logging.h" 6 | 7 | class Detect 8 | { 9 | public: 10 | static Detect* getInstance(); 11 | void release(); 12 | 13 | virtual json11::Json::object getDeviceInfo() = 0; 14 | virtual json11::Json::object getVersionInfo() = 0; 15 | virtual json11::Json::array getAntivirus() = 0; 16 | virtual json11::Json::array getFirewall() = 0; 17 | virtual json11::Json::array getHardDiskEncryption() = 0; 18 | virtual json11::Json::array getOsUpdateInfo() = 0; 19 | virtual json11::Json::array getVirtualMachine() = 0; 20 | 21 | virtual json11::Json::array getAntiphishing() = 0; 22 | virtual json11::Json::array getBackupClient() = 0; 23 | virtual json11::Json::array getPatchManagement() = 0; 24 | virtual json11::Json::array getPublicFileSharing() = 0; 25 | virtual json11::Json::array getBrowser() = 0; 26 | virtual json11::Json::array getInstantMessenger() = 0; 27 | virtual json11::Json::array getDataLossPrevention() = 0; 28 | virtual json11::Json::array getVpnClient() = 0; 29 | 30 | json11::Json::object getFullReport() 31 | { 32 | json11::Json::array applications; 33 | JsonAppend(applications, getAntivirus()); 34 | JsonAppend(applications, getFirewall()); 35 | JsonAppend(applications, getOsUpdateInfo()); 36 | JsonAppend(applications, getVirtualMachine()); 37 | JsonAppend(applications, getAntiphishing()); 38 | JsonAppend(applications, getBackupClient()); 39 | JsonAppend(applications, getPatchManagement()); 40 | JsonAppend(applications, getPublicFileSharing()); 41 | JsonAppend(applications, getBrowser()); 42 | JsonAppend(applications, getInstantMessenger()); 43 | JsonAppend(applications, getDataLossPrevention()); 44 | JsonAppend(applications, getVpnClient()); 45 | 46 | json11::Json::object deviceInfo = getDeviceInfo(); 47 | 48 | #if __linux__ 49 | // special modification to linux soh to move "os" section into root and add section "system" 50 | JsonReplace(deviceInfo, deviceInfo["os"].object_items()); 51 | deviceInfo["os"] = nullptr; 52 | JsonCleanNull(deviceInfo); 53 | deviceInfo["system"] = json11::Json::object {}; 54 | #endif 55 | json11::Json::object soh{ { 56 | "soh", json11::Json::object{ 57 | { "os_info", json11::Json::array{ deviceInfo } }, 58 | { "applications", applications } 59 | } 60 | } }; 61 | 62 | auto version = getVersionInfo(); 63 | soh.insert(version.begin(), version.end()); 64 | 65 | logWrite(LogLevel::info, 0, "Completed detection", nullptr); 66 | 67 | return soh; 68 | } 69 | 70 | 71 | protected: 72 | Detect() {}; 73 | virtual ~Detect() {}; 74 | static Detect* m_Instance; 75 | }; 76 | -------------------------------------------------------------------------------- /client/Detect_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../utils/json11/json11help.h" 4 | #include "../utils/Logging.h" 5 | #include "../utils/StringUtils.h" 6 | 7 | using namespace std; 8 | using namespace json11; 9 | 10 | #include "Detect.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | 65 | #ifdef NO_USER_INFO 66 | 67 | std::string getCurrentUser(); 68 | std::string getHostname(); 69 | 70 | string getCurrentUser() 71 | { 72 | char* name = NULL; 73 | struct utmpx* entry = NULL; 74 | 75 | while ((entry = getutxent()) != NULL) 76 | { 77 | if (strcmp(entry->ut_line, ":0") == 0) // tty=":0" maybe the first user logged in to system 78 | name = entry->ut_user; 79 | } 80 | 81 | endutxent(); 82 | 83 | return name ? string(name) : ""; 84 | } 85 | 86 | enum class isUserPasswordSetReturn { unknown, yes, no}; 87 | isUserPasswordSetReturn isUserPasswordSet(string username) 88 | { 89 | struct spwd* shadow_entry; 90 | 91 | /* Read the correct hash from the shadow entry */ 92 | shadow_entry = getspnam(username.c_str()); 93 | if (shadow_entry == NULL) 94 | { 95 | logMessage("shadow info is not available"); 96 | return isUserPasswordSetReturn::unknown; 97 | } 98 | 99 | if (strcmp(shadow_entry->sp_pwdp, "") == 0) 100 | return isUserPasswordSetReturn::yes; 101 | else 102 | return isUserPasswordSetReturn::no; 103 | } 104 | 105 | #endif 106 | 107 | string getHostname() 108 | { 109 | char name[1024]; 110 | int rc = gethostname(name, 1024); 111 | if (rc == 0) 112 | return string(name); 113 | else 114 | { 115 | logError("error getting host name", rc); 116 | return ""; 117 | } 118 | } 119 | 120 | 121 | struct networkAdapter { 122 | std::string mac; 123 | std::vector ipv4; 124 | std::vector maskV4; 125 | std::vector ipv6; 126 | std::vector maskV6; 127 | bool adapter_enabled; 128 | std::string media_state; 129 | }; 130 | 131 | typedef std::map Adapters; 132 | 133 | Adapters getNetworkAdapters() 134 | { 135 | Adapters adapters; 136 | 137 | struct ifaddrs* ifaddr; 138 | if (getifaddrs(&ifaddr) == -1) 139 | return adapters; 140 | 141 | for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 142 | { 143 | if (ifa->ifa_addr == NULL 144 | || 0 != (ifa->ifa_flags & IFF_LOOPBACK) // filters loopback interfaces (lo0) 145 | || 0 != (ifa->ifa_flags & IFF_POINTOPOINT) // filters tunnel software network interface (gif0) 146 | || ifa->ifa_flags == 0) // filters 6to4 tunnel interface (stf0)) 147 | { 148 | continue; 149 | } 150 | 151 | struct networkAdapter& adapter = adapters[ifa->ifa_name]; 152 | 153 | if (ifa->ifa_addr->sa_family == AF_PACKET) 154 | { 155 | struct sockaddr_ll* sll = (struct sockaddr_ll*) ifa->ifa_addr; 156 | char mac[18]; 157 | if (sll->sll_halen == 6) 158 | snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x", sll->sll_addr[0], sll->sll_addr[1], sll->sll_addr[2], sll->sll_addr[3], 159 | sll->sll_addr[4], sll->sll_addr[5]); 160 | else //if address length is greater than 6, it's a firewire port 161 | continue; 162 | 163 | adapter.mac = mac; 164 | } 165 | else if (ifa->ifa_addr->sa_family == AF_INET) 166 | { 167 | char host[NI_MAXHOST]; 168 | int ret = getnameinfo(ifa->ifa_addr, sizeof (struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 169 | if (ret != 0) { 170 | logError("getnameinfo() failed: " << gai_strerror(ret), ret); 171 | continue; 172 | } 173 | std::string sIPV4(host); 174 | adapter.ipv4.push_back(sIPV4); 175 | 176 | struct sockaddr_in* sa = (struct sockaddr_in*) ifa->ifa_netmask; 177 | adapter.maskV4.push_back(inet_ntoa(sa->sin_addr)); 178 | } 179 | else if (ifa->ifa_addr->sa_family == AF_INET6) 180 | { 181 | char host[NI_MAXHOST]; 182 | int ret = getnameinfo(ifa->ifa_addr, sizeof (struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST | AI_PASSIVE); 183 | if (ret != 0) 184 | { 185 | logError("getnameinfo() failed: " << gai_strerror(ret), ret); 186 | continue; 187 | } 188 | std::string ip(host); 189 | // strip the zone-id off if exists 190 | if (ip.find_first_of("%") != std::string::npos) 191 | ip = ip.substr(0, ip.find_first_of("%")); 192 | adapter.ipv6.push_back(ip); 193 | 194 | //adapter.maskv6; //ignored 195 | } 196 | else 197 | continue; 198 | 199 | // detect adapter_enabled and media_state 200 | int socId = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 201 | if (socId < 0) 202 | continue; 203 | 204 | struct ifreq if_req; 205 | strncpy(if_req.ifr_name, ifa->ifa_name, sizeof (if_req.ifr_name)); 206 | int rv = ioctl(socId, SIOCGIFFLAGS, &if_req); 207 | close(socId); 208 | 209 | if (rv != -1) 210 | { 211 | adapter.adapter_enabled = (if_req.ifr_flags & IFF_UP) && (if_req.ifr_flags & IFF_RUNNING); 212 | 213 | if (adapter.adapter_enabled) 214 | adapter.media_state = "connected"; 215 | else 216 | adapter.media_state = "disconnected"; 217 | } 218 | } 219 | 220 | freeifaddrs(ifaddr); 221 | 222 | return adapters; 223 | } 224 | 225 | class DetectLinux : public Detect 226 | { 227 | virtual Json::object getDeviceInfo() override; 228 | virtual Json::object getVersionInfo() override; 229 | virtual Json::array getAntivirus() override { return Json::array(); } 230 | virtual Json::array getFirewall() override { return Json::array(); } 231 | virtual Json::array getHardDiskEncryption() override { return Json::array(); } 232 | virtual Json::array getOsUpdateInfo() override { return Json::array(); } 233 | virtual Json::array getVirtualMachine() override { return Json::array(); } 234 | 235 | virtual Json::array getAntiphishing() override { return Json::array(); } 236 | virtual Json::array getBackupClient() override { return Json::array(); } 237 | virtual Json::array getPatchManagement() override { return Json::array(); } 238 | virtual Json::array getPublicFileSharing() override { return Json::array(); } 239 | virtual Json::array getBrowser() override { return Json::array(); } 240 | virtual Json::array getInstantMessenger() override { return Json::array(); } 241 | virtual Json::array getDataLossPrevention() override { return Json::array(); } 242 | virtual Json::array getVpnClient() override { return Json::array(); } 243 | 244 | public: 245 | DetectLinux() {} 246 | ~DetectLinux() {} 247 | 248 | }; 249 | 250 | /*static*/ Detect* Detect::getInstance() 251 | { 252 | if (m_Instance == NULL) 253 | m_Instance = new DetectLinux(); 254 | 255 | return m_Instance; 256 | } 257 | 258 | void Detect::release() { } 259 | 260 | /*static*/ Detect* Detect::m_Instance = NULL; 261 | 262 | Json::array getNetworkAdaptersInfo() 263 | { 264 | Json::array aAdapterArrays; 265 | map mAdapters; 266 | auto adapters = getNetworkAdapters(); 267 | if (!adapters.empty()) 268 | { 269 | for (auto adapter : adapters) 270 | { 271 | Json::object adapterObject; 272 | 273 | adapterObject["description"] = adapter.first; 274 | adapterObject["mac"] = adapter.second.mac; 275 | adapterObject["ipv4"] = adapter.second.ipv4; 276 | adapterObject["ipv6"] = adapter.second.ipv6; 277 | adapterObject["adapter_enabled"] = adapter.second.adapter_enabled; 278 | adapterObject["media_state"] = adapter.second.media_state; 279 | string mask = adapter.second.maskV4.empty() ? "" : adapter.second.maskV4.front(); 280 | adapterObject["subnet_masks"] = Json::array{ Json::object{ { "mask", mask } } }; 281 | 282 | aAdapterArrays.push_back(adapterObject); 283 | } 284 | } 285 | 286 | return aAdapterArrays; 287 | } 288 | 289 | Json::object DetectLinux::getDeviceInfo() 290 | { 291 | Json::object device; 292 | Json::object osDetails; 293 | 294 | 295 | osDetails["family"] = "linux"; 296 | JsonCheck(osDetails, "version", ""); 297 | JsonCheck(osDetails, "architecture", ""); 298 | JsonCheck(osDetails, "vendor", ""); 299 | JsonCheck(osDetails, "name", "Linux"); 300 | osDetails["language"] = ""; 301 | 302 | #ifdef NO_USER_INFO 303 | std::string username = getCurrentUser(); 304 | if (!username.empty()) 305 | { 306 | auto isPassSet = isUserPasswordSet(username); 307 | if (isPassSet != isUserPasswordSetReturn::unknown) 308 | osDetails["user_password_set"] = isPassSet == isUserPasswordSetReturn::yes; 309 | device["local_user"] = Json::array{ Json::object{ {"name", username} } }; 310 | } 311 | #endif 312 | 313 | device["os"] = osDetails; 314 | device["device_type"] = "desktop"; 315 | device["network_adapter_info"] = getNetworkAdaptersInfo(); 316 | 317 | 318 | return device; 319 | } 320 | 321 | Json::object DetectLinux::getVersionInfo() 322 | { 323 | string version; 324 | 325 | return Json::object({ 326 | {"agent_type", "persistent"}, 327 | {"device_name", getHostname()}, 328 | {"hostname", getHostname()}, 329 | {"scan_time", iso_time()}, 330 | {"core_version", version}, 331 | }); 332 | } 333 | -------------------------------------------------------------------------------- /gears.json: -------------------------------------------------------------------------------- 1 | { 2 | "Report-Freq": 600, 3 | "Gears-Server": "https://devices.opswatgears.com", 4 | "agent_version": "0.7" 5 | } 6 | -------------------------------------------------------------------------------- /nbproject/Makefile-Debug.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a -pre and a -post target defined where you can add customized code. 6 | # 7 | # This makefile implements configuration specific macros and targets. 8 | 9 | 10 | # Environment 11 | MKDIR=mkdir 12 | CP=cp 13 | GREP=grep 14 | NM=nm 15 | CCADMIN=CCadmin 16 | RANLIB=ranlib 17 | CC=gcc 18 | CCC=g++ 19 | CXX=g++ 20 | FC=gfortran 21 | AS=as 22 | 23 | # Macros 24 | CND_PLATFORM=GNU-Linux-x86 25 | CND_DLIB_EXT=so 26 | CND_CONF=Debug 27 | CND_DISTDIR=dist 28 | CND_BUILDDIR=build 29 | 30 | # Include project Makefile 31 | include Makefile 32 | 33 | # Object Directory 34 | OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM} 35 | 36 | # Object Files 37 | OBJECTFILES= \ 38 | ${OBJECTDIR}/client/Client.o \ 39 | ${OBJECTDIR}/client/Conf.o \ 40 | ${OBJECTDIR}/client/Detect_linux.o \ 41 | ${OBJECTDIR}/utils/EzCurl.o \ 42 | ${OBJECTDIR}/utils/json11/json11.o 43 | 44 | 45 | # C Compiler Flags 46 | CFLAGS= 47 | 48 | # CC Compiler Flags 49 | CCFLAGS=-Wall -Wvla -Wno-deprecated -Werror 50 | CXXFLAGS=-Wall -Wvla -Wno-deprecated -Werror 51 | 52 | # Fortran Compiler Flags 53 | FFLAGS= 54 | 55 | # Assembler Flags 56 | ASFLAGS= 57 | 58 | # Link Libraries and Options 59 | LDLIBSOPTIONS=-lstdc++ -Wl,--no-as-needed -lcurl -pthread 60 | 61 | # Build Targets 62 | .build-conf: ${BUILD_SUBPROJECTS} 63 | "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ./gearsd 64 | 65 | ./gearsd: ${OBJECTFILES} 66 | ${MKDIR} -p . 67 | g++ -o ./gearsd ${OBJECTFILES} ${LDLIBSOPTIONS} 68 | 69 | ${OBJECTDIR}/client/Client.o: client/Client.cpp 70 | ${MKDIR} -p ${OBJECTDIR}/client 71 | ${RM} "$@.d" 72 | $(COMPILE.cc) -g -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Client.o client/Client.cpp 73 | 74 | ${OBJECTDIR}/client/Conf.o: client/Conf.cpp 75 | ${MKDIR} -p ${OBJECTDIR}/client 76 | ${RM} "$@.d" 77 | $(COMPILE.cc) -g -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Conf.o client/Conf.cpp 78 | 79 | ${OBJECTDIR}/client/Detect_linux.o: client/Detect_linux.cpp 80 | ${MKDIR} -p ${OBJECTDIR}/client 81 | ${RM} "$@.d" 82 | $(COMPILE.cc) -g -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Detect_linux.o client/Detect_linux.cpp 83 | 84 | ${OBJECTDIR}/utils/EzCurl.o: utils/EzCurl.cpp 85 | ${MKDIR} -p ${OBJECTDIR}/utils 86 | ${RM} "$@.d" 87 | $(COMPILE.cc) -g -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/utils/EzCurl.o utils/EzCurl.cpp 88 | 89 | ${OBJECTDIR}/utils/json11/json11.o: utils/json11/json11.cpp 90 | ${MKDIR} -p ${OBJECTDIR}/utils/json11 91 | ${RM} "$@.d" 92 | $(COMPILE.cc) -g -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/utils/json11/json11.o utils/json11/json11.cpp 93 | 94 | # Subprojects 95 | .build-subprojects: 96 | 97 | # Clean Targets 98 | .clean-conf: ${CLEAN_SUBPROJECTS} 99 | ${RM} -r ${CND_BUILDDIR}/${CND_CONF} 100 | ${RM} ./gearsd 101 | 102 | # Subprojects 103 | .clean-subprojects: 104 | 105 | # Enable dependency checking 106 | .dep.inc: .depcheck-impl 107 | 108 | include .dep.inc 109 | -------------------------------------------------------------------------------- /nbproject/Makefile-Release.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a -pre and a -post target defined where you can add customized code. 6 | # 7 | # This makefile implements configuration specific macros and targets. 8 | 9 | 10 | # Environment 11 | MKDIR=mkdir 12 | CP=cp 13 | GREP=grep 14 | NM=nm 15 | CCADMIN=CCadmin 16 | RANLIB=ranlib 17 | CC=gcc 18 | CCC=g++ 19 | CXX=g++ 20 | FC=gfortran 21 | AS=as 22 | 23 | # Macros 24 | CND_PLATFORM=GNU-Linux-x86 25 | CND_DLIB_EXT=so 26 | CND_CONF=Release 27 | CND_DISTDIR=dist 28 | CND_BUILDDIR=build 29 | 30 | # Include project Makefile 31 | include Makefile 32 | 33 | # Object Directory 34 | OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM} 35 | 36 | # Object Files 37 | OBJECTFILES= \ 38 | ${OBJECTDIR}/client/Client.o \ 39 | ${OBJECTDIR}/client/Conf.o \ 40 | ${OBJECTDIR}/client/Detect_linux.o \ 41 | ${OBJECTDIR}/utils/EzCurl.o \ 42 | ${OBJECTDIR}/utils/json11/json11.o 43 | 44 | 45 | # C Compiler Flags 46 | CFLAGS= 47 | 48 | # CC Compiler Flags 49 | CCFLAGS=-Wall -Wvla -Wno-deprecated -Werror 50 | CXXFLAGS=-Wall -Wvla -Wno-deprecated -Werror 51 | 52 | # Fortran Compiler Flags 53 | FFLAGS= 54 | 55 | # Assembler Flags 56 | ASFLAGS= 57 | 58 | # Link Libraries and Options 59 | LDLIBSOPTIONS=-lstdc++ -Wl,--no-as-needed -lcurl -pthread 60 | 61 | # Build Targets 62 | .build-conf: ${BUILD_SUBPROJECTS} 63 | "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ./gearsd 64 | 65 | ./gearsd: ${OBJECTFILES} 66 | ${MKDIR} -p . 67 | ${LINK.cc} -o ./gearsd ${OBJECTFILES} ${LDLIBSOPTIONS} -s 68 | 69 | ${OBJECTDIR}/client/Client.o: client/Client.cpp 70 | ${MKDIR} -p ${OBJECTDIR}/client 71 | ${RM} "$@.d" 72 | $(COMPILE.cc) -O2 -s -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Client.o client/Client.cpp 73 | 74 | ${OBJECTDIR}/client/Conf.o: client/Conf.cpp 75 | ${MKDIR} -p ${OBJECTDIR}/client 76 | ${RM} "$@.d" 77 | $(COMPILE.cc) -O2 -s -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Conf.o client/Conf.cpp 78 | 79 | ${OBJECTDIR}/client/Detect_linux.o: client/Detect_linux.cpp 80 | ${MKDIR} -p ${OBJECTDIR}/client 81 | ${RM} "$@.d" 82 | $(COMPILE.cc) -O2 -s -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/client/Detect_linux.o client/Detect_linux.cpp 83 | 84 | ${OBJECTDIR}/utils/EzCurl.o: utils/EzCurl.cpp 85 | ${MKDIR} -p ${OBJECTDIR}/utils 86 | ${RM} "$@.d" 87 | $(COMPILE.cc) -O2 -s -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/utils/EzCurl.o utils/EzCurl.cpp 88 | 89 | ${OBJECTDIR}/utils/json11/json11.o: utils/json11/json11.cpp 90 | ${MKDIR} -p ${OBJECTDIR}/utils/json11 91 | ${RM} "$@.d" 92 | $(COMPILE.cc) -O2 -s -Iprecomp -std=c++11 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/utils/json11/json11.o utils/json11/json11.cpp 93 | 94 | # Subprojects 95 | .build-subprojects: 96 | 97 | # Clean Targets 98 | .clean-conf: ${CLEAN_SUBPROJECTS} 99 | ${RM} -r ${CND_BUILDDIR}/${CND_CONF} 100 | ${RM} ./gearsd 101 | 102 | # Subprojects 103 | .clean-subprojects: 104 | 105 | # Enable dependency checking 106 | .dep.inc: .depcheck-impl 107 | 108 | include .dep.inc 109 | -------------------------------------------------------------------------------- /nbproject/Makefile-impl.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a pre- and a post- target defined where you can add customization code. 6 | # 7 | # This makefile implements macros and targets common to all configurations. 8 | # 9 | # NOCDDL 10 | 11 | 12 | # Building and Cleaning subprojects are done by default, but can be controlled with the SUB 13 | # macro. If SUB=no, subprojects will not be built or cleaned. The following macro 14 | # statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf 15 | # and .clean-reqprojects-conf unless SUB has the value 'no' 16 | SUB_no=NO 17 | SUBPROJECTS=${SUB_${SUB}} 18 | BUILD_SUBPROJECTS_=.build-subprojects 19 | BUILD_SUBPROJECTS_NO= 20 | BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}} 21 | CLEAN_SUBPROJECTS_=.clean-subprojects 22 | CLEAN_SUBPROJECTS_NO= 23 | CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} 24 | 25 | 26 | # Project Name 27 | PROJECTNAME=grsc-linux-client 28 | 29 | # Active Configuration 30 | DEFAULTCONF=Debug 31 | CONF=${DEFAULTCONF} 32 | 33 | # All Configurations 34 | ALLCONFS=Debug Release 35 | 36 | 37 | # build 38 | .build-impl: .build-pre .validate-impl .depcheck-impl 39 | @#echo "=> Running $@... Configuration=$(CONF)" 40 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf 41 | 42 | 43 | # clean 44 | .clean-impl: .clean-pre .validate-impl .depcheck-impl 45 | @#echo "=> Running $@... Configuration=$(CONF)" 46 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf 47 | 48 | 49 | # clobber 50 | .clobber-impl: .clobber-pre .depcheck-impl 51 | @#echo "=> Running $@..." 52 | for CONF in ${ALLCONFS}; \ 53 | do \ 54 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \ 55 | done 56 | 57 | # all 58 | .all-impl: .all-pre .depcheck-impl 59 | @#echo "=> Running $@..." 60 | for CONF in ${ALLCONFS}; \ 61 | do \ 62 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \ 63 | done 64 | 65 | # build tests 66 | .build-tests-impl: .build-impl .build-tests-pre 67 | @#echo "=> Running $@... Configuration=$(CONF)" 68 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf 69 | 70 | # run tests 71 | .test-impl: .build-tests-impl .test-pre 72 | @#echo "=> Running $@... Configuration=$(CONF)" 73 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf 74 | 75 | # dependency checking support 76 | .depcheck-impl: 77 | @echo "# This code depends on make tool being used" >.dep.inc 78 | @if [ -n "${MAKE_VERSION}" ]; then \ 79 | echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \ 80 | echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \ 81 | echo "include \$${DEPFILES}" >>.dep.inc; \ 82 | echo "endif" >>.dep.inc; \ 83 | else \ 84 | echo ".KEEP_STATE:" >>.dep.inc; \ 85 | echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \ 86 | fi 87 | 88 | # configuration validation 89 | .validate-impl: 90 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 91 | then \ 92 | echo ""; \ 93 | echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \ 94 | echo "See 'make help' for details."; \ 95 | echo "Current directory: " `pwd`; \ 96 | echo ""; \ 97 | fi 98 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 99 | then \ 100 | exit 1; \ 101 | fi 102 | 103 | 104 | # help 105 | .help-impl: .help-pre 106 | @echo "This makefile supports the following configurations:" 107 | @echo " ${ALLCONFS}" 108 | @echo "" 109 | @echo "and the following targets:" 110 | @echo " build (default target)" 111 | @echo " clean" 112 | @echo " clobber" 113 | @echo " all" 114 | @echo " help" 115 | @echo "" 116 | @echo "Makefile Usage:" 117 | @echo " make [CONF=] [SUB=no] build" 118 | @echo " make [CONF=] [SUB=no] clean" 119 | @echo " make [SUB=no] clobber" 120 | @echo " make [SUB=no] all" 121 | @echo " make help" 122 | @echo "" 123 | @echo "Target 'build' will build a specific configuration and, unless 'SUB=no'," 124 | @echo " also build subprojects." 125 | @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no'," 126 | @echo " also clean subprojects." 127 | @echo "Target 'clobber' will remove all built files from all configurations and," 128 | @echo " unless 'SUB=no', also from subprojects." 129 | @echo "Target 'all' will will build all configurations and, unless 'SUB=no'," 130 | @echo " also build subprojects." 131 | @echo "Target 'help' prints this message." 132 | @echo "" 133 | 134 | -------------------------------------------------------------------------------- /nbproject/Makefile-variables.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated - do not edit! 3 | # 4 | # NOCDDL 5 | # 6 | CND_BASEDIR=`pwd` 7 | CND_BUILDDIR=build 8 | CND_DISTDIR=dist 9 | # Debug configuration 10 | CND_PLATFORM_Debug=GNU-Linux-x86 11 | CND_ARTIFACT_DIR_Debug=. 12 | CND_ARTIFACT_NAME_Debug=gearsd 13 | CND_ARTIFACT_PATH_Debug=./gearsd 14 | CND_PACKAGE_DIR_Debug=dist/Debug/GNU-Linux-x86/package 15 | CND_PACKAGE_NAME_Debug=grsc-linux-client.tar 16 | CND_PACKAGE_PATH_Debug=dist/Debug/GNU-Linux-x86/package/grsc-linux-client.tar 17 | # Release configuration 18 | CND_PLATFORM_Release=GNU-Linux-x86 19 | CND_ARTIFACT_DIR_Release=. 20 | CND_ARTIFACT_NAME_Release=gearsd 21 | CND_ARTIFACT_PATH_Release=./gearsd 22 | CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux-x86/package 23 | CND_PACKAGE_NAME_Release=grsc-linux-client.tar 24 | CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux-x86/package/grsc-linux-client.tar 25 | # 26 | # include compiler specific variables 27 | # 28 | # dmake command 29 | ROOT:sh = test -f nbproject/private/Makefile-variables.mk || \ 30 | (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk) 31 | # 32 | # gmake command 33 | .PHONY: $(shell test -f nbproject/private/Makefile-variables.mk || (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk)) 34 | # 35 | include nbproject/private/Makefile-variables.mk 36 | -------------------------------------------------------------------------------- /nbproject/configurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | client/Client.cpp 6 | client/Client.h 7 | client/Conf.cpp 8 | client/Conf.h 9 | client/Detect.h 10 | client/Detect_linux.cpp 11 | 12 | 13 | precomp/precomp.h 14 | 15 | 16 | 17 | utils/json11/compat.h 18 | utils/json11/json11.cpp 19 | utils/json11/json11.hpp 20 | utils/json11/json11help.h 21 | 22 | 23 | utils/os_spec/StringUtils_linux.h 24 | 25 | utils/EzCurl.cpp 26 | utils/EzCurl.h 27 | utils/Logging.h 28 | utils/StringUtils.h 29 | utils/Timer.h 30 | 31 | 35 | 36 | 40 | Makefile 41 | 42 | 43 | 44 | . 45 | 46 | Makefile 47 | 48 | 49 | 50 | GNU|GNU 51 | true 52 | false 53 | 54 | 55 | 56 | 8 57 | g++ 58 | 59 | precomp 60 | 61 | -Wall -Wvla -Wno-deprecated -Werror 62 | 63 | 64 | ./gearsd 65 | g++ 66 | 67 | -lstdc++ -Wl,--no-as-needed 68 | -lcurl 69 | -pthread 70 | 71 | 72 | 73 | 74 | Tar 75 | --buildroot ${TOP}/${NBTMPDIR} 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | default 123 | true 124 | false 125 | 126 | 127 | 128 | 5 129 | 130 | 131 | 5 132 | true 133 | 8 134 | 135 | precomp 136 | 137 | -Wall -Wvla -Wno-deprecated -Werror 138 | 139 | 140 | 5 141 | 142 | 143 | 5 144 | 145 | 146 | ./gearsd 147 | true 148 | 149 | -lstdc++ -Wl,--no-as-needed 150 | -lcurl 151 | -pthread 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.cnd.makeproject 4 | 5 | 6 | GearsClient 7 | 8 | cpp 9 | h,hpp 10 | UTF-8 11 | 12 | 13 | . 14 | 15 | 16 | 17 | Debug 18 | 1 19 | 20 | 21 | Release 22 | 1 23 | 24 | 25 | 26 | false 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /precomp/precomp.cpp: -------------------------------------------------------------------------------- 1 | #include "precomp.h" 2 | 3 | -------------------------------------------------------------------------------- /precomp/precomp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _MSC_VER 4 | #include "..\vc\targetver.h" 5 | #include 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "../utils/json11/json11.hpp" 33 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | # !/bin/sh 2 | # 3 | 4 | Usage="Usage: run " 5 | if [ $# -ne 1 ] 6 | then 7 | echo "$Usage" 8 | exit 1 9 | fi 10 | 11 | ./gearsd --setting "{\"Gears-Auth\":\"$1\",\"Log-Level\":\"trace\"}" --setting-file ./gears.json 12 | ./gearsd --run --setting-file ./gears.json 13 | 14 | -------------------------------------------------------------------------------- /utils/EzCurl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "EzCurl.h" 4 | #include "Logging.h" 5 | #include "StringUtils.h" 6 | 7 | using namespace std; 8 | 9 | namespace Curl 10 | { 11 | 12 | #ifdef _MSC_VER 13 | #include 14 | #else 15 | #include 16 | #endif 17 | 18 | template 19 | CURLcode setopt(CURL *curl, CURLoption option, T v) 20 | { 21 | return curl_easy_setopt(curl, option, static_cast(v)); 22 | } 23 | 24 | static size_t header_callback(char * buffer, size_t size, size_t nitems, void * outstream) 25 | { 26 | if (outstream != NULL) 27 | { 28 | string header = buffer; 29 | size_t sp = header.find(':'); 30 | string name; 31 | if (sp != string::npos) 32 | name = header.substr(0, sp); 33 | 34 | string value = header.substr(sp != string::npos ? sp : 0); 35 | 36 | if (sp == string::npos) 37 | { 38 | size_t skip = value.find(' ') + 1; 39 | value = value.substr(skip); 40 | skip = value.find(' ') + 1; 41 | value = value.substr(skip); 42 | } 43 | else 44 | value = value.substr(1); 45 | 46 | value = trim(value); 47 | 48 | if (!name.empty() || !value.empty()) 49 | { 50 | headers_t& headers = *reinterpret_cast(outstream); 51 | headers[name] = value; 52 | } 53 | } 54 | return size * nitems; 55 | } 56 | 57 | static size_t write_callback(char * buffer, size_t size, size_t nitems, void * outstream) 58 | { 59 | if (outstream != NULL) 60 | { 61 | std::string* pStrUserData = reinterpret_cast(outstream); 62 | *pStrUserData += std::string(buffer).substr(0, size * nitems); // will truncate if ptr is binary with null char (don't execpt binary reponse) 63 | } 64 | return size * nitems; 65 | } 66 | 67 | void CurlLib::Init() 68 | { 69 | curl_global_init(CURL_GLOBAL_ALL); 70 | } 71 | 72 | /*static*/ CurlLib* CurlLib::getInstance() 73 | { 74 | if (m_Instance == NULL) 75 | { 76 | m_Instance = new CurlLib(); 77 | m_Instance->Init(); 78 | } 79 | return m_Instance; 80 | } 81 | 82 | /*static*/ void CurlLib::destroyInstance() 83 | { 84 | if (m_Instance) 85 | { 86 | m_Instance->Deinit(); 87 | delete m_Instance; 88 | m_Instance = NULL; 89 | } 90 | } 91 | 92 | void CurlLib::Request(httpRequest& req) 93 | { 94 | if (m_Instance == NULL) 95 | return; 96 | 97 | if (sizeof(req.error) < CURL_ERROR_SIZE) 98 | { 99 | string msg = "httpRequest::error smaller than CURL_ERROR_SIZE"; 100 | std::copy(msg.begin(), msg.end(), req.error); 101 | req.errorCode = -3; 102 | return; 103 | } 104 | 105 | CURL* curl_handle = curl_easy_init(); 106 | if (curl_handle == NULL) 107 | { 108 | string msg = "curl_easy_init failed"; 109 | std::copy(msg.begin(), msg.end(), req.error); 110 | req.errorCode = -2; 111 | return; 112 | } 113 | 114 | setopt(curl_handle, CURLOPT_URL, req.url.c_str()); 115 | 116 | if (req.request.body.length() > 0 && req.method != "GET" && req.method != "HEAD") 117 | { 118 | setopt(curl_handle, CURLOPT_POST, 1); 119 | setopt(curl_handle, CURLOPT_POSTFIELDS, req.request.body.c_str()); 120 | setopt(curl_handle, CURLOPT_POSTFIELDSIZE, (curl_off_t)req.request.body.length()); 121 | } 122 | 123 | setopt(curl_handle, CURLOPT_CUSTOMREQUEST, req.method.c_str()); 124 | 125 | //Set Custom Headers, Required for Non URL Encoded POST - ie. Content-Type: text/html 126 | curl_slist * header_list = NULL; 127 | if (!req.request.headers.empty()) { 128 | for (auto i : req.request.headers) { 129 | header_list = curl_slist_append(header_list, (i.first + ": " + i.second).c_str()); 130 | } 131 | setopt(curl_handle, CURLOPT_HTTPHEADER, header_list); 132 | } 133 | 134 | // Https certificate verification 135 | setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, m_bVerifyPeer); 136 | setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, m_bVerifyPeer?2:0); // should be default but make sure 137 | 138 | if (m_certDir != NULL) 139 | setopt(curl_handle, CURLOPT_CAINFO, m_certDir); 140 | 141 | setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); 142 | setopt(curl_handle, CURLOPT_WRITEDATA, &req.response.body); 143 | setopt(curl_handle, CURLOPT_HEADERDATA, &req.response.headers); 144 | setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); 145 | 146 | setopt(curl_handle, CURLOPT_ERRORBUFFER, req.error); 147 | setopt(curl_handle, CURLOPT_TIMEOUT, req.totalTimeoutSec); // Set the timeout of the whole operation, (default is 0, which means no timeout) 148 | setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, req.connectTimeoutSec); // Set the timeout for connecting to the server. (default is 0, which uses 300 seconds) 149 | req.errorCode = curl_easy_perform(curl_handle); 150 | 151 | if (req.errorCode == 0) 152 | curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &req.respCode); 153 | 154 | if (header_list != NULL) 155 | curl_slist_free_all(header_list); 156 | 157 | curl_easy_cleanup(curl_handle); 158 | } 159 | 160 | /*static*/ CurlLib* CurlLib::m_Instance = NULL; 161 | 162 | } //namespace Curl 163 | 164 | 165 | -------------------------------------------------------------------------------- /utils/EzCurl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Curl 6 | { 7 | 8 | typedef std::map headers_t; 9 | 10 | struct httpEnity 11 | { 12 | headers_t headers; 13 | std::string body; 14 | }; 15 | 16 | struct httpRequest 17 | { 18 | std::string method; 19 | std::string url; 20 | struct httpEnity request; 21 | struct httpEnity response; // response.headers[""] is Reason-Phrase 22 | char error[512]; // native curl error text 23 | uint32_t errorCode; // native curl error code 24 | uint16_t respCode; // http response code 25 | uint16_t connectTimeoutSec; 26 | uint16_t totalTimeoutSec; 27 | }; 28 | 29 | class CurlLib 30 | { 31 | const char* m_certDir = NULL; 32 | bool m_bVerifyPeer = true; 33 | 34 | CurlLib() {} 35 | ~CurlLib() {} 36 | 37 | static CurlLib* m_Instance; 38 | void Init(); 39 | void Deinit() {} 40 | 41 | public: 42 | static CurlLib* getInstance(); 43 | static void destroyInstance(); 44 | 45 | void Request(httpRequest& req); 46 | }; 47 | 48 | inline uint8_t getStausCodeClass(uint16_t respCode) 49 | { 50 | return respCode / 100; 51 | } 52 | 53 | inline bool isStatusCode200s(uint16_t respCode) 54 | { 55 | return getStausCodeClass(respCode) == 2; 56 | } 57 | 58 | inline bool isStatusCodeError(uint16_t respCode) 59 | { 60 | uint8_t statusClass = getStausCodeClass(respCode); 61 | return statusClass == 4 || statusClass == 5; 62 | } 63 | 64 | } //namespace Curl 65 | -------------------------------------------------------------------------------- /utils/Logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "StringUtils.h" 4 | #include "json11/json11.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | enum class LogLevel { 11 | trace, 12 | debug, 13 | info, 14 | warn, 15 | error, 16 | fatal, 17 | none 18 | }; 19 | 20 | template 21 | struct LogData; 22 | 23 | #ifndef NO_ENABLE_TRACE 24 | #define traceJson(msg, json) (LogImpl( __FILE__, __LINE__, LogData() << msg, LogLevel::trace, 0, json)) 25 | #define traceInfo(msg, num) (LogImpl( __FILE__, __LINE__, LogData() << msg, LogLevel::trace, num)) 26 | #else 27 | #define traceMessage(msg) void() 28 | #define traceInfo(msg, num) void() 29 | #endif 30 | 31 | #define logMessage(msg) (LogImpl( __FILE__, __LINE__, LogData() << msg, LogLevel::debug, 0)) 32 | #define logWrite(level, num, msg, json) (LogImpl( __FILE__, __LINE__, LogData() << msg, level, num, json)) 33 | #define logError(msg, num) (LogImpl( __FILE__, __LINE__, LogData() << msg, LogLevel::error, num, nullptr)) 34 | 35 | #ifdef _MSC_VER 36 | #define CONSTEXPR 37 | #define NOEXCEPT 38 | #define NOINLINE _declspec(noinline) 39 | #else 40 | #define CONSTEXPR constexpr 41 | #define NOEXCEPT noexcept 42 | #define NOINLINE __attribute__ ((noinline)) 43 | #endif 44 | 45 | typedef std::map LogLevelStringType; 46 | 47 | static LogLevelStringType LogLevelString( { 48 | { LogLevel::trace, "trace" }, 49 | { LogLevel::debug, "debug" }, 50 | { LogLevel::info, "info" }, 51 | { LogLevel::warn, "warn" }, 52 | { LogLevel::error, "error" }, 53 | { LogLevel::fatal, "fatal" }, 54 | { LogLevel::none, "none" }, 55 | }); 56 | 57 | static LogLevelStringType LogLevelFormatted( { 58 | { LogLevel::trace, R"("trace")" }, 59 | { LogLevel::debug, R"("debug")" }, 60 | { LogLevel::info, R"("info" )" }, 61 | { LogLevel::warn, R"("warn" )" }, 62 | { LogLevel::error, R"("error")" }, 63 | { LogLevel::fatal, R"("fatal")" }, 64 | { LogLevel::none, R"("none" )" }, 65 | }); 66 | 67 | inline LogLevel GetSetLogLevel(LogLevel level = LogLevel::none) 68 | { 69 | static LogLevel storedLevel = LogLevel::info; // set default level 70 | if (level != LogLevel::none) 71 | storedLevel = level; 72 | return storedLevel; 73 | } 74 | 75 | inline void SetLogLevel(LogLevel level) 76 | { 77 | GetSetLogLevel(level); 78 | } 79 | 80 | inline void SetLogLevel(std::string level) 81 | { 82 | auto it = find_if(LogLevelString.begin(), LogLevelString.end(), [level](const LogLevelStringType::value_type & p) { 83 | return p.second == level; 84 | }); 85 | 86 | if (it != LogLevelString.end()) 87 | SetLogLevel(it->first); 88 | } 89 | 90 | inline LogLevel GetLogLevel() 91 | { 92 | return GetSetLogLevel(); 93 | } 94 | 95 | template 96 | NOINLINE void LogImpl(std::string file, int line, LogData&& data, LogLevel level, int64_t num = 0, json11::Json json = nullptr) 97 | { 98 | if (GetLogLevel() > level) 99 | return; 100 | 101 | std::string::size_type n = file.find_last_of("\\/"); 102 | if (n != std::string::npos) 103 | file = file.substr(n + 1); 104 | 105 | std::string time = iso_time(); 106 | std::stringstream ss; 107 | output(ss, std::move(data.list)); 108 | std::string formatted = simple_format((R"({"date":"%","level":%,"thread":%,"msg":%,"file":"%","line":%)"), 109 | time, LogLevelFormatted[level], static_cast(std::hash()(std::this_thread::get_id())), json11::Json(ss.str()).dump(), file, line); 110 | 111 | if (!json.is_null()) 112 | { 113 | std::string s = json.dump(); 114 | formatted += simple_format(R"(,"json":%)", s); 115 | } 116 | 117 | if (num != 0) 118 | formatted += simple_format(R"(,"num":%)", num); 119 | 120 | formatted += "},"; 121 | 122 | std::cout << formatted; 123 | std::cout << std::endl; 124 | } 125 | 126 | struct None { }; 127 | 128 | template 129 | struct LogData { 130 | List list; 131 | }; 132 | 133 | template 134 | CONSTEXPR LogData> operator<<(LogData&& begin, 135 | Value&& value) NOEXCEPT 136 | { 137 | return{ { std::forward(begin.list), std::forward(value) } }; 138 | } 139 | 140 | template 141 | CONSTEXPR LogData> operator<<(LogData&& begin, 142 | const char(&value)[n]) NOEXCEPT 143 | { 144 | return{ { std::forward(begin.list), value } }; 145 | } 146 | 147 | typedef std::ostream& (*fnOstream)(std::ostream&); 148 | 149 | template 150 | CONSTEXPR LogData> operator<<(LogData&& begin, 151 | fnOstream value) NOEXCEPT 152 | { 153 | return{ { std::forward(begin.list), value } }; 154 | } 155 | 156 | template 157 | void output(std::ostream& os, std::pair&& data) 158 | { 159 | output(os, std::move(data.first)); 160 | os << data.second; 161 | } 162 | 163 | inline void output(std::ostream&, None) 164 | { 165 | } 166 | -------------------------------------------------------------------------------- /utils/StringUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __APPLE__ 10 | #include 11 | #include "os_spec/StringUtils_osx.h" 12 | #endif 13 | 14 | #if __linux__ 15 | #include "os_spec/StringUtils_linux.h" 16 | #endif 17 | 18 | #ifdef _MSC_VER 19 | #include "os_spec/StringUtils_windows.h" 20 | #endif 21 | 22 | inline std::wstring s2ws(const char *cstr); 23 | inline std::string ws2s(const wchar_t *wcstr); 24 | 25 | inline std::string ws2s(std::wstring ws) 26 | { 27 | return ws2s(ws.c_str()); 28 | } 29 | 30 | inline std::wstring s2ws(std::string s) 31 | { 32 | return s2ws(s.c_str()); 33 | } 34 | 35 | inline std::string s2lower(const char *cstr) 36 | { 37 | std::string ret = cstr; 38 | transform(ret.begin(), ret.end(), ret.begin(), (int(*)(int))tolower); 39 | return ret; 40 | } 41 | 42 | inline std::string s2upper(const char *cstr) 43 | { 44 | std::string ret = cstr; 45 | transform(ret.begin(), ret.end(), ret.begin(), (int(*)(int))toupper); 46 | return ret; 47 | } 48 | 49 | inline std::string simple_format(const char* s) 50 | { 51 | std::stringstream ss; 52 | while (*s) { 53 | if (*s == '%') { 54 | if (*(s + 1) == '%') { 55 | ++s; 56 | } 57 | } 58 | ss << *s++; 59 | } 60 | return ss.str(); 61 | } 62 | 63 | // simple_format works like printf, but only allows typeless "%" as format specifier relying on stream operators type safe formatting. Example: 64 | // simple_format("one: % is correct, but two: %d, are %d %s", 1, 2, "ba", "format specifier"); 65 | // Results in returning: "one: 1 is correct, but two: 2d, are bad format specifiers" 66 | // 67 | template 68 | std::string simple_format(const char* s, T& value, Args... args) 69 | { 70 | std::stringstream ss; 71 | while (*s) { 72 | if (*s == '%') { 73 | if (*(s + 1) == '%') { 74 | ++s; 75 | } 76 | else { 77 | ss << value; 78 | ss << simple_format(s + 1, args...); 79 | break; 80 | } 81 | } 82 | ss << *s++; 83 | } 84 | return ss.str(); 85 | } 86 | 87 | inline std::string trim(const std::string &s) 88 | { 89 | auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);}); 90 | return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base()); 91 | } 92 | 93 | inline std::string iso_time(std::time_t t = std::time(NULL)) 94 | { 95 | // auto time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 96 | char time[256] = {0}; 97 | std::strftime(time, sizeof(time), "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&t)); 98 | return time; 99 | } 100 | 101 | inline std::string d2s(double d, int precision = 2) 102 | { 103 | std::stringstream ss; 104 | ss.precision(precision); 105 | ss << std::fixed << d; 106 | std::string s = ss.str(); 107 | s.erase(s.find_last_not_of('0') + 1, std::string::npos); //remove trailing zeros 108 | return (s[s.size() - 1] == '.') ? s.substr(0, s.size() - 1) : s; // remove decimal point if present 109 | } 110 | 111 | inline std::string humanable_size(uint64_t i) 112 | { 113 | double d = (double) i; 114 | 115 | int units = 0; 116 | for (; d > 1024.0 && units <= 5; units++) 117 | d /= 1024.0; 118 | 119 | switch (units) 120 | { 121 | case 0: 122 | return d2s(d) + "B"; 123 | case 1: 124 | return d2s(d) + "KB"; 125 | case 2: 126 | return d2s(d) + "MB"; 127 | case 3: 128 | return d2s(d) + "GB"; 129 | case 4: 130 | return d2s(d) + "TB"; 131 | default: 132 | return d2s(d) + "PB"; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /utils/Timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Timer 6 | { 7 | public: 8 | template 9 | Timer(uint32_t interval, bool async, callable&& f, arguments&&... args) 10 | { 11 | std::function::type()> 12 | repeatTask(std::bind(std::forward(f), std::forward(args)...)); 13 | 14 | if (async) 15 | { 16 | std::thread([interval, repeatTask]() { 17 | while (repeatTask()) 18 | std::this_thread::sleep_for(std::chrono::milliseconds(interval)); 19 | }).detach(); 20 | } 21 | else 22 | { 23 | while (repeatTask()) // repeat again if task returns true 24 | std::this_thread::sleep_for(std::chrono::milliseconds(interval)); 25 | } 26 | } 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /utils/json11/compat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _MSC_VER 4 | 5 | #define NOEXCEPT 6 | 7 | #if _MSC_VER <= 1800 // VS 2013 8 | #define noexcept 9 | #define snprintf _snprintf_s 10 | #endif 11 | 12 | #else 13 | 14 | #define NOEXCEPT noexcept 15 | 16 | #endif // _MSC_VER 17 | 18 | #if 0 19 | 20 | #define NOEXCEPT 21 | 22 | #include 23 | 24 | inline int c99_vsnprintf(char* str, size_t size, char* format, va_list ap) 25 | { 26 | int count = -1; 27 | 28 | if (size != 0) 29 | count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); 30 | if (count == -1) 31 | count = _vscprintf(format, ap); 32 | 33 | return count; 34 | } 35 | 36 | inline int c99_snprintf(char* str, size_t size, char* format, ...) 37 | { 38 | int count; 39 | va_list ap; 40 | 41 | va_start(ap, format); 42 | count = c99_vsnprintf(str, size, format, ap); 43 | va_end(ap); 44 | 45 | return count; 46 | } 47 | 48 | #define snprintf c99_snprintf 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /utils/json11/json11.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Dropbox, Inc. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "json11.hpp" 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace json11 { 29 | 30 | static const int max_depth = 200; 31 | 32 | using std::string; 33 | using std::vector; 34 | using std::map; 35 | using std::make_shared; 36 | using std::initializer_list; 37 | using std::move; 38 | 39 | /* * * * * * * * * * * * * * * * * * * * 40 | * Serialization 41 | */ 42 | 43 | static void dump(std::nullptr_t, string &out) { 44 | out += "null"; 45 | } 46 | 47 | static void dump(double value, string &out) { 48 | char buf[32]; 49 | snprintf(buf, sizeof buf, "%.17g", value); 50 | out += buf; 51 | } 52 | 53 | static void dump(int value, string &out) { 54 | char buf[32]; 55 | snprintf(buf, sizeof buf, "%d", value); 56 | out += buf; 57 | } 58 | 59 | static void dump(bool value, string &out) { 60 | out += value ? "true" : "false"; 61 | } 62 | 63 | static void dump(const string &value, string &out) { 64 | out += '"'; 65 | for (size_t i = 0; i < value.length(); i++) { 66 | const char ch = value[i]; 67 | if (ch == '\\') { 68 | out += "\\\\"; 69 | } else if (ch == '"') { 70 | out += "\\\""; 71 | } else if (ch == '\b') { 72 | out += "\\b"; 73 | } else if (ch == '\f') { 74 | out += "\\f"; 75 | } else if (ch == '\n') { 76 | out += "\\n"; 77 | } else if (ch == '\r') { 78 | out += "\\r"; 79 | } else if (ch == '\t') { 80 | out += "\\t"; 81 | } else if (static_cast(ch) <= 0x1f) { 82 | char buf[8]; 83 | snprintf(buf, sizeof buf, "\\u%04x", ch); 84 | out += buf; 85 | } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 86 | && static_cast(value[i+2]) == 0xa8) { 87 | out += "\\u2028"; 88 | i += 2; 89 | } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 90 | && static_cast(value[i+2]) == 0xa9) { 91 | out += "\\u2029"; 92 | i += 2; 93 | } else { 94 | out += ch; 95 | } 96 | } 97 | out += '"'; 98 | } 99 | 100 | static void dump(const Json::array &values, string &out) { 101 | bool first = true; 102 | out += "["; 103 | for (const auto &value : values) { 104 | if (!first) 105 | out += ", "; 106 | value.dump(out); 107 | first = false; 108 | } 109 | out += "]"; 110 | } 111 | 112 | static void dump(const Json::object &values, string &out) { 113 | bool first = true; 114 | out += "{"; 115 | for (const auto &kv : values) { 116 | if (!first) 117 | out += ", "; 118 | dump(kv.first, out); 119 | out += ": "; 120 | kv.second.dump(out); 121 | first = false; 122 | } 123 | out += "}"; 124 | } 125 | 126 | void Json::dump(string &out) const { 127 | m_ptr->dump(out); 128 | } 129 | 130 | /* * * * * * * * * * * * * * * * * * * * 131 | * Value wrappers 132 | */ 133 | 134 | template 135 | class Value : public JsonValue { 136 | protected: 137 | 138 | // Constructors 139 | explicit Value(const T &value) : m_value(value) {} 140 | explicit Value(T &&value) : m_value(move(value)) {} 141 | 142 | // Get type tag 143 | Json::Type type() const override { 144 | return tag; 145 | } 146 | 147 | // Comparisons 148 | bool equals(const JsonValue * other) const override { 149 | return m_value == static_cast *>(other)->m_value; 150 | } 151 | bool less(const JsonValue * other) const override { 152 | return m_value < static_cast *>(other)->m_value; 153 | } 154 | 155 | const T m_value; 156 | void dump(string &out) const override { json11::dump(m_value, out); } 157 | }; 158 | 159 | class JsonDouble final : public Value { 160 | double number_value() const override { return m_value; } 161 | int int_value() const override { return static_cast(m_value); } 162 | bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } 163 | bool less(const JsonValue * other) const override { return m_value < other->number_value(); } 164 | public: 165 | explicit JsonDouble(double value) : Value(value) {} 166 | }; 167 | 168 | class JsonInt final : public Value { 169 | double number_value() const override { return m_value; } 170 | int int_value() const override { return m_value; } 171 | bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } 172 | bool less(const JsonValue * other) const override { return m_value < other->number_value(); } 173 | public: 174 | explicit JsonInt(int value) : Value(value) {} 175 | }; 176 | 177 | class JsonBoolean final : public Value { 178 | bool bool_value() const override { return m_value; } 179 | public: 180 | explicit JsonBoolean(bool value) : Value(value) {} 181 | }; 182 | 183 | class JsonString final : public Value { 184 | const string &string_value() const override { return m_value; } 185 | public: 186 | explicit JsonString(const string &value) : Value(value) {} 187 | explicit JsonString(string &&value) : Value(move(value)) {} 188 | }; 189 | 190 | class JsonArray final : public Value { 191 | const Json::array &array_items() const override { return m_value; } 192 | const Json & operator[](size_t i) const override; 193 | public: 194 | explicit JsonArray(const Json::array &value) : Value(value) {} 195 | explicit JsonArray(Json::array &&value) : Value(move(value)) {} 196 | }; 197 | 198 | class JsonObject final : public Value { 199 | const Json::object &object_items() const override { return m_value; } 200 | const Json & operator[](const string &key) const override; 201 | public: 202 | explicit JsonObject(const Json::object &value) : Value(value) {} 203 | explicit JsonObject(Json::object &&value) : Value(move(value)) {} 204 | }; 205 | 206 | class JsonNull final : public Value { 207 | public: 208 | JsonNull() : Value(nullptr) {} 209 | }; 210 | 211 | /* * * * * * * * * * * * * * * * * * * * 212 | * Static globals - static-init-safe 213 | */ 214 | struct Statics { 215 | const std::shared_ptr null = make_shared(); 216 | const std::shared_ptr t = make_shared(true); 217 | const std::shared_ptr f = make_shared(false); 218 | const string empty_string; 219 | const vector empty_vector; 220 | const map empty_map; 221 | Statics() {} 222 | }; 223 | 224 | const Statics & statics() { 225 | static const Statics s {}; 226 | return s; 227 | } 228 | 229 | const Json & static_null() { 230 | // This has to be separate, not in Statics, because Json() accesses statics().null. 231 | static const Json json_null; 232 | return json_null; 233 | } 234 | 235 | /* * * * * * * * * * * * * * * * * * * * 236 | * Constructors 237 | */ 238 | 239 | Json::Json() noexcept : m_ptr(statics().null) {} 240 | Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} 241 | Json::Json(double value) : m_ptr(make_shared(value)) {} 242 | Json::Json(int value) : m_ptr(make_shared(value)) {} 243 | Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} 244 | Json::Json(const string &value) : m_ptr(make_shared(value)) {} 245 | Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} 246 | Json::Json(const char * value) : m_ptr(make_shared(value)) {} 247 | Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} 248 | Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} 249 | Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} 250 | Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} 251 | 252 | /* * * * * * * * * * * * * * * * * * * * 253 | * Accessors 254 | */ 255 | 256 | Json::Type Json::type() const { return m_ptr->type(); } 257 | double Json::number_value() const { return m_ptr->number_value(); } 258 | int Json::int_value() const { return m_ptr->int_value(); } 259 | bool Json::bool_value() const { return m_ptr->bool_value(); } 260 | const string & Json::string_value() const { return m_ptr->string_value(); } 261 | const vector & Json::array_items() const { return m_ptr->array_items(); } 262 | const map & Json::object_items() const { return m_ptr->object_items(); } 263 | const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } 264 | const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } 265 | 266 | double JsonValue::number_value() const { return 0; } 267 | int JsonValue::int_value() const { return 0; } 268 | bool JsonValue::bool_value() const { return false; } 269 | const string & JsonValue::string_value() const { return statics().empty_string; } 270 | const vector & JsonValue::array_items() const { return statics().empty_vector; } 271 | const map & JsonValue::object_items() const { return statics().empty_map; } 272 | const Json & JsonValue::operator[] (size_t) const { return static_null(); } 273 | const Json & JsonValue::operator[] (const string &) const { return static_null(); } 274 | 275 | const Json & JsonObject::operator[] (const string &key) const { 276 | auto iter = m_value.find(key); 277 | return (iter == m_value.end()) ? static_null() : iter->second; 278 | } 279 | const Json & JsonArray::operator[] (size_t i) const { 280 | if (i >= m_value.size()) return static_null(); 281 | else return m_value[i]; 282 | } 283 | 284 | /* * * * * * * * * * * * * * * * * * * * 285 | * Comparison 286 | */ 287 | 288 | bool Json::operator== (const Json &other) const { 289 | if (m_ptr->type() != other.m_ptr->type()) 290 | return false; 291 | 292 | return m_ptr->equals(other.m_ptr.get()); 293 | } 294 | 295 | bool Json::operator< (const Json &other) const { 296 | if (m_ptr->type() != other.m_ptr->type()) 297 | return m_ptr->type() < other.m_ptr->type(); 298 | 299 | return m_ptr->less(other.m_ptr.get()); 300 | } 301 | 302 | /* * * * * * * * * * * * * * * * * * * * 303 | * Parsing 304 | */ 305 | 306 | /* esc(c) 307 | * 308 | * Format char c suitable for printing in an error message. 309 | */ 310 | static inline string esc(char c) { 311 | char buf[12]; 312 | if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { 313 | snprintf(buf, sizeof buf, "'%c' (%d)", c, c); 314 | } else { 315 | snprintf(buf, sizeof buf, "(%d)", c); 316 | } 317 | return string(buf); 318 | } 319 | 320 | static inline bool in_range(long x, long lower, long upper) { 321 | return (x >= lower && x <= upper); 322 | } 323 | 324 | /* JsonParser 325 | * 326 | * Object that tracks all state of an in-progress parse. 327 | */ 328 | struct JsonParser { 329 | 330 | /* State 331 | */ 332 | const string &str; 333 | size_t i; 334 | string &err; 335 | bool failed; 336 | 337 | /* fail(msg, err_ret = Json()) 338 | * 339 | * Mark this parse as failed. 340 | */ 341 | Json fail(string &&msg) { 342 | return fail(move(msg), Json()); 343 | } 344 | 345 | template 346 | T fail(string &&msg, const T err_ret) { 347 | if (!failed) 348 | err = std::move(msg); 349 | failed = true; 350 | return err_ret; 351 | } 352 | 353 | /* consume_whitespace() 354 | * 355 | * Advance until the current character is non-whitespace. 356 | */ 357 | void consume_whitespace() { 358 | while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') 359 | i++; 360 | } 361 | 362 | /* get_next_token() 363 | * 364 | * Return the next non-whitespace character. If the end of the input is reached, 365 | * flag an error and return 0. 366 | */ 367 | char get_next_token() { 368 | consume_whitespace(); 369 | if (i == str.size()) 370 | return fail("unexpected end of input", 0); 371 | 372 | return str[i++]; 373 | } 374 | 375 | /* encode_utf8(pt, out) 376 | * 377 | * Encode pt as UTF-8 and add it to out. 378 | */ 379 | void encode_utf8(long pt, string & out) { 380 | if (pt < 0) 381 | return; 382 | 383 | if (pt < 0x80) { 384 | out += static_cast(pt); 385 | } else if (pt < 0x800) { 386 | out += static_cast((pt >> 6) | 0xC0); 387 | out += static_cast((pt & 0x3F) | 0x80); 388 | } else if (pt < 0x10000) { 389 | out += static_cast((pt >> 12) | 0xE0); 390 | out += static_cast(((pt >> 6) & 0x3F) | 0x80); 391 | out += static_cast((pt & 0x3F) | 0x80); 392 | } else { 393 | out += static_cast((pt >> 18) | 0xF0); 394 | out += static_cast(((pt >> 12) & 0x3F) | 0x80); 395 | out += static_cast(((pt >> 6) & 0x3F) | 0x80); 396 | out += static_cast((pt & 0x3F) | 0x80); 397 | } 398 | } 399 | 400 | /* parse_string() 401 | * 402 | * Parse a string, starting at the current position. 403 | */ 404 | string parse_string() { 405 | string out; 406 | long last_escaped_codepoint = -1; 407 | while (true) { 408 | if (i == str.size()) 409 | return fail("unexpected end of input in string", ""); 410 | 411 | char ch = str[i++]; 412 | 413 | if (ch == '"') { 414 | encode_utf8(last_escaped_codepoint, out); 415 | return out; 416 | } 417 | 418 | if (in_range(ch, 0, 0x1f)) 419 | return fail("unescaped " + esc(ch) + " in string", ""); 420 | 421 | // The usual case: non-escaped characters 422 | if (ch != '\\') { 423 | encode_utf8(last_escaped_codepoint, out); 424 | last_escaped_codepoint = -1; 425 | out += ch; 426 | continue; 427 | } 428 | 429 | // Handle escapes 430 | if (i == str.size()) 431 | return fail("unexpected end of input in string", ""); 432 | 433 | ch = str[i++]; 434 | 435 | if (ch == 'u') { 436 | // Extract 4-byte escape sequence 437 | string esc = str.substr(i, 4); 438 | // Explicitly check length of the substring. The following loop 439 | // relies on std::string returning the terminating NUL when 440 | // accessing str[length]. Checking here reduces brittleness. 441 | if (esc.length() < 4) { 442 | return fail("bad \\u escape: " + esc, ""); 443 | } 444 | for (int j = 0; j < 4; j++) { 445 | if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') 446 | && !in_range(esc[j], '0', '9')) 447 | return fail("bad \\u escape: " + esc, ""); 448 | } 449 | 450 | long codepoint = strtol(esc.data(), nullptr, 16); 451 | 452 | // JSON specifies that characters outside the BMP shall be encoded as a pair 453 | // of 4-hex-digit \u escapes encoding their surrogate pair components. Check 454 | // whether we're in the middle of such a beast: the previous codepoint was an 455 | // escaped lead (high) surrogate, and this is a trail (low) surrogate. 456 | if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) 457 | && in_range(codepoint, 0xDC00, 0xDFFF)) { 458 | // Reassemble the two surrogate pairs into one astral-plane character, per 459 | // the UTF-16 algorithm. 460 | encode_utf8((((last_escaped_codepoint - 0xD800) << 10) 461 | | (codepoint - 0xDC00)) + 0x10000, out); 462 | last_escaped_codepoint = -1; 463 | } else { 464 | encode_utf8(last_escaped_codepoint, out); 465 | last_escaped_codepoint = codepoint; 466 | } 467 | 468 | i += 4; 469 | continue; 470 | } 471 | 472 | encode_utf8(last_escaped_codepoint, out); 473 | last_escaped_codepoint = -1; 474 | 475 | if (ch == 'b') { 476 | out += '\b'; 477 | } else if (ch == 'f') { 478 | out += '\f'; 479 | } else if (ch == 'n') { 480 | out += '\n'; 481 | } else if (ch == 'r') { 482 | out += '\r'; 483 | } else if (ch == 't') { 484 | out += '\t'; 485 | } else if (ch == '"' || ch == '\\' || ch == '/') { 486 | out += ch; 487 | } else { 488 | return fail("invalid escape character " + esc(ch), ""); 489 | } 490 | } 491 | } 492 | 493 | /* parse_number() 494 | * 495 | * Parse a double. 496 | */ 497 | Json parse_number() { 498 | size_t start_pos = i; 499 | 500 | if (str[i] == '-') 501 | i++; 502 | 503 | // Integer part 504 | if (str[i] == '0') { 505 | i++; 506 | if (in_range(str[i], '0', '9')) 507 | return fail("leading 0s not permitted in numbers"); 508 | } else if (in_range(str[i], '1', '9')) { 509 | i++; 510 | while (in_range(str[i], '0', '9')) 511 | i++; 512 | } else { 513 | return fail("invalid " + esc(str[i]) + " in number"); 514 | } 515 | 516 | if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' 517 | && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { 518 | return std::atoi(str.c_str() + start_pos); 519 | } 520 | 521 | // Decimal part 522 | if (str[i] == '.') { 523 | i++; 524 | if (!in_range(str[i], '0', '9')) 525 | return fail("at least one digit required in fractional part"); 526 | 527 | while (in_range(str[i], '0', '9')) 528 | i++; 529 | } 530 | 531 | // Exponent part 532 | if (str[i] == 'e' || str[i] == 'E') { 533 | i++; 534 | 535 | if (str[i] == '+' || str[i] == '-') 536 | i++; 537 | 538 | if (!in_range(str[i], '0', '9')) 539 | return fail("at least one digit required in exponent"); 540 | 541 | while (in_range(str[i], '0', '9')) 542 | i++; 543 | } 544 | 545 | return std::strtod(str.c_str() + start_pos, nullptr); 546 | } 547 | 548 | /* expect(str, res) 549 | * 550 | * Expect that 'str' starts at the character that was just read. If it does, advance 551 | * the input and return res. If not, flag an error. 552 | */ 553 | Json expect(const string &expected, Json res) { 554 | assert(i != 0); 555 | i--; 556 | if (str.compare(i, expected.length(), expected) == 0) { 557 | i += expected.length(); 558 | return res; 559 | } else { 560 | return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); 561 | } 562 | } 563 | 564 | /* parse_json() 565 | * 566 | * Parse a JSON object. 567 | */ 568 | Json parse_json(int depth) { 569 | if (depth > max_depth) { 570 | return fail("exceeded maximum nesting depth"); 571 | } 572 | 573 | char ch = get_next_token(); 574 | if (failed) 575 | return Json(); 576 | 577 | if (ch == '-' || (ch >= '0' && ch <= '9')) { 578 | i--; 579 | return parse_number(); 580 | } 581 | 582 | if (ch == 't') 583 | return expect("true", true); 584 | 585 | if (ch == 'f') 586 | return expect("false", false); 587 | 588 | if (ch == 'n') 589 | return expect("null", Json()); 590 | 591 | if (ch == '"') 592 | return parse_string(); 593 | 594 | if (ch == '{') { 595 | map data; 596 | ch = get_next_token(); 597 | if (ch == '}') 598 | return data; 599 | 600 | while (1) { 601 | if (ch != '"') 602 | return fail("expected '\"' in object, got " + esc(ch)); 603 | 604 | string key = parse_string(); 605 | if (failed) 606 | return Json(); 607 | 608 | ch = get_next_token(); 609 | if (ch != ':') 610 | return fail("expected ':' in object, got " + esc(ch)); 611 | 612 | data[std::move(key)] = parse_json(depth + 1); 613 | if (failed) 614 | return Json(); 615 | 616 | ch = get_next_token(); 617 | if (ch == '}') 618 | break; 619 | if (ch != ',') 620 | return fail("expected ',' in object, got " + esc(ch)); 621 | 622 | ch = get_next_token(); 623 | } 624 | return data; 625 | } 626 | 627 | if (ch == '[') { 628 | vector data; 629 | ch = get_next_token(); 630 | if (ch == ']') 631 | return data; 632 | 633 | while (1) { 634 | i--; 635 | data.push_back(parse_json(depth + 1)); 636 | if (failed) 637 | return Json(); 638 | 639 | ch = get_next_token(); 640 | if (ch == ']') 641 | break; 642 | if (ch != ',') 643 | return fail("expected ',' in list, got " + esc(ch)); 644 | 645 | ch = get_next_token(); 646 | (void)ch; 647 | } 648 | return data; 649 | } 650 | 651 | return fail("expected value, got " + esc(ch)); 652 | } 653 | }; 654 | 655 | Json Json::parse(const string &in, string &err) { 656 | JsonParser parser { in, 0, err, false }; 657 | Json result = parser.parse_json(0); 658 | 659 | // Check for any trailing garbage 660 | parser.consume_whitespace(); 661 | if (parser.i != in.size()) 662 | return parser.fail("unexpected trailing " + esc(in[parser.i])); 663 | 664 | return result; 665 | } 666 | 667 | // Documented in json11.hpp 668 | vector Json::parse_multi(const string &in, string &err) { 669 | JsonParser parser { in, 0, err, false }; 670 | 671 | vector json_vec; 672 | while (parser.i != in.size() && !parser.failed) { 673 | json_vec.push_back(parser.parse_json(0)); 674 | // Check for another object 675 | parser.consume_whitespace(); 676 | } 677 | return json_vec; 678 | } 679 | 680 | /* * * * * * * * * * * * * * * * * * * * 681 | * Shape-checking 682 | */ 683 | 684 | bool Json::has_shape(const shape & types, string & err) const { 685 | if (!is_object()) { 686 | err = "expected JSON object, got " + dump(); 687 | return false; 688 | } 689 | 690 | for (auto & item : types) { 691 | if ((*this)[item.first].type() != item.second) { 692 | err = "bad type for " + item.first + " in " + dump(); 693 | return false; 694 | } 695 | } 696 | 697 | return true; 698 | } 699 | 700 | } // namespace json11 701 | -------------------------------------------------------------------------------- /utils/json11/json11.hpp: -------------------------------------------------------------------------------- 1 | /* json11 2 | * 3 | * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. 4 | * 5 | * The core object provided by the library is json11::Json. A Json object represents any JSON 6 | * value: null, bool, number (int or double), string (std::string), array (std::vector), or 7 | * object (std::map). 8 | * 9 | * Json objects act like values: they can be assigned, copied, moved, compared for equality or 10 | * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and 11 | * Json::parse (static) to parse a std::string as a Json object. 12 | * 13 | * Internally, the various types of Json object are represented by the JsonValue class 14 | * hierarchy. 15 | * 16 | * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, 17 | * so some JSON implementations distinguish between integers and floating-point numbers, while 18 | * some don't. In json11, we choose the latter. Because some JSON implementations (namely 19 | * Javascript itself) treat all numbers as the same type, distinguishing the two leads 20 | * to JSON that will be *silently* changed by a round-trip through those implementations. 21 | * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also 22 | * provides integer helpers. 23 | * 24 | * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the 25 | * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 26 | * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch 27 | * will be exact for +/- 275 years.) 28 | */ 29 | 30 | /* Copyright (c) 2013 Dropbox, Inc. 31 | * 32 | * Permission is hereby granted, free of charge, to any person obtaining a copy 33 | * of this software and associated documentation files (the "Software"), to deal 34 | * in the Software without restriction, including without limitation the rights 35 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | * copies of the Software, and to permit persons to whom the Software is 37 | * furnished to do so, subject to the following conditions: 38 | * 39 | * The above copyright notice and this permission notice shall be included in 40 | * all copies or substantial portions of the Software. 41 | * 42 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 48 | * THE SOFTWARE. 49 | */ 50 | 51 | #pragma once 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include "compat.h" 59 | 60 | namespace json11 { 61 | 62 | class JsonValue; 63 | 64 | class Json final { 65 | public: 66 | // Types 67 | enum Type { 68 | NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT 69 | }; 70 | 71 | // Array and object typedefs 72 | typedef std::vector array; 73 | typedef std::map object; 74 | 75 | // Constructors for the various types of JSON value. 76 | Json() NOEXCEPT; // NUL 77 | Json(std::nullptr_t) NOEXCEPT; // NUL 78 | Json(double value); // NUMBER 79 | Json(int value); // NUMBER 80 | Json(bool value); // BOOL 81 | Json(const std::string &value); // STRING 82 | Json(std::string &&value); // STRING 83 | Json(const char * value); // STRING 84 | Json(const array &values); // ARRAY 85 | Json(array &&values); // ARRAY 86 | Json(const object &values); // OBJECT 87 | Json(object &&values); // OBJECT 88 | 89 | // Implicit constructor: anything with a to_json() function. 90 | template 91 | Json(const T & t) : Json(t.to_json()) {} 92 | 93 | // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) 94 | template ::value 96 | && std::is_constructible::value, 97 | int>::type = 0> 98 | Json(const M & m) : Json(object(m.begin(), m.end())) {} 99 | 100 | // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) 101 | template ::value, 103 | int>::type = 0> 104 | Json(const V & v) : Json(array(v.begin(), v.end())) {} 105 | 106 | // This prevents Json(some_pointer) from accidentally producing a bool. Use 107 | // Json(bool(some_pointer)) if that behavior is desired. 108 | Json(void *) = delete; 109 | 110 | // Accessors 111 | Type type() const; 112 | 113 | bool is_null() const { return type() == NUL; } 114 | bool is_number() const { return type() == NUMBER; } 115 | bool is_bool() const { return type() == BOOL; } 116 | bool is_string() const { return type() == STRING; } 117 | bool is_array() const { return type() == ARRAY; } 118 | bool is_object() const { return type() == OBJECT; } 119 | 120 | // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not 121 | // distinguish between integer and non-integer numbers - number_value() and int_value() 122 | // can both be applied to a NUMBER-typed object. 123 | double number_value() const; 124 | int int_value() const; 125 | 126 | // Return the enclosed value if this is a boolean, false otherwise. 127 | bool bool_value() const; 128 | // Return the enclosed string if this is a string, "" otherwise. 129 | const std::string &string_value() const; 130 | // Return the enclosed std::vector if this is an array, or an empty vector otherwise. 131 | const array &array_items() const; 132 | // Return the enclosed std::map if this is an object, or an empty map otherwise. 133 | const object &object_items() const; 134 | 135 | // Return a reference to arr[i] if this is an array, Json() otherwise. 136 | const Json & operator[](size_t i) const; 137 | // Return a reference to obj[key] if this is an object, Json() otherwise. 138 | const Json & operator[](const std::string &key) const; 139 | 140 | // Serialize. 141 | void dump(std::string &out) const; 142 | std::string dump() const { 143 | std::string out; 144 | dump(out); 145 | return out; 146 | } 147 | 148 | // Parse. If parse fails, return Json() and assign an error message to err. 149 | static Json parse(const std::string & in, std::string & err); 150 | static Json parse(const char * in, std::string & err) { 151 | if (in) { 152 | return parse(std::string(in), err); 153 | } else { 154 | err = "null input"; 155 | return nullptr; 156 | } 157 | } 158 | // Parse multiple objects, concatenated or separated by whitespace 159 | static std::vector parse_multi(const std::string & in, std::string & err); 160 | 161 | bool operator== (const Json &rhs) const; 162 | bool operator< (const Json &rhs) const; 163 | bool operator!= (const Json &rhs) const { return !(*this == rhs); } 164 | bool operator<= (const Json &rhs) const { return !(rhs < *this); } 165 | bool operator> (const Json &rhs) const { return (rhs < *this); } 166 | bool operator>= (const Json &rhs) const { return !(*this < rhs); } 167 | 168 | /* has_shape(types, err) 169 | * 170 | * Return true if this is a JSON object and, for each item in types, has a field of 171 | * the given type. If not, return false and set err to a descriptive message. 172 | */ 173 | typedef std::initializer_list> shape; 174 | bool has_shape(const shape & types, std::string & err) const; 175 | 176 | private: 177 | std::shared_ptr m_ptr; 178 | }; 179 | 180 | // Internal class hierarchy - JsonValue objects are not exposed to users of this API. 181 | class JsonValue { 182 | protected: 183 | friend class Json; 184 | friend class JsonInt; 185 | friend class JsonDouble; 186 | virtual Json::Type type() const = 0; 187 | virtual bool equals(const JsonValue * other) const = 0; 188 | virtual bool less(const JsonValue * other) const = 0; 189 | virtual void dump(std::string &out) const = 0; 190 | virtual double number_value() const; 191 | virtual int int_value() const; 192 | virtual bool bool_value() const; 193 | virtual const std::string &string_value() const; 194 | virtual const Json::array &array_items() const; 195 | virtual const Json &operator[](size_t i) const; 196 | virtual const Json::object &object_items() const; 197 | virtual const Json &operator[](const std::string &key) const; 198 | virtual ~JsonValue() {} 199 | }; 200 | 201 | } // namespace json11 202 | -------------------------------------------------------------------------------- /utils/json11/json11help.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../StringUtils.h" 4 | #include "../utils/json11/json11.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace json11 12 | { 13 | using namespace std; 14 | 15 | inline void JsonToMap(const Json::object& m1, std::map& m2) 16 | { 17 | for (auto x : m1) 18 | m2.insert(std::make_pair(x.first, x.second.string_value())); 19 | } 20 | 21 | inline map JsonToMap(const Json::object& m1) 22 | { 23 | map m2; 24 | JsonToMap(m1, m2); 25 | return m2; 26 | } 27 | 28 | inline void CopySelectedObject(Json::object& m1, Json::object& m2, const list select) 29 | { 30 | for (auto x : select) 31 | m1[x] = m2[x]; 32 | } 33 | 34 | inline string VerifyObjectType(Json::object& m1, map select) 35 | { 36 | string err; 37 | for (auto x : select) 38 | { 39 | if (m1[x.first].is_null()) 40 | err += x.first + " is missing. "; 41 | else if (m1[x.first].type() != x.second) 42 | err += x.first + " is wrong type. "; 43 | } 44 | return trim(err); 45 | } 46 | 47 | inline void JsonCheck(Json::object& o, const string& name, const string& value) 48 | { 49 | if (!value.empty()) 50 | o[name] = value; 51 | } 52 | 53 | inline void JsonCheck(Json::object& o, const string& name, const wstring& value) 54 | { 55 | if (!value.empty()) 56 | o[name] = ws2s(value); 57 | } 58 | 59 | inline void JsonAppend(Json::array& a1, const Json::array& a2) 60 | { 61 | a1.insert(a1.end(), a2.begin(), a2.end()); 62 | } 63 | 64 | inline void JsonCleanNull(Json::object& json) 65 | { 66 | for (auto x = json.begin(); x != json.end(); ++x) 67 | { 68 | if (x->second.is_null() || (x->second.is_string() && x->second.string_value() == "")) 69 | json.erase(x); 70 | } 71 | } 72 | 73 | inline void JsonReplace(Json::object& m2, Json::object m1) 74 | { 75 | for (auto x : m1) 76 | m2[x.first] = x.second; 77 | } 78 | 79 | inline Json JsonParse(std::string s) 80 | { 81 | string err; 82 | return Json::parse(s, err); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /utils/optionparser/JsonOptionParser.h: -------------------------------------------------------------------------------- 1 | #include "optionparser.h" 2 | 3 | #include "../json11/json11.hpp" 4 | #include "../json11/json11help.h" 5 | #include 6 | #include 7 | 8 | /* 9 | * JsonOptionParser extends optionparse.h (The Lean Mean C++ Option Parser). by placing command line 10 | * formation into json11 object. optionparse.h uses follows getopt() and getopt_long() conventions 11 | * and should refer to its doumentation setting up option::Descriptor. 12 | * 13 | * Parameters that have argements are indicated by JsonArg::Required or JsonArg::Options otherwise 14 | * ArgJson::none is needed. 15 | * 16 | * Parameters are all stored a json object ParseCmdline["parameters"]. Parameters with no arguments 17 | * are set by name to 'true', Also properly formatted Json on the commanded is parsed into json. 18 | * 19 | * For example: 20 | * 21 | * Commandline Json 22 | * -------------------- ------------- 23 | * my_program.exe --run { "parameters": {"run": true} } 24 | * 25 | * my_program.exe --run fast { "parameters": {"run": "fast"} } 26 | * 27 | * my_program.exe --run --user bob { "parameters": {"run": true, "user":"bob" } } 28 | * 29 | * Proper Json is stored as json object: 30 | * my_program.exe --user "{\"name\":\"bob\",\"admin\":true}" { "parameters": {"user": { "name": "bob". "admin": true } } 31 | * 32 | * Incorrectly formatted Json treated like any other string with quotes escaped: 33 | * my_program.exe --user "{\"name\":bob\",\"admin\":true}" { "parameters": {"user": "{\"name\":bob\",\"admin\":true}" } } 34 | * 35 | */ 36 | 37 | namespace option 38 | { 39 | using namespace json11; 40 | 41 | enum optionIndex { UNKNOWN = 0, HELP, FIRST }; 42 | 43 | struct JsonArg : public Arg 44 | { 45 | // static ArgStatus Json(const Option& option, bool msg) // Eventually require proper json and return parsing errors 46 | 47 | static ArgStatus Required(const Option& option, bool msg) 48 | { 49 | if (option.arg != 0) 50 | return ARG_OK; 51 | 52 | // not sure how to supply stream instead of cerr 53 | //if (msg) 54 | // std::cerr << "Option " << option.name << "' requires an argument\n"; 55 | 56 | return ARG_ILLEGAL; 57 | } 58 | }; 59 | 60 | 61 | // ParseCmdline["usage"] is populated with usage info 62 | // ParseCmdline["parameters"] hold detected parameters and arguments 63 | // ParseCmdline["error"] is present when error detected 64 | inline Json ParseCmdline(const Descriptor usage[], int argc = 0, char* argv[] = NULL) 65 | { 66 | Json::object ret; 67 | std::stringstream ss; 68 | 69 | argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present 70 | Stats stats(usage, argc, argv); 71 | unique_ptr options(new Option[stats.options_max]); 72 | unique_ptr buffer(new Option[stats.buffer_max]); 73 | Parser parse(usage, argc, argv, options.get(), buffer.get()); 74 | 75 | printUsage(ss, usage); 76 | ret["usage"] = ss.str(); 77 | ss.str(""); // 'empty' stream 78 | 79 | do 80 | { 81 | if (options[HELP] || argc <= 0) 82 | break; 83 | 84 | if (parse.error()) 85 | { 86 | ss << "Illegal Argument"; 87 | break; 88 | } 89 | 90 | for (Option* opt = options[UNKNOWN]; opt != NULL; opt = opt->next()) 91 | ss << "Unknown option: " << opt->name << "\n"; 92 | 93 | if (options[UNKNOWN] != NULL) 94 | break; 95 | 96 | for (int i = 0; i < parse.nonOptionsCount(); i++) 97 | ss << "Non-option: " << parse.nonOption(i) << "\n"; 98 | 99 | if (parse.nonOptionsCount() > 0) 100 | break; 101 | 102 | Json::object parameters; 103 | for (uint32_t i = 0; i < stats.options_max; i++) 104 | { 105 | const char* parameterName = options[i].desc != NULL ? options[i].desc->longopt : NULL; 106 | if (parameterName == NULL) 107 | continue; 108 | 109 | if (options[i].arg != NULL) // option has arguments 110 | { 111 | std::string err; 112 | Json settings = Json::parse(options[i].arg, err); 113 | if (!settings.is_null()) 114 | parameters.insert(std::make_pair(parameterName, settings)); 115 | else 116 | parameters.insert(std::make_pair(parameterName, Json(options[i].arg))); // json parse failed store argument as string 117 | } 118 | else if (options[i].type()) // option present with no arguments 119 | parameters[parameterName] = true; 120 | } 121 | 122 | ret["parameters"] = parameters; 123 | } 124 | while(0); 125 | 126 | if (ss.str().length()) 127 | ret["error"] = ss.str(); 128 | 129 | return ret; 130 | } 131 | 132 | #ifdef NO_INVOKE_YET 133 | 134 | int JsonRun(const Json& parameters); 135 | 136 | inline int InvokeJsonRun(const Descriptor usage[], int argc, char* argv[]) 137 | { 138 | Json cmdline = ParseCmdline(argc, argv, usage); 139 | 140 | if (!cmdline["err"].is_null()) 141 | { 142 | std::cout << cmdline["err"].string_value(); 143 | return -1; 144 | } 145 | return ::JsonRun(cmdline["parameters"]); 146 | } 147 | 148 | #endif 149 | 150 | } -------------------------------------------------------------------------------- /utils/os_spec/StringUtils_linux.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline std::string ws2s(const wchar_t *wcstr) 4 | { 5 | std::wstring wsInput = wcstr; 6 | std::string ret; 7 | size_t origsize = wsInput.length() + 1; 8 | char* converted = new char[origsize]; 9 | wcstombs(converted, wsInput.c_str(), origsize); 10 | ret = (std::string) converted; 11 | delete [] converted; 12 | return ret; 13 | } 14 | 15 | inline std::wstring s2ws(const char *cstr) 16 | { 17 | std::string sInput = cstr; 18 | std::wstring ret; 19 | size_t origsize = sInput.length() + 1; 20 | wchar_t* converted = new wchar_t[origsize]; 21 | mbstowcs(converted, sInput.c_str(), origsize); 22 | ret = (std::wstring) converted; 23 | delete [] converted; 24 | return ret; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /utils/os_spec/StringUtils_osx.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | inline std::wstring s2ws(const char* s) 4 | { 5 | return std::wstring_convert>().from_bytes(s); 6 | } 7 | 8 | inline std::string ws2s(const wchar_t* s) 9 | { 10 | return std::wstring_convert>().to_bytes(s); 11 | } 12 | -------------------------------------------------------------------------------- /utils/os_spec/StringUtils_windows.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline std::wstring s2ws(const char* s) 4 | { 5 | return std::wstring_convert>().from_bytes(s); 6 | } 7 | 8 | inline std::string ws2s(const wchar_t* s) 9 | { 10 | return std::wstring_convert>().to_bytes(s); 11 | } 12 | --------------------------------------------------------------------------------