├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README ├── cmake └── Modules │ └── FindZNC.cmake ├── jload.cpp ├── jload.h ├── json ├── json.h └── jsoncpp.cpp ├── tmpl └── index.tmpl ├── twitchtmi.cpp └── twitchtmi.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | build/ 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | 3 | project(znctwitch) 4 | 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/") 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 8 | set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) 9 | 10 | find_package(ZNC REQUIRED) 11 | find_package(CURL REQUIRED) 12 | 13 | set(twitch_SOURCES 14 | twitchtmi.cpp 15 | twitchtmi.h 16 | jload.cpp 17 | jload.h 18 | json/jsoncpp.cpp 19 | json/json.h) 20 | 21 | add_library(twitch MODULE 22 | ${twitch_SOURCES}) 23 | target_link_libraries(twitch 24 | ${ZNC_LIBRARIES} 25 | ${CURL_LIBRARIES}) 26 | target_include_directories(twitch SYSTEM 27 | PRIVATE 28 | ${ZNC_INCLUDE_DIRS} 29 | ${CURL_INCLUDE_DIRS}) 30 | target_compile_options(twitch 31 | PRIVATE ${ZNC_DEFINITIONS} -Wno-shadow 32 | PUBLIC -fno-strict-aliasing 33 | PUBLIC -std=gnu++17) 34 | set_target_properties(twitch PROPERTIES 35 | PREFIX "") 36 | 37 | include(GNUInstallDirs) 38 | install(TARGETS twitch DESTINATION "${CMAKE_INSTALL_LIBDIR}/znc") 39 | install(DIRECTORY "${PROJECT_SOURCE_DIR}/tmpl/" DESTINATION share/znc/modules/twitch/tmpl) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ZNC Module for Twitch IRC Chat interface 2 | 3 | If the presence of this module makes your ZNC behave weird or even makes it crash, 4 | you might have to use a diffrent SSL backend for libcurl, as the default openssl 5 | based one conflicts with znc itself using openssl. 6 | On Debian-Based distributions, just install for example libcurl3-nss 7 | 8 | -------------------------------------------------------------------------------- /cmake/Modules/FindZNC.cmake: -------------------------------------------------------------------------------- 1 | # Once done these will be defined: 2 | # 3 | # ZNC_FOUND 4 | # ZNC_INCLUDE_DIRS 5 | # ZNC_LIBRARIES 6 | # ZNC_DEFINITIONS 7 | 8 | if(ZNC_INCLUDE_DIRS AND ZNC_LIBRARIES AND ZNC_DEFINITIONS) 9 | set(ZNC_FOUND TRUE) 10 | else() 11 | find_package(PkgConfig QUIET) 12 | if(PKG_CONFIG_FOUND) 13 | pkg_check_modules(ZNC_PKG QUIET znc) 14 | endif() 15 | 16 | find_path(ZNC_INCLUDE_DIR 17 | NAMES 18 | znc/zncconfig.h 19 | znc/main.h 20 | znc/Modules.h 21 | HINTS 22 | ${ZNC_PKG_INCLUDE_DIRS}) 23 | 24 | find_library(ZNC_LIB 25 | NAMES 26 | znc 27 | HINTS 28 | ${ZNC_PKG_LIBRARY_DIRS}) 29 | 30 | if(NOT ZNC_LIB) 31 | unset(ZNC_LIB CACHE) 32 | endif() 33 | 34 | if(NOT ZNC_PKG_CFLAGS_OTHER) 35 | set(ZNC_PKG_CFLAGS_OTHER -Wall -W -Wno-unused-parameter -Woverloaded-virtual -Wshadow -pthread -fvisibility=hidden -fPIC -include znc/zncconfig.h) 36 | endif() 37 | 38 | set(ZNC_DEFINITIONS ${ZNC_PKG_CFLAGS_OTHER} CACHE STRING "ZNC module compile options") 39 | set(ZNC_INCLUDE_DIRS ${ZNC_INCLUDE_DIR} CACHE PATH "ZNC include dir") 40 | set(ZNC_LIBRARIES ${ZNC_LIB} CACHE STRING "ZNC module library (optional)") 41 | 42 | include(FindPackageHandleStandardArgs) 43 | find_package_handle_standard_args(ZNC DEFAULT_MSG 44 | ZNC_INCLUDE_DIR ZNC_DEFINITIONS) 45 | mark_as_advanced(ZNC_INCLUDE_DIR ZNC_LIB) 46 | endif() 47 | -------------------------------------------------------------------------------- /jload.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "jload.h" 6 | 7 | static bool wasCurlInit = false; 8 | 9 | void initCurl() 10 | { 11 | if(!wasCurlInit) 12 | { 13 | curl_global_init(CURL_GLOBAL_ALL); 14 | wasCurlInit = true; 15 | } 16 | } 17 | 18 | static size_t WriteCB(void *contents, size_t size, size_t nmemb, void *userp) 19 | { 20 | std::string *userstr = (std::string*)userp; 21 | userstr->append((char*)contents, size * nmemb); 22 | return size * nmemb; 23 | } 24 | 25 | static size_t ReadCB(void *dest, size_t size, size_t nmemb, void *userp) 26 | { 27 | std::istringstream *postSs = (std::istringstream*)userp; 28 | size_t bufsize = size * nmemb; 29 | postSs->read((char*)dest, bufsize); 30 | return postSs->gcount(); 31 | } 32 | 33 | std::string getUrl(const std::string &url, const std::list &extraHeaders, const std::string &postData) 34 | { 35 | std::string resStr; 36 | 37 | CURL *curl; 38 | CURLcode res; 39 | 40 | initCurl(); 41 | 42 | curl = curl_easy_init(); 43 | if(!curl) 44 | return resStr; 45 | 46 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 47 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 48 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 49 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 50 | curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); 51 | curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); 52 | curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); 53 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 54 | curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); 55 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resStr); 56 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCB); 57 | 58 | struct curl_slist *hlist = nullptr; 59 | bool has_client_id = false; 60 | for(const std::string &extraHeader: extraHeaders) 61 | { 62 | hlist = curl_slist_append(hlist, extraHeader.c_str()); 63 | if(extraHeader.substr(0, 10) == "Client-ID:") 64 | has_client_id = true; 65 | } 66 | if(!has_client_id) 67 | hlist = curl_slist_append(hlist, "Client-ID: kimne78kx3ncx6brgo4mv6wki5h1ko"); 68 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hlist); 69 | 70 | std::istringstream postSs(postData); 71 | if (!postData.empty()) 72 | { 73 | curl_easy_setopt(curl, CURLOPT_POST, 1L); 74 | curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadCB); 75 | curl_easy_setopt(curl, CURLOPT_READDATA, &postSs); 76 | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)postData.size()); 77 | } 78 | 79 | res = curl_easy_perform(curl); 80 | 81 | curl_slist_free_all(hlist); 82 | curl_easy_cleanup(curl); 83 | 84 | if(res != CURLE_OK) 85 | resStr.clear(); 86 | 87 | return resStr; 88 | } 89 | 90 | Json::Value getJsonFromUrl(const std::string &url, const std::list &extraHeaders, const std::string &postData) 91 | { 92 | Json::Value res; 93 | Json::CharReaderBuilder rb; 94 | std::unique_ptr reader(rb.newCharReader()); 95 | 96 | std::string data = getUrl(url, extraHeaders, postData); 97 | 98 | if(data.empty()) 99 | return res; 100 | 101 | bool ok = reader->parse(data.c_str(), data.c_str() + data.size(), &res, nullptr); 102 | 103 | if(!ok) 104 | return Json::Value(); 105 | 106 | return res; 107 | } 108 | -------------------------------------------------------------------------------- /jload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "json/json.h" 6 | 7 | void initCurl(); 8 | 9 | std::string getUrl(const std::string &url, const std::list &extraHeaders, const std::string &postData); 10 | inline std::string getUrl(const std::string &url, const std::list &extraHeaders) { return getUrl(url, extraHeaders, std::string()); } 11 | inline std::string getUrl(const std::string &url) { return getUrl(url, std::list(), std::string()); } 12 | 13 | Json::Value getJsonFromUrl(const std::string &url, const std::list &extraHeaders, const std::string &postData); 14 | inline Json::Value getJsonFromUrl(const std::string &url, const std::list &extraHeaders) { return getJsonFromUrl(url, extraHeaders, std::string()); } 15 | inline Json::Value getJsonFromUrl(const std::string &url) { return getJsonFromUrl(url, std::list(), std::string()); } 16 | -------------------------------------------------------------------------------- /json/json.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). 2 | /// It is intended to be used with #include "json/json.h" 3 | 4 | // ////////////////////////////////////////////////////////////////////// 5 | // Beginning of content of file: LICENSE 6 | // ////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | The JsonCpp library's source code, including accompanying documentation, 10 | tests and demonstration applications, are licensed under the following 11 | conditions... 12 | 13 | Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 14 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 15 | this software is released into the Public Domain. 16 | 17 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 18 | 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and 19 | The JsonCpp Authors, and is released under the terms of the MIT License (see below). 20 | 21 | In jurisdictions which recognize Public Domain property, the user of this 22 | software may choose to accept it either as 1) Public Domain, 2) under the 23 | conditions of the MIT License (see below), or 3) under the terms of dual 24 | Public Domain/MIT License conditions described here, as they choose. 25 | 26 | The MIT License is about as close to Public Domain as a license can get, and is 27 | described in clear, concise terms at: 28 | 29 | http://en.wikipedia.org/wiki/MIT_License 30 | 31 | The full text of the MIT License follows: 32 | 33 | ======================================================================== 34 | Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 35 | 36 | Permission is hereby granted, free of charge, to any person 37 | obtaining a copy of this software and associated documentation 38 | files (the "Software"), to deal in the Software without 39 | restriction, including without limitation the rights to use, copy, 40 | modify, merge, publish, distribute, sublicense, and/or sell copies 41 | of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be 45 | included in all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 50 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 51 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 52 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 53 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | ======================================================================== 56 | (END LICENSE TEXT) 57 | 58 | The MIT license is compatible with both the GPL and commercial 59 | software, affording one all of the rights of Public Domain with the 60 | minor nuisance of being required to keep the above copyright notice 61 | and license text in the source code. Note also that by accepting the 62 | Public Domain "license" you can re-license your copy using whatever 63 | license you like. 64 | 65 | */ 66 | 67 | // ////////////////////////////////////////////////////////////////////// 68 | // End of content of file: LICENSE 69 | // ////////////////////////////////////////////////////////////////////// 70 | 71 | 72 | 73 | 74 | 75 | #ifndef JSON_AMALGAMATED_H_INCLUDED 76 | # define JSON_AMALGAMATED_H_INCLUDED 77 | /// If defined, indicates that the source file is amalgamated 78 | /// to prevent private header inclusion. 79 | #define JSON_IS_AMALGAMATION 80 | 81 | // ////////////////////////////////////////////////////////////////////// 82 | // Beginning of content of file: include/json/version.h 83 | // ////////////////////////////////////////////////////////////////////// 84 | 85 | #ifndef JSON_VERSION_H_INCLUDED 86 | #define JSON_VERSION_H_INCLUDED 87 | 88 | // Note: version must be updated in three places when doing a release. This 89 | // annoying process ensures that amalgamate, CMake, and meson all report the 90 | // correct version. 91 | // 1. /meson.build 92 | // 2. /include/json/version.h 93 | // 3. /CMakeLists.txt 94 | // IMPORTANT: also update the SOVERSION!! 95 | 96 | #define JSONCPP_VERSION_STRING "1.9.4" 97 | #define JSONCPP_VERSION_MAJOR 1 98 | #define JSONCPP_VERSION_MINOR 9 99 | #define JSONCPP_VERSION_PATCH 4 100 | #define JSONCPP_VERSION_QUALIFIER 101 | #define JSONCPP_VERSION_HEXA \ 102 | ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ 103 | (JSONCPP_VERSION_PATCH << 8)) 104 | 105 | #ifdef JSONCPP_USING_SECURE_MEMORY 106 | #undef JSONCPP_USING_SECURE_MEMORY 107 | #endif 108 | #define JSONCPP_USING_SECURE_MEMORY 0 109 | // If non-zero, the library zeroes any memory that it has allocated before 110 | // it frees its memory. 111 | 112 | #endif // JSON_VERSION_H_INCLUDED 113 | 114 | // ////////////////////////////////////////////////////////////////////// 115 | // End of content of file: include/json/version.h 116 | // ////////////////////////////////////////////////////////////////////// 117 | 118 | 119 | 120 | 121 | 122 | 123 | // ////////////////////////////////////////////////////////////////////// 124 | // Beginning of content of file: include/json/allocator.h 125 | // ////////////////////////////////////////////////////////////////////// 126 | 127 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 128 | // Distributed under MIT license, or public domain if desired and 129 | // recognized in your jurisdiction. 130 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 131 | 132 | #ifndef JSON_ALLOCATOR_H_INCLUDED 133 | #define JSON_ALLOCATOR_H_INCLUDED 134 | 135 | #include 136 | #include 137 | 138 | #pragma pack(push, 8) 139 | 140 | namespace Json { 141 | template class SecureAllocator { 142 | public: 143 | // Type definitions 144 | using value_type = T; 145 | using pointer = T*; 146 | using const_pointer = const T*; 147 | using reference = T&; 148 | using const_reference = const T&; 149 | using size_type = std::size_t; 150 | using difference_type = std::ptrdiff_t; 151 | 152 | /** 153 | * Allocate memory for N items using the standard allocator. 154 | */ 155 | pointer allocate(size_type n) { 156 | // allocate using "global operator new" 157 | return static_cast(::operator new(n * sizeof(T))); 158 | } 159 | 160 | /** 161 | * Release memory which was allocated for N items at pointer P. 162 | * 163 | * The memory block is filled with zeroes before being released. 164 | */ 165 | void deallocate(pointer p, size_type n) { 166 | // memset_s is used because memset may be optimized away by the compiler 167 | memset_s(p, n * sizeof(T), 0, n * sizeof(T)); 168 | // free using "global operator delete" 169 | ::operator delete(p); 170 | } 171 | 172 | /** 173 | * Construct an item in-place at pointer P. 174 | */ 175 | template void construct(pointer p, Args&&... args) { 176 | // construct using "placement new" and "perfect forwarding" 177 | ::new (static_cast(p)) T(std::forward(args)...); 178 | } 179 | 180 | size_type max_size() const { return size_t(-1) / sizeof(T); } 181 | 182 | pointer address(reference x) const { return std::addressof(x); } 183 | 184 | const_pointer address(const_reference x) const { return std::addressof(x); } 185 | 186 | /** 187 | * Destroy an item in-place at pointer P. 188 | */ 189 | void destroy(pointer p) { 190 | // destroy using "explicit destructor" 191 | p->~T(); 192 | } 193 | 194 | // Boilerplate 195 | SecureAllocator() {} 196 | template SecureAllocator(const SecureAllocator&) {} 197 | template struct rebind { using other = SecureAllocator; }; 198 | }; 199 | 200 | template 201 | bool operator==(const SecureAllocator&, const SecureAllocator&) { 202 | return true; 203 | } 204 | 205 | template 206 | bool operator!=(const SecureAllocator&, const SecureAllocator&) { 207 | return false; 208 | } 209 | 210 | } // namespace Json 211 | 212 | #pragma pack(pop) 213 | 214 | #endif // JSON_ALLOCATOR_H_INCLUDED 215 | 216 | // ////////////////////////////////////////////////////////////////////// 217 | // End of content of file: include/json/allocator.h 218 | // ////////////////////////////////////////////////////////////////////// 219 | 220 | 221 | 222 | 223 | 224 | 225 | // ////////////////////////////////////////////////////////////////////// 226 | // Beginning of content of file: include/json/config.h 227 | // ////////////////////////////////////////////////////////////////////// 228 | 229 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 230 | // Distributed under MIT license, or public domain if desired and 231 | // recognized in your jurisdiction. 232 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 233 | 234 | #ifndef JSON_CONFIG_H_INCLUDED 235 | #define JSON_CONFIG_H_INCLUDED 236 | #include 237 | #include 238 | #include 239 | #include 240 | #include 241 | #include 242 | #include 243 | #include 244 | 245 | // If non-zero, the library uses exceptions to report bad input instead of C 246 | // assertion macros. The default is to use exceptions. 247 | #ifndef JSON_USE_EXCEPTION 248 | #define JSON_USE_EXCEPTION 1 249 | #endif 250 | 251 | // Temporary, tracked for removal with issue #982. 252 | #ifndef JSON_USE_NULLREF 253 | #define JSON_USE_NULLREF 1 254 | #endif 255 | 256 | /// If defined, indicates that the source file is amalgamated 257 | /// to prevent private header inclusion. 258 | /// Remarks: it is automatically defined in the generated amalgamated header. 259 | // #define JSON_IS_AMALGAMATION 260 | 261 | // Export macros for DLL visibility 262 | #if defined(JSON_DLL_BUILD) 263 | #if defined(_MSC_VER) || defined(__MINGW32__) 264 | #define JSON_API __declspec(dllexport) 265 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 266 | #elif defined(__GNUC__) || defined(__clang__) 267 | #define JSON_API __attribute__((visibility("default"))) 268 | #endif // if defined(_MSC_VER) 269 | 270 | #elif defined(JSON_DLL) 271 | #if defined(_MSC_VER) || defined(__MINGW32__) 272 | #define JSON_API __declspec(dllimport) 273 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 274 | #endif // if defined(_MSC_VER) 275 | #endif // ifdef JSON_DLL_BUILD 276 | 277 | #if !defined(JSON_API) 278 | #define JSON_API 279 | #endif 280 | 281 | #if defined(_MSC_VER) && _MSC_VER < 1800 282 | #error \ 283 | "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" 284 | #endif 285 | 286 | #if defined(_MSC_VER) && _MSC_VER < 1900 287 | // As recommended at 288 | // https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 289 | extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, 290 | const char* format, ...); 291 | #define jsoncpp_snprintf msvc_pre1900_c99_snprintf 292 | #else 293 | #define jsoncpp_snprintf std::snprintf 294 | #endif 295 | 296 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 297 | // integer 298 | // Storages, and 64 bits integer support is disabled. 299 | // #define JSON_NO_INT64 1 300 | 301 | // JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. 302 | // C++11 should be used directly in JSONCPP. 303 | #define JSONCPP_OVERRIDE override 304 | 305 | #ifdef __clang__ 306 | #if __has_extension(attribute_deprecated_with_message) 307 | #define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) 308 | #endif 309 | #elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) 310 | #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) 311 | #define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) 312 | #elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) 313 | #define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) 314 | #endif // GNUC version 315 | #elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates 316 | // MSVC) 317 | #define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 318 | #endif // __clang__ || __GNUC__ || _MSC_VER 319 | 320 | #if !defined(JSONCPP_DEPRECATED) 321 | #define JSONCPP_DEPRECATED(message) 322 | #endif // if !defined(JSONCPP_DEPRECATED) 323 | 324 | #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) 325 | #define JSON_USE_INT64_DOUBLE_CONVERSION 1 326 | #endif 327 | 328 | #if !defined(JSON_IS_AMALGAMATION) 329 | 330 | #include "allocator.h" 331 | #include "version.h" 332 | 333 | #endif // if !defined(JSON_IS_AMALGAMATION) 334 | 335 | namespace Json { 336 | using Int = int; 337 | using UInt = unsigned int; 338 | #if defined(JSON_NO_INT64) 339 | using LargestInt = int; 340 | using LargestUInt = unsigned int; 341 | #undef JSON_HAS_INT64 342 | #else // if defined(JSON_NO_INT64) 343 | // For Microsoft Visual use specific types as long long is not supported 344 | #if defined(_MSC_VER) // Microsoft Visual Studio 345 | using Int64 = __int64; 346 | using UInt64 = unsigned __int64; 347 | #else // if defined(_MSC_VER) // Other platforms, use long long 348 | using Int64 = int64_t; 349 | using UInt64 = uint64_t; 350 | #endif // if defined(_MSC_VER) 351 | using LargestInt = Int64; 352 | using LargestUInt = UInt64; 353 | #define JSON_HAS_INT64 354 | #endif // if defined(JSON_NO_INT64) 355 | 356 | template 357 | using Allocator = 358 | typename std::conditional, 359 | std::allocator>::type; 360 | using String = std::basic_string, Allocator>; 361 | using IStringStream = 362 | std::basic_istringstream; 364 | using OStringStream = 365 | std::basic_ostringstream; 367 | using IStream = std::istream; 368 | using OStream = std::ostream; 369 | } // namespace Json 370 | 371 | // Legacy names (formerly macros). 372 | using JSONCPP_STRING = Json::String; 373 | using JSONCPP_ISTRINGSTREAM = Json::IStringStream; 374 | using JSONCPP_OSTRINGSTREAM = Json::OStringStream; 375 | using JSONCPP_ISTREAM = Json::IStream; 376 | using JSONCPP_OSTREAM = Json::OStream; 377 | 378 | #endif // JSON_CONFIG_H_INCLUDED 379 | 380 | // ////////////////////////////////////////////////////////////////////// 381 | // End of content of file: include/json/config.h 382 | // ////////////////////////////////////////////////////////////////////// 383 | 384 | 385 | 386 | 387 | 388 | 389 | // ////////////////////////////////////////////////////////////////////// 390 | // Beginning of content of file: include/json/forwards.h 391 | // ////////////////////////////////////////////////////////////////////// 392 | 393 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 394 | // Distributed under MIT license, or public domain if desired and 395 | // recognized in your jurisdiction. 396 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 397 | 398 | #ifndef JSON_FORWARDS_H_INCLUDED 399 | #define JSON_FORWARDS_H_INCLUDED 400 | 401 | #if !defined(JSON_IS_AMALGAMATION) 402 | #include "config.h" 403 | #endif // if !defined(JSON_IS_AMALGAMATION) 404 | 405 | namespace Json { 406 | 407 | // writer.h 408 | class StreamWriter; 409 | class StreamWriterBuilder; 410 | class Writer; 411 | class FastWriter; 412 | class StyledWriter; 413 | class StyledStreamWriter; 414 | 415 | // reader.h 416 | class Reader; 417 | class CharReader; 418 | class CharReaderBuilder; 419 | 420 | // json_features.h 421 | class Features; 422 | 423 | // value.h 424 | using ArrayIndex = unsigned int; 425 | class StaticString; 426 | class Path; 427 | class PathArgument; 428 | class Value; 429 | class ValueIteratorBase; 430 | class ValueIterator; 431 | class ValueConstIterator; 432 | 433 | } // namespace Json 434 | 435 | #endif // JSON_FORWARDS_H_INCLUDED 436 | 437 | // ////////////////////////////////////////////////////////////////////// 438 | // End of content of file: include/json/forwards.h 439 | // ////////////////////////////////////////////////////////////////////// 440 | 441 | 442 | 443 | 444 | 445 | 446 | // ////////////////////////////////////////////////////////////////////// 447 | // Beginning of content of file: include/json/json_features.h 448 | // ////////////////////////////////////////////////////////////////////// 449 | 450 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 451 | // Distributed under MIT license, or public domain if desired and 452 | // recognized in your jurisdiction. 453 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 454 | 455 | #ifndef JSON_FEATURES_H_INCLUDED 456 | #define JSON_FEATURES_H_INCLUDED 457 | 458 | #if !defined(JSON_IS_AMALGAMATION) 459 | #include "forwards.h" 460 | #endif // if !defined(JSON_IS_AMALGAMATION) 461 | 462 | #pragma pack(push, 8) 463 | 464 | namespace Json { 465 | 466 | /** \brief Configuration passed to reader and writer. 467 | * This configuration object can be used to force the Reader or Writer 468 | * to behave in a standard conforming way. 469 | */ 470 | class JSON_API Features { 471 | public: 472 | /** \brief A configuration that allows all features and assumes all strings 473 | * are UTF-8. 474 | * - C & C++ comments are allowed 475 | * - Root object can be any JSON value 476 | * - Assumes Value strings are encoded in UTF-8 477 | */ 478 | static Features all(); 479 | 480 | /** \brief A configuration that is strictly compatible with the JSON 481 | * specification. 482 | * - Comments are forbidden. 483 | * - Root object must be either an array or an object value. 484 | * - Assumes Value strings are encoded in UTF-8 485 | */ 486 | static Features strictMode(); 487 | 488 | /** \brief Initialize the configuration like JsonConfig::allFeatures; 489 | */ 490 | Features(); 491 | 492 | /// \c true if comments are allowed. Default: \c true. 493 | bool allowComments_{true}; 494 | 495 | /// \c true if root must be either an array or an object value. Default: \c 496 | /// false. 497 | bool strictRoot_{false}; 498 | 499 | /// \c true if dropped null placeholders are allowed. Default: \c false. 500 | bool allowDroppedNullPlaceholders_{false}; 501 | 502 | /// \c true if numeric object key are allowed. Default: \c false. 503 | bool allowNumericKeys_{false}; 504 | }; 505 | 506 | } // namespace Json 507 | 508 | #pragma pack(pop) 509 | 510 | #endif // JSON_FEATURES_H_INCLUDED 511 | 512 | // ////////////////////////////////////////////////////////////////////// 513 | // End of content of file: include/json/json_features.h 514 | // ////////////////////////////////////////////////////////////////////// 515 | 516 | 517 | 518 | 519 | 520 | 521 | // ////////////////////////////////////////////////////////////////////// 522 | // Beginning of content of file: include/json/value.h 523 | // ////////////////////////////////////////////////////////////////////// 524 | 525 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 526 | // Distributed under MIT license, or public domain if desired and 527 | // recognized in your jurisdiction. 528 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 529 | 530 | #ifndef JSON_H_INCLUDED 531 | #define JSON_H_INCLUDED 532 | 533 | #if !defined(JSON_IS_AMALGAMATION) 534 | #include "forwards.h" 535 | #endif // if !defined(JSON_IS_AMALGAMATION) 536 | 537 | // Conditional NORETURN attribute on the throw functions would: 538 | // a) suppress false positives from static code analysis 539 | // b) possibly improve optimization opportunities. 540 | #if !defined(JSONCPP_NORETURN) 541 | #if defined(_MSC_VER) && _MSC_VER == 1800 542 | #define JSONCPP_NORETURN __declspec(noreturn) 543 | #else 544 | #define JSONCPP_NORETURN [[noreturn]] 545 | #endif 546 | #endif 547 | 548 | // Support for '= delete' with template declarations was a late addition 549 | // to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2 550 | // even though these declare themselves to be c++11 compilers. 551 | #if !defined(JSONCPP_TEMPLATE_DELETE) 552 | #if defined(__clang__) && defined(__apple_build_version__) 553 | #if __apple_build_version__ <= 8000042 554 | #define JSONCPP_TEMPLATE_DELETE 555 | #endif 556 | #elif defined(__clang__) 557 | #if __clang_major__ == 3 && __clang_minor__ <= 8 558 | #define JSONCPP_TEMPLATE_DELETE 559 | #endif 560 | #endif 561 | #if !defined(JSONCPP_TEMPLATE_DELETE) 562 | #define JSONCPP_TEMPLATE_DELETE = delete 563 | #endif 564 | #endif 565 | 566 | #include 567 | #include 568 | #include 569 | #include 570 | #include 571 | #include 572 | 573 | // Disable warning C4251: : needs to have dll-interface to 574 | // be used by... 575 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 576 | #pragma warning(push) 577 | #pragma warning(disable : 4251 4275) 578 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 579 | 580 | #pragma pack(push, 8) 581 | 582 | /** \brief JSON (JavaScript Object Notation). 583 | */ 584 | namespace Json { 585 | 586 | #if JSON_USE_EXCEPTION 587 | /** Base class for all exceptions we throw. 588 | * 589 | * We use nothing but these internally. Of course, STL can throw others. 590 | */ 591 | class JSON_API Exception : public std::exception { 592 | public: 593 | Exception(String msg); 594 | ~Exception() noexcept override; 595 | char const* what() const noexcept override; 596 | 597 | protected: 598 | String msg_; 599 | }; 600 | 601 | /** Exceptions which the user cannot easily avoid. 602 | * 603 | * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input 604 | * 605 | * \remark derived from Json::Exception 606 | */ 607 | class JSON_API RuntimeError : public Exception { 608 | public: 609 | RuntimeError(String const& msg); 610 | }; 611 | 612 | /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. 613 | * 614 | * These are precondition-violations (user bugs) and internal errors (our bugs). 615 | * 616 | * \remark derived from Json::Exception 617 | */ 618 | class JSON_API LogicError : public Exception { 619 | public: 620 | LogicError(String const& msg); 621 | }; 622 | #endif 623 | 624 | /// used internally 625 | JSONCPP_NORETURN void throwRuntimeError(String const& msg); 626 | /// used internally 627 | JSONCPP_NORETURN void throwLogicError(String const& msg); 628 | 629 | /** \brief Type of the value held by a Value object. 630 | */ 631 | enum ValueType { 632 | nullValue = 0, ///< 'null' value 633 | intValue, ///< signed integer value 634 | uintValue, ///< unsigned integer value 635 | realValue, ///< double value 636 | stringValue, ///< UTF-8 string value 637 | booleanValue, ///< bool value 638 | arrayValue, ///< array value (ordered list) 639 | objectValue ///< object value (collection of name/value pairs). 640 | }; 641 | 642 | enum CommentPlacement { 643 | commentBefore = 0, ///< a comment placed on the line before a value 644 | commentAfterOnSameLine, ///< a comment just after a value on the same line 645 | commentAfter, ///< a comment on the line after a value (only make sense for 646 | /// root value) 647 | numberOfCommentPlacement 648 | }; 649 | 650 | /** \brief Type of precision for formatting of real values. 651 | */ 652 | enum PrecisionType { 653 | significantDigits = 0, ///< we set max number of significant digits in string 654 | decimalPlaces ///< we set max number of digits after "." in string 655 | }; 656 | 657 | /** \brief Lightweight wrapper to tag static string. 658 | * 659 | * Value constructor and objectValue member assignment takes advantage of the 660 | * StaticString and avoid the cost of string duplication when storing the 661 | * string or the member name. 662 | * 663 | * Example of usage: 664 | * \code 665 | * Json::Value aValue( StaticString("some text") ); 666 | * Json::Value object; 667 | * static const StaticString code("code"); 668 | * object[code] = 1234; 669 | * \endcode 670 | */ 671 | class JSON_API StaticString { 672 | public: 673 | explicit StaticString(const char* czstring) : c_str_(czstring) {} 674 | 675 | operator const char*() const { return c_str_; } 676 | 677 | const char* c_str() const { return c_str_; } 678 | 679 | private: 680 | const char* c_str_; 681 | }; 682 | 683 | /** \brief Represents a JSON value. 684 | * 685 | * This class is a discriminated union wrapper that can represents a: 686 | * - signed integer [range: Value::minInt - Value::maxInt] 687 | * - unsigned integer (range: 0 - Value::maxUInt) 688 | * - double 689 | * - UTF-8 string 690 | * - boolean 691 | * - 'null' 692 | * - an ordered list of Value 693 | * - collection of name/value pairs (javascript object) 694 | * 695 | * The type of the held value is represented by a #ValueType and 696 | * can be obtained using type(). 697 | * 698 | * Values of an #objectValue or #arrayValue can be accessed using operator[]() 699 | * methods. 700 | * Non-const methods will automatically create the a #nullValue element 701 | * if it does not exist. 702 | * The sequence of an #arrayValue will be automatically resized and initialized 703 | * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. 704 | * 705 | * The get() methods can be used to obtain default value in the case the 706 | * required element does not exist. 707 | * 708 | * It is possible to iterate over the list of member keys of an object using 709 | * the getMemberNames() method. 710 | * 711 | * \note #Value string-length fit in size_t, but keys must be < 2^30. 712 | * (The reason is an implementation detail.) A #CharReader will raise an 713 | * exception if a bound is exceeded to avoid security holes in your app, 714 | * but the Value API does *not* check bounds. That is the responsibility 715 | * of the caller. 716 | */ 717 | class JSON_API Value { 718 | friend class ValueIteratorBase; 719 | 720 | public: 721 | using Members = std::vector; 722 | using iterator = ValueIterator; 723 | using const_iterator = ValueConstIterator; 724 | using UInt = Json::UInt; 725 | using Int = Json::Int; 726 | #if defined(JSON_HAS_INT64) 727 | using UInt64 = Json::UInt64; 728 | using Int64 = Json::Int64; 729 | #endif // defined(JSON_HAS_INT64) 730 | using LargestInt = Json::LargestInt; 731 | using LargestUInt = Json::LargestUInt; 732 | using ArrayIndex = Json::ArrayIndex; 733 | 734 | // Required for boost integration, e. g. BOOST_TEST 735 | using value_type = std::string; 736 | 737 | #if JSON_USE_NULLREF 738 | // Binary compatibility kludges, do not use. 739 | static const Value& null; 740 | static const Value& nullRef; 741 | #endif 742 | 743 | // null and nullRef are deprecated, use this instead. 744 | static Value const& nullSingleton(); 745 | 746 | /// Minimum signed integer value that can be stored in a Json::Value. 747 | static constexpr LargestInt minLargestInt = 748 | LargestInt(~(LargestUInt(-1) / 2)); 749 | /// Maximum signed integer value that can be stored in a Json::Value. 750 | static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2); 751 | /// Maximum unsigned integer value that can be stored in a Json::Value. 752 | static constexpr LargestUInt maxLargestUInt = LargestUInt(-1); 753 | 754 | /// Minimum signed int value that can be stored in a Json::Value. 755 | static constexpr Int minInt = Int(~(UInt(-1) / 2)); 756 | /// Maximum signed int value that can be stored in a Json::Value. 757 | static constexpr Int maxInt = Int(UInt(-1) / 2); 758 | /// Maximum unsigned int value that can be stored in a Json::Value. 759 | static constexpr UInt maxUInt = UInt(-1); 760 | 761 | #if defined(JSON_HAS_INT64) 762 | /// Minimum signed 64 bits int value that can be stored in a Json::Value. 763 | static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2)); 764 | /// Maximum signed 64 bits int value that can be stored in a Json::Value. 765 | static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2); 766 | /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. 767 | static constexpr UInt64 maxUInt64 = UInt64(-1); 768 | #endif // defined(JSON_HAS_INT64) 769 | /// Default precision for real value for string representation. 770 | static constexpr UInt defaultRealPrecision = 17; 771 | // The constant is hard-coded because some compiler have trouble 772 | // converting Value::maxUInt64 to a double correctly (AIX/xlC). 773 | // Assumes that UInt64 is a 64 bits integer. 774 | static constexpr double maxUInt64AsDouble = 18446744073709551615.0; 775 | // Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler 776 | // when using gcc and clang backend compilers. CZString 777 | // cannot be defined as private. See issue #486 778 | #ifdef __NVCC__ 779 | public: 780 | #else 781 | private: 782 | #endif 783 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 784 | class CZString { 785 | public: 786 | enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; 787 | CZString(ArrayIndex index); 788 | CZString(char const* str, unsigned length, DuplicationPolicy allocate); 789 | CZString(CZString const& other); 790 | CZString(CZString&& other) noexcept; 791 | ~CZString(); 792 | CZString& operator=(const CZString& other); 793 | CZString& operator=(CZString&& other) noexcept; 794 | 795 | bool operator<(CZString const& other) const; 796 | bool operator==(CZString const& other) const; 797 | ArrayIndex index() const; 798 | // const char* c_str() const; ///< \deprecated 799 | char const* data() const; 800 | unsigned length() const; 801 | bool isStaticString() const; 802 | 803 | private: 804 | void swap(CZString& other); 805 | 806 | struct StringStorage { 807 | unsigned policy_ : 2; 808 | unsigned length_ : 30; // 1GB max 809 | }; 810 | 811 | char const* cstr_; // actually, a prefixed string, unless policy is noDup 812 | union { 813 | ArrayIndex index_; 814 | StringStorage storage_; 815 | }; 816 | }; 817 | 818 | public: 819 | typedef std::map ObjectValues; 820 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 821 | 822 | public: 823 | /** 824 | * \brief Create a default Value of the given type. 825 | * 826 | * This is a very useful constructor. 827 | * To create an empty array, pass arrayValue. 828 | * To create an empty object, pass objectValue. 829 | * Another Value can then be set to this one by assignment. 830 | * This is useful since clear() and resize() will not alter types. 831 | * 832 | * Examples: 833 | * \code 834 | * Json::Value null_value; // null 835 | * Json::Value arr_value(Json::arrayValue); // [] 836 | * Json::Value obj_value(Json::objectValue); // {} 837 | * \endcode 838 | */ 839 | Value(ValueType type = nullValue); 840 | Value(Int value); 841 | Value(UInt value); 842 | #if defined(JSON_HAS_INT64) 843 | Value(Int64 value); 844 | Value(UInt64 value); 845 | #endif // if defined(JSON_HAS_INT64) 846 | Value(double value); 847 | Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) 848 | Value(const char* begin, const char* end); ///< Copy all, incl zeroes. 849 | /** 850 | * \brief Constructs a value from a static string. 851 | * 852 | * Like other value string constructor but do not duplicate the string for 853 | * internal storage. The given string must remain alive after the call to 854 | * this constructor. 855 | * 856 | * \note This works only for null-terminated strings. (We cannot change the 857 | * size of this class, so we have nowhere to store the length, which might be 858 | * computed later for various operations.) 859 | * 860 | * Example of usage: 861 | * \code 862 | * static StaticString foo("some text"); 863 | * Json::Value aValue(foo); 864 | * \endcode 865 | */ 866 | Value(const StaticString& value); 867 | Value(const String& value); 868 | Value(bool value); 869 | Value(std::nullptr_t ptr) = delete; 870 | Value(const Value& other); 871 | Value(Value&& other) noexcept; 872 | ~Value(); 873 | 874 | /// \note Overwrite existing comments. To preserve comments, use 875 | /// #swapPayload(). 876 | Value& operator=(const Value& other); 877 | Value& operator=(Value&& other) noexcept; 878 | 879 | /// Swap everything. 880 | void swap(Value& other); 881 | /// Swap values but leave comments and source offsets in place. 882 | void swapPayload(Value& other); 883 | 884 | /// copy everything. 885 | void copy(const Value& other); 886 | /// copy values but leave comments and source offsets in place. 887 | void copyPayload(const Value& other); 888 | 889 | ValueType type() const; 890 | 891 | /// Compare payload only, not comments etc. 892 | bool operator<(const Value& other) const; 893 | bool operator<=(const Value& other) const; 894 | bool operator>=(const Value& other) const; 895 | bool operator>(const Value& other) const; 896 | bool operator==(const Value& other) const; 897 | bool operator!=(const Value& other) const; 898 | int compare(const Value& other) const; 899 | 900 | const char* asCString() const; ///< Embedded zeroes could cause you trouble! 901 | #if JSONCPP_USING_SECURE_MEMORY 902 | unsigned getCStringLength() const; // Allows you to understand the length of 903 | // the CString 904 | #endif 905 | String asString() const; ///< Embedded zeroes are possible. 906 | /** Get raw char* of string-value. 907 | * \return false if !string. (Seg-fault if str or end are NULL.) 908 | */ 909 | bool getString(char const** begin, char const** end) const; 910 | Int asInt() const; 911 | UInt asUInt() const; 912 | #if defined(JSON_HAS_INT64) 913 | Int64 asInt64() const; 914 | UInt64 asUInt64() const; 915 | #endif // if defined(JSON_HAS_INT64) 916 | LargestInt asLargestInt() const; 917 | LargestUInt asLargestUInt() const; 918 | float asFloat() const; 919 | double asDouble() const; 920 | bool asBool() const; 921 | 922 | bool isNull() const; 923 | bool isBool() const; 924 | bool isInt() const; 925 | bool isInt64() const; 926 | bool isUInt() const; 927 | bool isUInt64() const; 928 | bool isIntegral() const; 929 | bool isDouble() const; 930 | bool isNumeric() const; 931 | bool isString() const; 932 | bool isArray() const; 933 | bool isObject() const; 934 | 935 | /// The `as` and `is` member function templates and specializations. 936 | template T as() const JSONCPP_TEMPLATE_DELETE; 937 | template bool is() const JSONCPP_TEMPLATE_DELETE; 938 | 939 | bool isConvertibleTo(ValueType other) const; 940 | 941 | /// Number of values in array or object 942 | ArrayIndex size() const; 943 | 944 | /// \brief Return true if empty array, empty object, or null; 945 | /// otherwise, false. 946 | bool empty() const; 947 | 948 | /// Return !isNull() 949 | explicit operator bool() const; 950 | 951 | /// Remove all object members and array elements. 952 | /// \pre type() is arrayValue, objectValue, or nullValue 953 | /// \post type() is unchanged 954 | void clear(); 955 | 956 | /// Resize the array to newSize elements. 957 | /// New elements are initialized to null. 958 | /// May only be called on nullValue or arrayValue. 959 | /// \pre type() is arrayValue or nullValue 960 | /// \post type() is arrayValue 961 | void resize(ArrayIndex newSize); 962 | 963 | //@{ 964 | /// Access an array element (zero based index). If the array contains less 965 | /// than index element, then null value are inserted in the array so that 966 | /// its size is index+1. 967 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 968 | /// this from the operator[] which takes a string.) 969 | Value& operator[](ArrayIndex index); 970 | Value& operator[](int index); 971 | //@} 972 | 973 | //@{ 974 | /// Access an array element (zero based index). 975 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 976 | /// this from the operator[] which takes a string.) 977 | const Value& operator[](ArrayIndex index) const; 978 | const Value& operator[](int index) const; 979 | //@} 980 | 981 | /// If the array contains at least index+1 elements, returns the element 982 | /// value, otherwise returns defaultValue. 983 | Value get(ArrayIndex index, const Value& defaultValue) const; 984 | /// Return true if index < size(). 985 | bool isValidIndex(ArrayIndex index) const; 986 | /// \brief Append value to array at the end. 987 | /// 988 | /// Equivalent to jsonvalue[jsonvalue.size()] = value; 989 | Value& append(const Value& value); 990 | Value& append(Value&& value); 991 | 992 | /// \brief Insert value in array at specific index 993 | bool insert(ArrayIndex index, const Value& newValue); 994 | bool insert(ArrayIndex index, Value&& newValue); 995 | 996 | /// Access an object value by name, create a null member if it does not exist. 997 | /// \note Because of our implementation, keys are limited to 2^30 -1 chars. 998 | /// Exceeding that will cause an exception. 999 | Value& operator[](const char* key); 1000 | /// Access an object value by name, returns null if there is no member with 1001 | /// that name. 1002 | const Value& operator[](const char* key) const; 1003 | /// Access an object value by name, create a null member if it does not exist. 1004 | /// \param key may contain embedded nulls. 1005 | Value& operator[](const String& key); 1006 | /// Access an object value by name, returns null if there is no member with 1007 | /// that name. 1008 | /// \param key may contain embedded nulls. 1009 | const Value& operator[](const String& key) const; 1010 | /** \brief Access an object value by name, create a null member if it does not 1011 | * exist. 1012 | * 1013 | * If the object has no entry for that name, then the member name used to 1014 | * store the new entry is not duplicated. 1015 | * Example of use: 1016 | * \code 1017 | * Json::Value object; 1018 | * static const StaticString code("code"); 1019 | * object[code] = 1234; 1020 | * \endcode 1021 | */ 1022 | Value& operator[](const StaticString& key); 1023 | /// Return the member named key if it exist, defaultValue otherwise. 1024 | /// \note deep copy 1025 | Value get(const char* key, const Value& defaultValue) const; 1026 | /// Return the member named key if it exist, defaultValue otherwise. 1027 | /// \note deep copy 1028 | /// \note key may contain embedded nulls. 1029 | Value get(const char* begin, const char* end, 1030 | const Value& defaultValue) const; 1031 | /// Return the member named key if it exist, defaultValue otherwise. 1032 | /// \note deep copy 1033 | /// \param key may contain embedded nulls. 1034 | Value get(const String& key, const Value& defaultValue) const; 1035 | /// Most general and efficient version of isMember()const, get()const, 1036 | /// and operator[]const 1037 | /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 1038 | Value const* find(char const* begin, char const* end) const; 1039 | /// Most general and efficient version of object-mutators. 1040 | /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 1041 | /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. 1042 | Value* demand(char const* begin, char const* end); 1043 | /// \brief Remove and return the named member. 1044 | /// 1045 | /// Do nothing if it did not exist. 1046 | /// \pre type() is objectValue or nullValue 1047 | /// \post type() is unchanged 1048 | void removeMember(const char* key); 1049 | /// Same as removeMember(const char*) 1050 | /// \param key may contain embedded nulls. 1051 | void removeMember(const String& key); 1052 | /// Same as removeMember(const char* begin, const char* end, Value* removed), 1053 | /// but 'key' is null-terminated. 1054 | bool removeMember(const char* key, Value* removed); 1055 | /** \brief Remove the named map member. 1056 | * 1057 | * Update 'removed' iff removed. 1058 | * \param key may contain embedded nulls. 1059 | * \return true iff removed (no exceptions) 1060 | */ 1061 | bool removeMember(String const& key, Value* removed); 1062 | /// Same as removeMember(String const& key, Value* removed) 1063 | bool removeMember(const char* begin, const char* end, Value* removed); 1064 | /** \brief Remove the indexed array element. 1065 | * 1066 | * O(n) expensive operations. 1067 | * Update 'removed' iff removed. 1068 | * \return true if removed (no exceptions) 1069 | */ 1070 | bool removeIndex(ArrayIndex index, Value* removed); 1071 | 1072 | /// Return true if the object has a member named key. 1073 | /// \note 'key' must be null-terminated. 1074 | bool isMember(const char* key) const; 1075 | /// Return true if the object has a member named key. 1076 | /// \param key may contain embedded nulls. 1077 | bool isMember(const String& key) const; 1078 | /// Same as isMember(String const& key)const 1079 | bool isMember(const char* begin, const char* end) const; 1080 | 1081 | /// \brief Return a list of the member names. 1082 | /// 1083 | /// If null, return an empty list. 1084 | /// \pre type() is objectValue or nullValue 1085 | /// \post if type() was nullValue, it remains nullValue 1086 | Members getMemberNames() const; 1087 | 1088 | /// \deprecated Always pass len. 1089 | JSONCPP_DEPRECATED("Use setComment(String const&) instead.") 1090 | void setComment(const char* comment, CommentPlacement placement) { 1091 | setComment(String(comment, strlen(comment)), placement); 1092 | } 1093 | /// Comments must be //... or /* ... */ 1094 | void setComment(const char* comment, size_t len, CommentPlacement placement) { 1095 | setComment(String(comment, len), placement); 1096 | } 1097 | /// Comments must be //... or /* ... */ 1098 | void setComment(String comment, CommentPlacement placement); 1099 | bool hasComment(CommentPlacement placement) const; 1100 | /// Include delimiters and embedded newlines. 1101 | String getComment(CommentPlacement placement) const; 1102 | 1103 | String toStyledString() const; 1104 | 1105 | const_iterator begin() const; 1106 | const_iterator end() const; 1107 | 1108 | iterator begin(); 1109 | iterator end(); 1110 | 1111 | // Accessors for the [start, limit) range of bytes within the JSON text from 1112 | // which this value was parsed, if any. 1113 | void setOffsetStart(ptrdiff_t start); 1114 | void setOffsetLimit(ptrdiff_t limit); 1115 | ptrdiff_t getOffsetStart() const; 1116 | ptrdiff_t getOffsetLimit() const; 1117 | 1118 | private: 1119 | void setType(ValueType v) { 1120 | bits_.value_type_ = static_cast(v); 1121 | } 1122 | bool isAllocated() const { return bits_.allocated_; } 1123 | void setIsAllocated(bool v) { bits_.allocated_ = v; } 1124 | 1125 | void initBasic(ValueType type, bool allocated = false); 1126 | void dupPayload(const Value& other); 1127 | void releasePayload(); 1128 | void dupMeta(const Value& other); 1129 | 1130 | Value& resolveReference(const char* key); 1131 | Value& resolveReference(const char* key, const char* end); 1132 | 1133 | // struct MemberNamesTransform 1134 | //{ 1135 | // typedef const char *result_type; 1136 | // const char *operator()( const CZString &name ) const 1137 | // { 1138 | // return name.c_str(); 1139 | // } 1140 | //}; 1141 | 1142 | union ValueHolder { 1143 | LargestInt int_; 1144 | LargestUInt uint_; 1145 | double real_; 1146 | bool bool_; 1147 | char* string_; // if allocated_, ptr to { unsigned, char[] }. 1148 | ObjectValues* map_; 1149 | } value_; 1150 | 1151 | struct { 1152 | // Really a ValueType, but types should agree for bitfield packing. 1153 | unsigned int value_type_ : 8; 1154 | // Unless allocated_, string_ must be null-terminated. 1155 | unsigned int allocated_ : 1; 1156 | } bits_; 1157 | 1158 | class Comments { 1159 | public: 1160 | Comments() = default; 1161 | Comments(const Comments& that); 1162 | Comments(Comments&& that) noexcept; 1163 | Comments& operator=(const Comments& that); 1164 | Comments& operator=(Comments&& that) noexcept; 1165 | bool has(CommentPlacement slot) const; 1166 | String get(CommentPlacement slot) const; 1167 | void set(CommentPlacement slot, String comment); 1168 | 1169 | private: 1170 | using Array = std::array; 1171 | std::unique_ptr ptr_; 1172 | }; 1173 | Comments comments_; 1174 | 1175 | // [start, limit) byte offsets in the source JSON text from which this Value 1176 | // was extracted. 1177 | ptrdiff_t start_; 1178 | ptrdiff_t limit_; 1179 | }; 1180 | 1181 | template <> inline bool Value::as() const { return asBool(); } 1182 | template <> inline bool Value::is() const { return isBool(); } 1183 | 1184 | template <> inline Int Value::as() const { return asInt(); } 1185 | template <> inline bool Value::is() const { return isInt(); } 1186 | 1187 | template <> inline UInt Value::as() const { return asUInt(); } 1188 | template <> inline bool Value::is() const { return isUInt(); } 1189 | 1190 | #if defined(JSON_HAS_INT64) 1191 | template <> inline Int64 Value::as() const { return asInt64(); } 1192 | template <> inline bool Value::is() const { return isInt64(); } 1193 | 1194 | template <> inline UInt64 Value::as() const { return asUInt64(); } 1195 | template <> inline bool Value::is() const { return isUInt64(); } 1196 | #endif 1197 | 1198 | template <> inline double Value::as() const { return asDouble(); } 1199 | template <> inline bool Value::is() const { return isDouble(); } 1200 | 1201 | template <> inline String Value::as() const { return asString(); } 1202 | template <> inline bool Value::is() const { return isString(); } 1203 | 1204 | /// These `as` specializations are type conversions, and do not have a 1205 | /// corresponding `is`. 1206 | template <> inline float Value::as() const { return asFloat(); } 1207 | template <> inline const char* Value::as() const { 1208 | return asCString(); 1209 | } 1210 | 1211 | /** \brief Experimental and untested: represents an element of the "path" to 1212 | * access a node. 1213 | */ 1214 | class JSON_API PathArgument { 1215 | public: 1216 | friend class Path; 1217 | 1218 | PathArgument(); 1219 | PathArgument(ArrayIndex index); 1220 | PathArgument(const char* key); 1221 | PathArgument(String key); 1222 | 1223 | private: 1224 | enum Kind { kindNone = 0, kindIndex, kindKey }; 1225 | String key_; 1226 | ArrayIndex index_{}; 1227 | Kind kind_{kindNone}; 1228 | }; 1229 | 1230 | /** \brief Experimental and untested: represents a "path" to access a node. 1231 | * 1232 | * Syntax: 1233 | * - "." => root node 1234 | * - ".[n]" => elements at index 'n' of root node (an array value) 1235 | * - ".name" => member named 'name' of root node (an object value) 1236 | * - ".name1.name2.name3" 1237 | * - ".[0][1][2].name1[3]" 1238 | * - ".%" => member name is provided as parameter 1239 | * - ".[%]" => index is provided as parameter 1240 | */ 1241 | class JSON_API Path { 1242 | public: 1243 | Path(const String& path, const PathArgument& a1 = PathArgument(), 1244 | const PathArgument& a2 = PathArgument(), 1245 | const PathArgument& a3 = PathArgument(), 1246 | const PathArgument& a4 = PathArgument(), 1247 | const PathArgument& a5 = PathArgument()); 1248 | 1249 | const Value& resolve(const Value& root) const; 1250 | Value resolve(const Value& root, const Value& defaultValue) const; 1251 | /// Creates the "path" to access the specified node and returns a reference on 1252 | /// the node. 1253 | Value& make(Value& root) const; 1254 | 1255 | private: 1256 | using InArgs = std::vector; 1257 | using Args = std::vector; 1258 | 1259 | void makePath(const String& path, const InArgs& in); 1260 | void addPathInArg(const String& path, const InArgs& in, 1261 | InArgs::const_iterator& itInArg, PathArgument::Kind kind); 1262 | static void invalidPath(const String& path, int location); 1263 | 1264 | Args args_; 1265 | }; 1266 | 1267 | /** \brief base class for Value iterators. 1268 | * 1269 | */ 1270 | class JSON_API ValueIteratorBase { 1271 | public: 1272 | using iterator_category = std::bidirectional_iterator_tag; 1273 | using size_t = unsigned int; 1274 | using difference_type = int; 1275 | using SelfType = ValueIteratorBase; 1276 | 1277 | bool operator==(const SelfType& other) const { return isEqual(other); } 1278 | 1279 | bool operator!=(const SelfType& other) const { return !isEqual(other); } 1280 | 1281 | difference_type operator-(const SelfType& other) const { 1282 | return other.computeDistance(*this); 1283 | } 1284 | 1285 | /// Return either the index or the member name of the referenced value as a 1286 | /// Value. 1287 | Value key() const; 1288 | 1289 | /// Return the index of the referenced Value, or -1 if it is not an 1290 | /// arrayValue. 1291 | UInt index() const; 1292 | 1293 | /// Return the member name of the referenced Value, or "" if it is not an 1294 | /// objectValue. 1295 | /// \note Avoid `c_str()` on result, as embedded zeroes are possible. 1296 | String name() const; 1297 | 1298 | /// Return the member name of the referenced Value. "" if it is not an 1299 | /// objectValue. 1300 | /// \deprecated This cannot be used for UTF-8 strings, since there can be 1301 | /// embedded nulls. 1302 | JSONCPP_DEPRECATED("Use `key = name();` instead.") 1303 | char const* memberName() const; 1304 | /// Return the member name of the referenced Value, or NULL if it is not an 1305 | /// objectValue. 1306 | /// \note Better version than memberName(). Allows embedded nulls. 1307 | char const* memberName(char const** end) const; 1308 | 1309 | protected: 1310 | /*! Internal utility functions to assist with implementing 1311 | * other iterator functions. The const and non-const versions 1312 | * of the "deref" protected methods expose the protected 1313 | * current_ member variable in a way that can often be 1314 | * optimized away by the compiler. 1315 | */ 1316 | const Value& deref() const; 1317 | Value& deref(); 1318 | 1319 | void increment(); 1320 | 1321 | void decrement(); 1322 | 1323 | difference_type computeDistance(const SelfType& other) const; 1324 | 1325 | bool isEqual(const SelfType& other) const; 1326 | 1327 | void copy(const SelfType& other); 1328 | 1329 | private: 1330 | Value::ObjectValues::iterator current_; 1331 | // Indicates that iterator is for a null value. 1332 | bool isNull_{true}; 1333 | 1334 | public: 1335 | // For some reason, BORLAND needs these at the end, rather 1336 | // than earlier. No idea why. 1337 | ValueIteratorBase(); 1338 | explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); 1339 | }; 1340 | 1341 | /** \brief const iterator for object and array value. 1342 | * 1343 | */ 1344 | class JSON_API ValueConstIterator : public ValueIteratorBase { 1345 | friend class Value; 1346 | 1347 | public: 1348 | using value_type = const Value; 1349 | // typedef unsigned int size_t; 1350 | // typedef int difference_type; 1351 | using reference = const Value&; 1352 | using pointer = const Value*; 1353 | using SelfType = ValueConstIterator; 1354 | 1355 | ValueConstIterator(); 1356 | ValueConstIterator(ValueIterator const& other); 1357 | 1358 | private: 1359 | /*! \internal Use by Value to create an iterator. 1360 | */ 1361 | explicit ValueConstIterator(const Value::ObjectValues::iterator& current); 1362 | 1363 | public: 1364 | SelfType& operator=(const ValueIteratorBase& other); 1365 | 1366 | SelfType operator++(int) { 1367 | SelfType temp(*this); 1368 | ++*this; 1369 | return temp; 1370 | } 1371 | 1372 | SelfType operator--(int) { 1373 | SelfType temp(*this); 1374 | --*this; 1375 | return temp; 1376 | } 1377 | 1378 | SelfType& operator--() { 1379 | decrement(); 1380 | return *this; 1381 | } 1382 | 1383 | SelfType& operator++() { 1384 | increment(); 1385 | return *this; 1386 | } 1387 | 1388 | reference operator*() const { return deref(); } 1389 | 1390 | pointer operator->() const { return &deref(); } 1391 | }; 1392 | 1393 | /** \brief Iterator for object and array value. 1394 | */ 1395 | class JSON_API ValueIterator : public ValueIteratorBase { 1396 | friend class Value; 1397 | 1398 | public: 1399 | using value_type = Value; 1400 | using size_t = unsigned int; 1401 | using difference_type = int; 1402 | using reference = Value&; 1403 | using pointer = Value*; 1404 | using SelfType = ValueIterator; 1405 | 1406 | ValueIterator(); 1407 | explicit ValueIterator(const ValueConstIterator& other); 1408 | ValueIterator(const ValueIterator& other); 1409 | 1410 | private: 1411 | /*! \internal Use by Value to create an iterator. 1412 | */ 1413 | explicit ValueIterator(const Value::ObjectValues::iterator& current); 1414 | 1415 | public: 1416 | SelfType& operator=(const SelfType& other); 1417 | 1418 | SelfType operator++(int) { 1419 | SelfType temp(*this); 1420 | ++*this; 1421 | return temp; 1422 | } 1423 | 1424 | SelfType operator--(int) { 1425 | SelfType temp(*this); 1426 | --*this; 1427 | return temp; 1428 | } 1429 | 1430 | SelfType& operator--() { 1431 | decrement(); 1432 | return *this; 1433 | } 1434 | 1435 | SelfType& operator++() { 1436 | increment(); 1437 | return *this; 1438 | } 1439 | 1440 | /*! The return value of non-const iterators can be 1441 | * changed, so the these functions are not const 1442 | * because the returned references/pointers can be used 1443 | * to change state of the base class. 1444 | */ 1445 | reference operator*() const { return const_cast(deref()); } 1446 | pointer operator->() const { return const_cast(&deref()); } 1447 | }; 1448 | 1449 | inline void swap(Value& a, Value& b) { a.swap(b); } 1450 | 1451 | } // namespace Json 1452 | 1453 | #pragma pack(pop) 1454 | 1455 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1456 | #pragma warning(pop) 1457 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1458 | 1459 | #endif // JSON_H_INCLUDED 1460 | 1461 | // ////////////////////////////////////////////////////////////////////// 1462 | // End of content of file: include/json/value.h 1463 | // ////////////////////////////////////////////////////////////////////// 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | // ////////////////////////////////////////////////////////////////////// 1471 | // Beginning of content of file: include/json/reader.h 1472 | // ////////////////////////////////////////////////////////////////////// 1473 | 1474 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 1475 | // Distributed under MIT license, or public domain if desired and 1476 | // recognized in your jurisdiction. 1477 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1478 | 1479 | #ifndef JSON_READER_H_INCLUDED 1480 | #define JSON_READER_H_INCLUDED 1481 | 1482 | #if !defined(JSON_IS_AMALGAMATION) 1483 | #include "json_features.h" 1484 | #include "value.h" 1485 | #endif // if !defined(JSON_IS_AMALGAMATION) 1486 | #include 1487 | #include 1488 | #include 1489 | #include 1490 | #include 1491 | 1492 | // Disable warning C4251: : needs to have dll-interface to 1493 | // be used by... 1494 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1495 | #pragma warning(push) 1496 | #pragma warning(disable : 4251) 1497 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1498 | 1499 | #pragma pack(push, 8) 1500 | 1501 | namespace Json { 1502 | 1503 | /** \brief Unserialize a JSON document into a 1504 | * Value. 1505 | * 1506 | * \deprecated Use CharReader and CharReaderBuilder. 1507 | */ 1508 | 1509 | class JSONCPP_DEPRECATED( 1510 | "Use CharReader and CharReaderBuilder instead.") JSON_API Reader { 1511 | public: 1512 | using Char = char; 1513 | using Location = const Char*; 1514 | 1515 | /** \brief An error tagged with where in the JSON text it was encountered. 1516 | * 1517 | * The offsets give the [start, limit) range of bytes within the text. Note 1518 | * that this is bytes, not codepoints. 1519 | */ 1520 | struct StructuredError { 1521 | ptrdiff_t offset_start; 1522 | ptrdiff_t offset_limit; 1523 | String message; 1524 | }; 1525 | 1526 | /** \brief Constructs a Reader allowing all features for parsing. 1527 | */ 1528 | JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") 1529 | Reader(); 1530 | 1531 | /** \brief Constructs a Reader allowing the specified feature set for parsing. 1532 | */ 1533 | JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") 1534 | Reader(const Features& features); 1535 | 1536 | /** \brief Read a Value from a JSON 1537 | * document. 1538 | * 1539 | * \param document UTF-8 encoded string containing the document 1540 | * to read. 1541 | * \param[out] root Contains the root value of the document if it 1542 | * was successfully parsed. 1543 | * \param collectComments \c true to collect comment and allow writing 1544 | * them back during serialization, \c false to 1545 | * discard comments. This parameter is ignored 1546 | * if Features::allowComments_ is \c false. 1547 | * \return \c true if the document was successfully parsed, \c false if an 1548 | * error occurred. 1549 | */ 1550 | bool parse(const std::string& document, Value& root, 1551 | bool collectComments = true); 1552 | 1553 | /** \brief Read a Value from a JSON 1554 | * document. 1555 | * 1556 | * \param beginDoc Pointer on the beginning of the UTF-8 encoded 1557 | * string of the document to read. 1558 | * \param endDoc Pointer on the end of the UTF-8 encoded string 1559 | * of the document to read. Must be >= beginDoc. 1560 | * \param[out] root Contains the root value of the document if it 1561 | * was successfully parsed. 1562 | * \param collectComments \c true to collect comment and allow writing 1563 | * them back during serialization, \c false to 1564 | * discard comments. This parameter is ignored 1565 | * if Features::allowComments_ is \c false. 1566 | * \return \c true if the document was successfully parsed, \c false if an 1567 | * error occurred. 1568 | */ 1569 | bool parse(const char* beginDoc, const char* endDoc, Value& root, 1570 | bool collectComments = true); 1571 | 1572 | /// \brief Parse from input stream. 1573 | /// \see Json::operator>>(std::istream&, Json::Value&). 1574 | bool parse(IStream& is, Value& root, bool collectComments = true); 1575 | 1576 | /** \brief Returns a user friendly string that list errors in the parsed 1577 | * document. 1578 | * 1579 | * \return Formatted error message with the list of errors with their 1580 | * location in the parsed document. An empty string is returned if no error 1581 | * occurred during parsing. 1582 | * \deprecated Use getFormattedErrorMessages() instead (typo fix). 1583 | */ 1584 | JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") 1585 | String getFormatedErrorMessages() const; 1586 | 1587 | /** \brief Returns a user friendly string that list errors in the parsed 1588 | * document. 1589 | * 1590 | * \return Formatted error message with the list of errors with their 1591 | * location in the parsed document. An empty string is returned if no error 1592 | * occurred during parsing. 1593 | */ 1594 | String getFormattedErrorMessages() const; 1595 | 1596 | /** \brief Returns a vector of structured errors encountered while parsing. 1597 | * 1598 | * \return A (possibly empty) vector of StructuredError objects. Currently 1599 | * only one error can be returned, but the caller should tolerate multiple 1600 | * errors. This can occur if the parser recovers from a non-fatal parse 1601 | * error and then encounters additional errors. 1602 | */ 1603 | std::vector getStructuredErrors() const; 1604 | 1605 | /** \brief Add a semantic error message. 1606 | * 1607 | * \param value JSON Value location associated with the error 1608 | * \param message The error message. 1609 | * \return \c true if the error was successfully added, \c false if the Value 1610 | * offset exceeds the document size. 1611 | */ 1612 | bool pushError(const Value& value, const String& message); 1613 | 1614 | /** \brief Add a semantic error message with extra context. 1615 | * 1616 | * \param value JSON Value location associated with the error 1617 | * \param message The error message. 1618 | * \param extra Additional JSON Value location to contextualize the error 1619 | * \return \c true if the error was successfully added, \c false if either 1620 | * Value offset exceeds the document size. 1621 | */ 1622 | bool pushError(const Value& value, const String& message, const Value& extra); 1623 | 1624 | /** \brief Return whether there are any errors. 1625 | * 1626 | * \return \c true if there are no errors to report \c false if errors have 1627 | * occurred. 1628 | */ 1629 | bool good() const; 1630 | 1631 | private: 1632 | enum TokenType { 1633 | tokenEndOfStream = 0, 1634 | tokenObjectBegin, 1635 | tokenObjectEnd, 1636 | tokenArrayBegin, 1637 | tokenArrayEnd, 1638 | tokenString, 1639 | tokenNumber, 1640 | tokenTrue, 1641 | tokenFalse, 1642 | tokenNull, 1643 | tokenArraySeparator, 1644 | tokenMemberSeparator, 1645 | tokenComment, 1646 | tokenError 1647 | }; 1648 | 1649 | class Token { 1650 | public: 1651 | TokenType type_; 1652 | Location start_; 1653 | Location end_; 1654 | }; 1655 | 1656 | class ErrorInfo { 1657 | public: 1658 | Token token_; 1659 | String message_; 1660 | Location extra_; 1661 | }; 1662 | 1663 | using Errors = std::deque; 1664 | 1665 | bool readToken(Token& token); 1666 | void skipSpaces(); 1667 | bool match(const Char* pattern, int patternLength); 1668 | bool readComment(); 1669 | bool readCStyleComment(); 1670 | bool readCppStyleComment(); 1671 | bool readString(); 1672 | void readNumber(); 1673 | bool readValue(); 1674 | bool readObject(Token& token); 1675 | bool readArray(Token& token); 1676 | bool decodeNumber(Token& token); 1677 | bool decodeNumber(Token& token, Value& decoded); 1678 | bool decodeString(Token& token); 1679 | bool decodeString(Token& token, String& decoded); 1680 | bool decodeDouble(Token& token); 1681 | bool decodeDouble(Token& token, Value& decoded); 1682 | bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, 1683 | unsigned int& unicode); 1684 | bool decodeUnicodeEscapeSequence(Token& token, Location& current, 1685 | Location end, unsigned int& unicode); 1686 | bool addError(const String& message, Token& token, Location extra = nullptr); 1687 | bool recoverFromError(TokenType skipUntilToken); 1688 | bool addErrorAndRecover(const String& message, Token& token, 1689 | TokenType skipUntilToken); 1690 | void skipUntilSpace(); 1691 | Value& currentValue(); 1692 | Char getNextChar(); 1693 | void getLocationLineAndColumn(Location location, int& line, 1694 | int& column) const; 1695 | String getLocationLineAndColumn(Location location) const; 1696 | void addComment(Location begin, Location end, CommentPlacement placement); 1697 | void skipCommentTokens(Token& token); 1698 | 1699 | static bool containsNewLine(Location begin, Location end); 1700 | static String normalizeEOL(Location begin, Location end); 1701 | 1702 | using Nodes = std::stack; 1703 | Nodes nodes_; 1704 | Errors errors_; 1705 | String document_; 1706 | Location begin_{}; 1707 | Location end_{}; 1708 | Location current_{}; 1709 | Location lastValueEnd_{}; 1710 | Value* lastValue_{}; 1711 | String commentsBefore_; 1712 | Features features_; 1713 | bool collectComments_{}; 1714 | }; // Reader 1715 | 1716 | /** Interface for reading JSON from a char array. 1717 | */ 1718 | class JSON_API CharReader { 1719 | public: 1720 | virtual ~CharReader() = default; 1721 | /** \brief Read a Value from a JSON 1722 | * document. The document must be a UTF-8 encoded string containing the 1723 | * document to read. 1724 | * 1725 | * \param beginDoc Pointer on the beginning of the UTF-8 encoded string 1726 | * of the document to read. 1727 | * \param endDoc Pointer on the end of the UTF-8 encoded string of the 1728 | * document to read. Must be >= beginDoc. 1729 | * \param[out] root Contains the root value of the document if it was 1730 | * successfully parsed. 1731 | * \param[out] errs Formatted error messages (if not NULL) a user 1732 | * friendly string that lists errors in the parsed 1733 | * document. 1734 | * \return \c true if the document was successfully parsed, \c false if an 1735 | * error occurred. 1736 | */ 1737 | virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, 1738 | String* errs) = 0; 1739 | 1740 | class JSON_API Factory { 1741 | public: 1742 | virtual ~Factory() = default; 1743 | /** \brief Allocate a CharReader via operator new(). 1744 | * \throw std::exception if something goes wrong (e.g. invalid settings) 1745 | */ 1746 | virtual CharReader* newCharReader() const = 0; 1747 | }; // Factory 1748 | }; // CharReader 1749 | 1750 | /** \brief Build a CharReader implementation. 1751 | * 1752 | * Usage: 1753 | * \code 1754 | * using namespace Json; 1755 | * CharReaderBuilder builder; 1756 | * builder["collectComments"] = false; 1757 | * Value value; 1758 | * String errs; 1759 | * bool ok = parseFromStream(builder, std::cin, &value, &errs); 1760 | * \endcode 1761 | */ 1762 | class JSON_API CharReaderBuilder : public CharReader::Factory { 1763 | public: 1764 | // Note: We use a Json::Value so that we can add data-members to this class 1765 | // without a major version bump. 1766 | /** Configuration of this builder. 1767 | * These are case-sensitive. 1768 | * Available settings (case-sensitive): 1769 | * - `"collectComments": false or true` 1770 | * - true to collect comment and allow writing them back during 1771 | * serialization, false to discard comments. This parameter is ignored 1772 | * if allowComments is false. 1773 | * - `"allowComments": false or true` 1774 | * - true if comments are allowed. 1775 | * - `"allowTrailingCommas": false or true` 1776 | * - true if trailing commas in objects and arrays are allowed. 1777 | * - `"strictRoot": false or true` 1778 | * - true if root must be either an array or an object value 1779 | * - `"allowDroppedNullPlaceholders": false or true` 1780 | * - true if dropped null placeholders are allowed. (See 1781 | * StreamWriterBuilder.) 1782 | * - `"allowNumericKeys": false or true` 1783 | * - true if numeric object keys are allowed. 1784 | * - `"allowSingleQuotes": false or true` 1785 | * - true if '' are allowed for strings (both keys and values) 1786 | * - `"stackLimit": integer` 1787 | * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an 1788 | * exception. 1789 | * - This is a security issue (seg-faults caused by deeply nested JSON), so 1790 | * the default is low. 1791 | * - `"failIfExtra": false or true` 1792 | * - If true, `parse()` returns false when extra non-whitespace trails the 1793 | * JSON value in the input string. 1794 | * - `"rejectDupKeys": false or true` 1795 | * - If true, `parse()` returns false when a key is duplicated within an 1796 | * object. 1797 | * - `"allowSpecialFloats": false or true` 1798 | * - If true, special float values (NaNs and infinities) are allowed and 1799 | * their values are lossfree restorable. 1800 | * 1801 | * You can examine 'settings_` yourself to see the defaults. You can also 1802 | * write and read them just like any JSON Value. 1803 | * \sa setDefaults() 1804 | */ 1805 | Json::Value settings_; 1806 | 1807 | CharReaderBuilder(); 1808 | ~CharReaderBuilder() override; 1809 | 1810 | CharReader* newCharReader() const override; 1811 | 1812 | /** \return true if 'settings' are legal and consistent; 1813 | * otherwise, indicate bad settings via 'invalid'. 1814 | */ 1815 | bool validate(Json::Value* invalid) const; 1816 | 1817 | /** A simple way to update a specific setting. 1818 | */ 1819 | Value& operator[](const String& key); 1820 | 1821 | /** Called by ctor, but you can use this to reset settings_. 1822 | * \pre 'settings' != NULL (but Json::null is fine) 1823 | * \remark Defaults: 1824 | * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults 1825 | */ 1826 | static void setDefaults(Json::Value* settings); 1827 | /** Same as old Features::strictMode(). 1828 | * \pre 'settings' != NULL (but Json::null is fine) 1829 | * \remark Defaults: 1830 | * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode 1831 | */ 1832 | static void strictMode(Json::Value* settings); 1833 | }; 1834 | 1835 | /** Consume entire stream and use its begin/end. 1836 | * Someday we might have a real StreamReader, but for now this 1837 | * is convenient. 1838 | */ 1839 | bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root, 1840 | String* errs); 1841 | 1842 | /** \brief Read from 'sin' into 'root'. 1843 | * 1844 | * Always keep comments from the input JSON. 1845 | * 1846 | * This can be used to read a file into a particular sub-object. 1847 | * For example: 1848 | * \code 1849 | * Json::Value root; 1850 | * cin >> root["dir"]["file"]; 1851 | * cout << root; 1852 | * \endcode 1853 | * Result: 1854 | * \verbatim 1855 | * { 1856 | * "dir": { 1857 | * "file": { 1858 | * // The input stream JSON would be nested here. 1859 | * } 1860 | * } 1861 | * } 1862 | * \endverbatim 1863 | * \throw std::exception on parse error. 1864 | * \see Json::operator<<() 1865 | */ 1866 | JSON_API IStream& operator>>(IStream&, Value&); 1867 | 1868 | } // namespace Json 1869 | 1870 | #pragma pack(pop) 1871 | 1872 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1873 | #pragma warning(pop) 1874 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1875 | 1876 | #endif // JSON_READER_H_INCLUDED 1877 | 1878 | // ////////////////////////////////////////////////////////////////////// 1879 | // End of content of file: include/json/reader.h 1880 | // ////////////////////////////////////////////////////////////////////// 1881 | 1882 | 1883 | 1884 | 1885 | 1886 | 1887 | // ////////////////////////////////////////////////////////////////////// 1888 | // Beginning of content of file: include/json/writer.h 1889 | // ////////////////////////////////////////////////////////////////////// 1890 | 1891 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 1892 | // Distributed under MIT license, or public domain if desired and 1893 | // recognized in your jurisdiction. 1894 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1895 | 1896 | #ifndef JSON_WRITER_H_INCLUDED 1897 | #define JSON_WRITER_H_INCLUDED 1898 | 1899 | #if !defined(JSON_IS_AMALGAMATION) 1900 | #include "value.h" 1901 | #endif // if !defined(JSON_IS_AMALGAMATION) 1902 | #include 1903 | #include 1904 | #include 1905 | 1906 | // Disable warning C4251: : needs to have dll-interface to 1907 | // be used by... 1908 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) 1909 | #pragma warning(push) 1910 | #pragma warning(disable : 4251) 1911 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1912 | 1913 | #pragma pack(push, 8) 1914 | 1915 | namespace Json { 1916 | 1917 | class Value; 1918 | 1919 | /** 1920 | * 1921 | * Usage: 1922 | * \code 1923 | * using namespace Json; 1924 | * void writeToStdout(StreamWriter::Factory const& factory, Value const& value) 1925 | * { std::unique_ptr const writer( factory.newStreamWriter()); 1926 | * writer->write(value, &std::cout); 1927 | * std::cout << std::endl; // add lf and flush 1928 | * } 1929 | * \endcode 1930 | */ 1931 | class JSON_API StreamWriter { 1932 | protected: 1933 | OStream* sout_; // not owned; will not delete 1934 | public: 1935 | StreamWriter(); 1936 | virtual ~StreamWriter(); 1937 | /** Write Value into document as configured in sub-class. 1938 | * Do not take ownership of sout, but maintain a reference during function. 1939 | * \pre sout != NULL 1940 | * \return zero on success (For now, we always return zero, so check the 1941 | * stream instead.) \throw std::exception possibly, depending on 1942 | * configuration 1943 | */ 1944 | virtual int write(Value const& root, OStream* sout) = 0; 1945 | 1946 | /** \brief A simple abstract factory. 1947 | */ 1948 | class JSON_API Factory { 1949 | public: 1950 | virtual ~Factory(); 1951 | /** \brief Allocate a CharReader via operator new(). 1952 | * \throw std::exception if something goes wrong (e.g. invalid settings) 1953 | */ 1954 | virtual StreamWriter* newStreamWriter() const = 0; 1955 | }; // Factory 1956 | }; // StreamWriter 1957 | 1958 | /** \brief Write into stringstream, then return string, for convenience. 1959 | * A StreamWriter will be created from the factory, used, and then deleted. 1960 | */ 1961 | String JSON_API writeString(StreamWriter::Factory const& factory, 1962 | Value const& root); 1963 | 1964 | /** \brief Build a StreamWriter implementation. 1965 | 1966 | * Usage: 1967 | * \code 1968 | * using namespace Json; 1969 | * Value value = ...; 1970 | * StreamWriterBuilder builder; 1971 | * builder["commentStyle"] = "None"; 1972 | * builder["indentation"] = " "; // or whatever you like 1973 | * std::unique_ptr writer( 1974 | * builder.newStreamWriter()); 1975 | * writer->write(value, &std::cout); 1976 | * std::cout << std::endl; // add lf and flush 1977 | * \endcode 1978 | */ 1979 | class JSON_API StreamWriterBuilder : public StreamWriter::Factory { 1980 | public: 1981 | // Note: We use a Json::Value so that we can add data-members to this class 1982 | // without a major version bump. 1983 | /** Configuration of this builder. 1984 | * Available settings (case-sensitive): 1985 | * - "commentStyle": "None" or "All" 1986 | * - "indentation": "". 1987 | * - Setting this to an empty string also omits newline characters. 1988 | * - "enableYAMLCompatibility": false or true 1989 | * - slightly change the whitespace around colons 1990 | * - "dropNullPlaceholders": false or true 1991 | * - Drop the "null" string from the writer's output for nullValues. 1992 | * Strictly speaking, this is not valid JSON. But when the output is being 1993 | * fed to a browser's JavaScript, it makes for smaller output and the 1994 | * browser can handle the output just fine. 1995 | * - "useSpecialFloats": false or true 1996 | * - If true, outputs non-finite floating point values in the following way: 1997 | * NaN values as "NaN", positive infinity as "Infinity", and negative 1998 | * infinity as "-Infinity". 1999 | * - "precision": int 2000 | * - Number of precision digits for formatting of real values. 2001 | * - "precisionType": "significant"(default) or "decimal" 2002 | * - Type of precision for formatting of real values. 2003 | * - "emitUTF8": false or true 2004 | * - If true, outputs raw UTF8 strings instead of escaping them. 2005 | 2006 | * You can examine 'settings_` yourself 2007 | * to see the defaults. You can also write and read them just like any 2008 | * JSON Value. 2009 | * \sa setDefaults() 2010 | */ 2011 | Json::Value settings_; 2012 | 2013 | StreamWriterBuilder(); 2014 | ~StreamWriterBuilder() override; 2015 | 2016 | /** 2017 | * \throw std::exception if something goes wrong (e.g. invalid settings) 2018 | */ 2019 | StreamWriter* newStreamWriter() const override; 2020 | 2021 | /** \return true if 'settings' are legal and consistent; 2022 | * otherwise, indicate bad settings via 'invalid'. 2023 | */ 2024 | bool validate(Json::Value* invalid) const; 2025 | /** A simple way to update a specific setting. 2026 | */ 2027 | Value& operator[](const String& key); 2028 | 2029 | /** Called by ctor, but you can use this to reset settings_. 2030 | * \pre 'settings' != NULL (but Json::null is fine) 2031 | * \remark Defaults: 2032 | * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults 2033 | */ 2034 | static void setDefaults(Json::Value* settings); 2035 | }; 2036 | 2037 | /** \brief Abstract class for writers. 2038 | * \deprecated Use StreamWriter. (And really, this is an implementation detail.) 2039 | */ 2040 | class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { 2041 | public: 2042 | virtual ~Writer(); 2043 | 2044 | virtual String write(const Value& root) = 0; 2045 | }; 2046 | 2047 | /** \brief Outputs a Value in JSON format 2048 | *without formatting (not human friendly). 2049 | * 2050 | * The JSON document is written in a single line. It is not intended for 'human' 2051 | *consumption, 2052 | * but may be useful to support feature such as RPC where bandwidth is limited. 2053 | * \sa Reader, Value 2054 | * \deprecated Use StreamWriterBuilder. 2055 | */ 2056 | #if defined(_MSC_VER) 2057 | #pragma warning(push) 2058 | #pragma warning(disable : 4996) // Deriving from deprecated class 2059 | #endif 2060 | class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter 2061 | : public Writer { 2062 | public: 2063 | FastWriter(); 2064 | ~FastWriter() override = default; 2065 | 2066 | void enableYAMLCompatibility(); 2067 | 2068 | /** \brief Drop the "null" string from the writer's output for nullValues. 2069 | * Strictly speaking, this is not valid JSON. But when the output is being 2070 | * fed to a browser's JavaScript, it makes for smaller output and the 2071 | * browser can handle the output just fine. 2072 | */ 2073 | void dropNullPlaceholders(); 2074 | 2075 | void omitEndingLineFeed(); 2076 | 2077 | public: // overridden from Writer 2078 | String write(const Value& root) override; 2079 | 2080 | private: 2081 | void writeValue(const Value& value); 2082 | 2083 | String document_; 2084 | bool yamlCompatibilityEnabled_{false}; 2085 | bool dropNullPlaceholders_{false}; 2086 | bool omitEndingLineFeed_{false}; 2087 | }; 2088 | #if defined(_MSC_VER) 2089 | #pragma warning(pop) 2090 | #endif 2091 | 2092 | /** \brief Writes a Value in JSON format in a 2093 | *human friendly way. 2094 | * 2095 | * The rules for line break and indent are as follow: 2096 | * - Object value: 2097 | * - if empty then print {} without indent and line break 2098 | * - if not empty the print '{', line break & indent, print one value per 2099 | *line 2100 | * and then unindent and line break and print '}'. 2101 | * - Array value: 2102 | * - if empty then print [] without indent and line break 2103 | * - if the array contains no object value, empty array or some other value 2104 | *types, 2105 | * and all the values fit on one lines, then print the array on a single 2106 | *line. 2107 | * - otherwise, it the values do not fit on one line, or the array contains 2108 | * object or non empty array, then print one value per line. 2109 | * 2110 | * If the Value have comments then they are outputed according to their 2111 | *#CommentPlacement. 2112 | * 2113 | * \sa Reader, Value, Value::setComment() 2114 | * \deprecated Use StreamWriterBuilder. 2115 | */ 2116 | #if defined(_MSC_VER) 2117 | #pragma warning(push) 2118 | #pragma warning(disable : 4996) // Deriving from deprecated class 2119 | #endif 2120 | class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API 2121 | StyledWriter : public Writer { 2122 | public: 2123 | StyledWriter(); 2124 | ~StyledWriter() override = default; 2125 | 2126 | public: // overridden from Writer 2127 | /** \brief Serialize a Value in JSON format. 2128 | * \param root Value to serialize. 2129 | * \return String containing the JSON document that represents the root value. 2130 | */ 2131 | String write(const Value& root) override; 2132 | 2133 | private: 2134 | void writeValue(const Value& value); 2135 | void writeArrayValue(const Value& value); 2136 | bool isMultilineArray(const Value& value); 2137 | void pushValue(const String& value); 2138 | void writeIndent(); 2139 | void writeWithIndent(const String& value); 2140 | void indent(); 2141 | void unindent(); 2142 | void writeCommentBeforeValue(const Value& root); 2143 | void writeCommentAfterValueOnSameLine(const Value& root); 2144 | static bool hasCommentForValue(const Value& value); 2145 | static String normalizeEOL(const String& text); 2146 | 2147 | using ChildValues = std::vector; 2148 | 2149 | ChildValues childValues_; 2150 | String document_; 2151 | String indentString_; 2152 | unsigned int rightMargin_{74}; 2153 | unsigned int indentSize_{3}; 2154 | bool addChildValues_{false}; 2155 | }; 2156 | #if defined(_MSC_VER) 2157 | #pragma warning(pop) 2158 | #endif 2159 | 2160 | /** \brief Writes a Value in JSON format in a 2161 | human friendly way, 2162 | to a stream rather than to a string. 2163 | * 2164 | * The rules for line break and indent are as follow: 2165 | * - Object value: 2166 | * - if empty then print {} without indent and line break 2167 | * - if not empty the print '{', line break & indent, print one value per 2168 | line 2169 | * and then unindent and line break and print '}'. 2170 | * - Array value: 2171 | * - if empty then print [] without indent and line break 2172 | * - if the array contains no object value, empty array or some other value 2173 | types, 2174 | * and all the values fit on one lines, then print the array on a single 2175 | line. 2176 | * - otherwise, it the values do not fit on one line, or the array contains 2177 | * object or non empty array, then print one value per line. 2178 | * 2179 | * If the Value have comments then they are outputed according to their 2180 | #CommentPlacement. 2181 | * 2182 | * \sa Reader, Value, Value::setComment() 2183 | * \deprecated Use StreamWriterBuilder. 2184 | */ 2185 | #if defined(_MSC_VER) 2186 | #pragma warning(push) 2187 | #pragma warning(disable : 4996) // Deriving from deprecated class 2188 | #endif 2189 | class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API 2190 | StyledStreamWriter { 2191 | public: 2192 | /** 2193 | * \param indentation Each level will be indented by this amount extra. 2194 | */ 2195 | StyledStreamWriter(String indentation = "\t"); 2196 | ~StyledStreamWriter() = default; 2197 | 2198 | public: 2199 | /** \brief Serialize a Value in JSON format. 2200 | * \param out Stream to write to. (Can be ostringstream, e.g.) 2201 | * \param root Value to serialize. 2202 | * \note There is no point in deriving from Writer, since write() should not 2203 | * return a value. 2204 | */ 2205 | void write(OStream& out, const Value& root); 2206 | 2207 | private: 2208 | void writeValue(const Value& value); 2209 | void writeArrayValue(const Value& value); 2210 | bool isMultilineArray(const Value& value); 2211 | void pushValue(const String& value); 2212 | void writeIndent(); 2213 | void writeWithIndent(const String& value); 2214 | void indent(); 2215 | void unindent(); 2216 | void writeCommentBeforeValue(const Value& root); 2217 | void writeCommentAfterValueOnSameLine(const Value& root); 2218 | static bool hasCommentForValue(const Value& value); 2219 | static String normalizeEOL(const String& text); 2220 | 2221 | using ChildValues = std::vector; 2222 | 2223 | ChildValues childValues_; 2224 | OStream* document_; 2225 | String indentString_; 2226 | unsigned int rightMargin_{74}; 2227 | String indentation_; 2228 | bool addChildValues_ : 1; 2229 | bool indented_ : 1; 2230 | }; 2231 | #if defined(_MSC_VER) 2232 | #pragma warning(pop) 2233 | #endif 2234 | 2235 | #if defined(JSON_HAS_INT64) 2236 | String JSON_API valueToString(Int value); 2237 | String JSON_API valueToString(UInt value); 2238 | #endif // if defined(JSON_HAS_INT64) 2239 | String JSON_API valueToString(LargestInt value); 2240 | String JSON_API valueToString(LargestUInt value); 2241 | String JSON_API valueToString( 2242 | double value, unsigned int precision = Value::defaultRealPrecision, 2243 | PrecisionType precisionType = PrecisionType::significantDigits); 2244 | String JSON_API valueToString(bool value); 2245 | String JSON_API valueToQuotedString(const char* value); 2246 | 2247 | /// \brief Output using the StyledStreamWriter. 2248 | /// \see Json::operator>>() 2249 | JSON_API OStream& operator<<(OStream&, const Value& root); 2250 | 2251 | } // namespace Json 2252 | 2253 | #pragma pack(pop) 2254 | 2255 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 2256 | #pragma warning(pop) 2257 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 2258 | 2259 | #endif // JSON_WRITER_H_INCLUDED 2260 | 2261 | // ////////////////////////////////////////////////////////////////////// 2262 | // End of content of file: include/json/writer.h 2263 | // ////////////////////////////////////////////////////////////////////// 2264 | 2265 | 2266 | 2267 | 2268 | 2269 | 2270 | // ////////////////////////////////////////////////////////////////////// 2271 | // Beginning of content of file: include/json/assertions.h 2272 | // ////////////////////////////////////////////////////////////////////// 2273 | 2274 | // Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 2275 | // Distributed under MIT license, or public domain if desired and 2276 | // recognized in your jurisdiction. 2277 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 2278 | 2279 | #ifndef JSON_ASSERTIONS_H_INCLUDED 2280 | #define JSON_ASSERTIONS_H_INCLUDED 2281 | 2282 | #include 2283 | #include 2284 | 2285 | #if !defined(JSON_IS_AMALGAMATION) 2286 | #include "config.h" 2287 | #endif // if !defined(JSON_IS_AMALGAMATION) 2288 | 2289 | /** It should not be possible for a maliciously designed file to 2290 | * cause an abort() or seg-fault, so these macros are used only 2291 | * for pre-condition violations and internal logic errors. 2292 | */ 2293 | #if JSON_USE_EXCEPTION 2294 | 2295 | // @todo <= add detail about condition in exception 2296 | #define JSON_ASSERT(condition) \ 2297 | do { \ 2298 | if (!(condition)) { \ 2299 | Json::throwLogicError("assert json failed"); \ 2300 | } \ 2301 | } while (0) 2302 | 2303 | #define JSON_FAIL_MESSAGE(message) \ 2304 | do { \ 2305 | OStringStream oss; \ 2306 | oss << message; \ 2307 | Json::throwLogicError(oss.str()); \ 2308 | abort(); \ 2309 | } while (0) 2310 | 2311 | #else // JSON_USE_EXCEPTION 2312 | 2313 | #define JSON_ASSERT(condition) assert(condition) 2314 | 2315 | // The call to assert() will show the failure message in debug builds. In 2316 | // release builds we abort, for a core-dump or debugger. 2317 | #define JSON_FAIL_MESSAGE(message) \ 2318 | { \ 2319 | OStringStream oss; \ 2320 | oss << message; \ 2321 | assert(false && oss.str().c_str()); \ 2322 | abort(); \ 2323 | } 2324 | 2325 | #endif 2326 | 2327 | #define JSON_ASSERT_MESSAGE(condition, message) \ 2328 | do { \ 2329 | if (!(condition)) { \ 2330 | JSON_FAIL_MESSAGE(message); \ 2331 | } \ 2332 | } while (0) 2333 | 2334 | #endif // JSON_ASSERTIONS_H_INCLUDED 2335 | 2336 | // ////////////////////////////////////////////////////////////////////// 2337 | // End of content of file: include/json/assertions.h 2338 | // ////////////////////////////////////////////////////////////////////// 2339 | 2340 | 2341 | 2342 | 2343 | 2344 | #endif //ifndef JSON_AMALGAMATED_H_INCLUDED 2345 | -------------------------------------------------------------------------------- /tmpl/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |

Twitch API Credentials

7 |
8 |
9 |
10 |
Client ID:
11 | 12 |
13 |
14 |
Client Secret:
15 | 16 |
17 |
18 |
Redirect URL
19 | 20 |
Set this as allowed redirect URL in your Twitch-App. 21 |
The default URL just echos out the code received from Twitch. 22 |
Replace with your own or dummy and copy code from URL if desired.
23 |
24 |
25 |
Auth Code
26 | 27 |
Auth code received from Twitch Post-Auth. Entering this will log you in. 28 |
29 |
30 | 31 | 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |

Twitch Authentication

41 |
42 |
43 |
44 | Logged in as: 45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 |
55 |
56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /twitchtmi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "twitchtmi.h" 15 | #include "jload.h" 16 | 17 | #if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 7) 18 | #error This module needs at least ZNC 1.7.0 or later. 19 | #endif 20 | 21 | TwitchTMI::~TwitchTMI() 22 | { 23 | 24 | } 25 | 26 | bool TwitchTMI::OnLoad(const CString& sArgsi, CString& sMessage) 27 | { 28 | OnBoot(); 29 | 30 | if(GetNetwork()) 31 | { 32 | std::list channels; 33 | 34 | for(CChan *ch: GetNetwork()->GetChans()) 35 | { 36 | CString sname = ch->GetName().substr(1); 37 | sname.MakeLower(); 38 | channels.push_back(sname); 39 | 40 | ch->SetTopic(GetNV("tmi_topic_" + sname)); 41 | ch->SetTopicOwner("jtv"); 42 | ch->SetTopicDate(GetNV("tmi_topic_time_" + sname).ToULong()); 43 | } 44 | 45 | CThreadPool::Get().addJob(new TwitchTMIJob(this, channels)); 46 | } 47 | 48 | if(GetArgs().Token(0) != "FrankerZ") { 49 | lastFrankerZ = std::numeric_limits::max(); 50 | noLastPlay = true; 51 | } 52 | 53 | PutIRC("CAP REQ :twitch.tv/membership"); 54 | PutIRC("CAP REQ :twitch.tv/commands"); 55 | PutIRC("CAP REQ :twitch.tv/tags"); 56 | 57 | return true; 58 | } 59 | 60 | bool TwitchTMI::OnBoot() 61 | { 62 | initCurl(); 63 | 64 | updateTimer = new TwitchTMIUpdateTimer(this); 65 | AddTimer(updateTimer); 66 | 67 | refreshTimer = new TwitchTokenRefreshTimer(this); 68 | AddTimer(refreshTimer); 69 | 70 | return true; 71 | } 72 | 73 | CString TwitchTMI::GetWebMenuTitle() 74 | { 75 | return "Twitch"; 76 | } 77 | 78 | void TwitchTMI::DoTwitchLogin(const CString &code) 79 | { 80 | CString authUrl = "https://id.twitch.tv/oauth2/token?client_id=" + GetNV("ClientID"); 81 | authUrl += "&client_secret=" + GetNV("ClientSecret"); 82 | authUrl += "&code=" + code; 83 | authUrl += "&grant_type=authorization_code"; 84 | authUrl += "&redirect_uri=" + GetNV("RedirectUrl"); 85 | 86 | try 87 | { 88 | Json::Value res = getJsonFromUrl(authUrl, { "Client-ID: " + GetNV("ClientID") }, "{}"); 89 | Json::Value root = getJsonFromUrl("https://api.twitch.tv/helix/users", { "Client-ID: " + GetNV("ClientID"), "Authorization: Bearer " + res["access_token"].asString() }); 90 | 91 | SetNV("TwitchUser", root["data"][0]["display_name"].asString()); 92 | SetNV("RefreshToken", res["refresh_token"].asString()); 93 | } catch(const Json::Exception&) { 94 | CUtils::PrintError("Twitch login failed."); 95 | DelNV("TwitchUser"); 96 | DelNV("RefreshToken"); 97 | return; 98 | } 99 | } 100 | 101 | bool TwitchTMI::OnWebRequest(CWebSock &sock, const CString &pageName, CTemplate &tmpl) 102 | { 103 | if (pageName != "index") 104 | return false; 105 | 106 | if (sock.IsPost()) { 107 | SetNV("ClientID", sock.GetParam("ClientID")); 108 | CString secret = sock.GetParam("ClientSecret"); 109 | if (!secret.empty()) 110 | SetNV("ClientSecret", secret); 111 | SetNV("RedirectUrl", sock.GetParam("RedirectUrl")); 112 | 113 | CString code = sock.GetParam("AuthCode"); 114 | if (!code.empty()) 115 | { 116 | DoTwitchLogin(code); 117 | } 118 | else 119 | { 120 | DelNV("TwitchUser"); 121 | DelNV("RefreshToken"); 122 | } 123 | } 124 | 125 | CString redirUrl = GetNV("RedirectUrl"); 126 | if (redirUrl.empty()) 127 | redirUrl = "https://btbn.de/twitch_state.php"; 128 | tmpl["RedirectUrl"] = redirUrl; 129 | tmpl["ClientID"] = GetNV("ClientID"); 130 | 131 | CString twUser = GetNV("TwitchUser"); 132 | if (twUser.empty()) 133 | twUser = "Not logged in"; 134 | tmpl["LoggedInUser"] = twUser; 135 | 136 | return true; 137 | } 138 | 139 | CString TwitchTMI::GetTwitchAccessToken() 140 | { 141 | CString refresh_token = GetNV("RefreshToken"); 142 | if (refresh_token.empty()) 143 | return CString(); 144 | 145 | CString authUrl = "https://id.twitch.tv/oauth2/token?client_id=" + GetNV("ClientID"); 146 | authUrl += "&client_secret=" + GetNV("ClientSecret"); 147 | authUrl += "&refresh_token=" + refresh_token; 148 | authUrl += "&grant_type=refresh_token"; 149 | 150 | try 151 | { 152 | Json::Value res = getJsonFromUrl(authUrl, { "Client-ID: " + GetNV("ClientID") }, "{}"); 153 | SetNV("RefreshToken", res["refresh_token"].asString()); 154 | return res["access_token"].asString(); 155 | } catch(const Json::Exception&) { 156 | CUtils::PrintError("Failed getting Twitch access token."); 157 | return CString(); 158 | } 159 | } 160 | 161 | CModule::EModRet TwitchTMI::OnIRCRegistration(CString &sPass, CString &sNick, CString &sIdent, CString &sRealName) 162 | { 163 | CString token = GetTwitchAccessToken(); 164 | if (token.empty()) 165 | return CModule::CONTINUE; 166 | 167 | sPass = "oauth:" + token; 168 | sNick = GetNV("TwitchUser"); 169 | 170 | return CModule::HALTMODS; 171 | } 172 | 173 | void TwitchTMI::OnIRCConnected() 174 | { 175 | PutIRC("CAP REQ :twitch.tv/membership"); 176 | PutIRC("CAP REQ :twitch.tv/commands"); 177 | PutIRC("CAP REQ :twitch.tv/tags"); 178 | } 179 | 180 | CModule::EModRet TwitchTMI::OnUserRawMessage(CMessage &msg) 181 | { 182 | const CString &cmd = msg.GetCommand(); 183 | if(cmd.Equals("WHO") || cmd.Equals("AWAY") || cmd.Equals("TWITCHCLIENT") || cmd.Equals("JTVCLIENT")) 184 | { 185 | return CModule::HALT; 186 | } 187 | else if(cmd.Equals("KICK")) 188 | { 189 | VCString params = msg.GetParams(); 190 | if(params.size() < 2) 191 | return CModule::HALT; 192 | msg.SetCommand("PRIVMSG"); 193 | params = { params[0], "/timeout " + CString(" ").Join(std::next(params.begin()), params.end()) }; 194 | msg.SetParams(params); 195 | } 196 | else if(cmd.Equals("MODE")) 197 | { 198 | VCString params = msg.GetParams(); 199 | if(params.size() != 3) 200 | return CModule::HALT; 201 | 202 | CString victim, mode = params[1]; 203 | 204 | CNick victimNick; 205 | victimNick.Parse(params[2]); 206 | 207 | if(mode.Equals("+b")) 208 | mode = "/ban"; 209 | else if(mode.Equals("-b")) 210 | mode = "/unban"; 211 | else if(mode.Equals("+o")) 212 | mode = "/mod"; 213 | else if(mode.Equals("-o")) 214 | mode = "/unmod"; 215 | else 216 | { 217 | PutStatus("Unknown mode change: " + mode); 218 | return CModule::HALT; 219 | } 220 | 221 | if(victimNick.GetHost().Contains(".tmi.twitch.tv")) 222 | victim = victimNick.GetHost().Token(0, false, "."); 223 | else 224 | victim = victimNick.GetNick(); 225 | 226 | msg.SetCommand("PRIVMSG"); 227 | params = { params[0], mode + " " + victim }; 228 | msg.SetParams(params); 229 | } 230 | 231 | return CModule::CONTINUE; 232 | } 233 | 234 | static CString subPlanToName(const CString &plan) 235 | { 236 | if(plan.AsLower() == "prime") 237 | return "Twitch Prime"; 238 | else if(plan == "1000") 239 | return "Tier 1"; 240 | else if(plan == "2000") 241 | return "Tier 2"; 242 | else if(plan == "3000"); 243 | return "Tier 3"; 244 | return plan; 245 | } 246 | 247 | CModule::EModRet TwitchTMI::OnRawMessage(CMessage &msg) 248 | { 249 | CString realNick = msg.GetTag("display-name").Trim_n(); 250 | if (realNick == "") 251 | realNick = msg.GetNick().GetNick(); 252 | 253 | if(msg.GetCommand().Equals("HOSTTARGET")) 254 | { 255 | return CModule::HALT; 256 | } 257 | else if(msg.GetCommand().Equals("CLEARCHAT")) 258 | { 259 | msg.SetCommand("NOTICE"); 260 | if(msg.GetParam(1) != "") 261 | { 262 | msg.SetParam(1, msg.GetParam(1) + " was timed out."); 263 | } 264 | else 265 | { 266 | msg.SetParam(1, "Chat was cleared by a moderator."); 267 | } 268 | } 269 | else if(msg.GetCommand().Equals("USERSTATE")) 270 | { 271 | return CModule::HALT; 272 | } 273 | else if(msg.GetCommand().Equals("ROOMSTATE")) 274 | { 275 | return CModule::HALT; 276 | } 277 | else if(msg.GetCommand().Equals("RECONNECT")) 278 | { 279 | return CModule::HALT; 280 | } 281 | else if(msg.GetCommand().Equals("GLOBALUSERSTATE")) 282 | { 283 | return CModule::HALT; 284 | } 285 | else if(msg.GetCommand().Equals("WHISPER")) 286 | { 287 | msg.SetCommand("PRIVMSG"); 288 | } 289 | else if(msg.GetCommand().Equals("USERNOTICE")) 290 | { 291 | CString msg_id = msg.GetTag("msg-id"); 292 | CString new_msg = " from " + realNick; 293 | 294 | CString sys_msg = msg.GetTag("system-msg"); 295 | if(!sys_msg.empty()) 296 | new_msg = sys_msg; 297 | 298 | if(msg_id == "sub" || msg_id == "resub") { 299 | CString dur = msg.GetTag("msg-param-cumulative-months"); 300 | CString strk = msg.GetTag("msg-param-streak-months"); 301 | CString do_strk = msg.GetTag("msg-param-should-share-streak"); 302 | CString plan = subPlanToName(msg.GetTag("msg-param-sub-plan")); 303 | CString txt = msg.GetParam(1).Trim_n(); 304 | 305 | new_msg = realNick + " just "; 306 | if(msg_id == "sub") 307 | new_msg += "subscribed with a " + plan + " sub"; 308 | else 309 | new_msg += "resubscribed with a " + plan + " sub for " + dur + " months"; 310 | 311 | if(do_strk != "0") 312 | new_msg += ", currently on a " + strk + " month streak"; 313 | 314 | if(txt != "") 315 | new_msg += " saying: " + txt; 316 | else 317 | new_msg += "!"; 318 | } else if(msg_id == "subgift") { 319 | CString rec = msg.GetTag("msg-param-recipient-display-name"); 320 | CString plan = subPlanToName(msg.GetTag("msg-param-sub-plan")); 321 | 322 | new_msg = realNick + " gifted a " + plan + " sub to " + rec + "!"; 323 | } else if(msg_id == "raid") { 324 | CString cnt = msg.GetTag("msg-param-viewerCount"); 325 | CString src = msg.GetTag("msg-param-displayName"); 326 | 327 | new_msg = src + " is raiding with a party of " + cnt + "!"; 328 | } else if(msg_id == "ritual") { 329 | CString rit = msg.GetTag("msg-param-ritual-name"); 330 | CString txt = msg.GetParam(1).Trim_n(); 331 | 332 | new_msg = ""; 333 | if(rit == "new_chatter") 334 | new_msg = realNick + " is new here, saying: " + txt; 335 | } else if(msg_id == "giftpaidupgrade" || msg_id == "submysterygift") { 336 | new_msg = sys_msg; 337 | } 338 | 339 | realNick = "ttv"; 340 | new_msg = ">>> " + new_msg + " <<<"; 341 | msg.SetCommand("PRIVMSG"); 342 | msg.SetParam(1, new_msg); 343 | } 344 | 345 | msg.GetNick().SetNick(realNick); 346 | 347 | if(realNick == "ttv") 348 | msg.GetNick().Parse("ttv!ttv@ttv.tmi.twitch.tv"); 349 | 350 | return CModule::CONTINUE; 351 | } 352 | 353 | CModule::EModRet TwitchTMI::OnUserJoin(CString& sChannel, CString& sKey) 354 | { 355 | return CModule::CONTINUE; 356 | } 357 | 358 | CModule::EModRet TwitchTMI::OnPrivTextMessage(CTextMessage &Message) 359 | { 360 | if(Message.GetNick().GetNick().Equals("jtv")) 361 | return CModule::HALT; 362 | 363 | return CModule::CONTINUE; 364 | } 365 | 366 | void TwitchTMI::PutUserChanMessage(CChan *chan, const CString &from, const CString &msg) 367 | { 368 | std::stringstream ss; 369 | ss << ":" << from << " PRIVMSG " << chan->GetName() << " :"; 370 | CString s = ss.str(); 371 | 372 | PutUser(s + msg); 373 | 374 | if(!chan->AutoClearChanBuffer() || !GetNetwork()->IsUserOnline() || chan->IsDetached()) 375 | chan->AddBuffer(s + "{text}", msg); 376 | } 377 | 378 | void TwitchTMI::InjectMessageHelper(CChan *chan, const CString &action) 379 | { 380 | std::stringstream ss; 381 | CString mynick = GetNetwork()->GetIRCNick().GetNickMask(); 382 | 383 | ss << "PRIVMSG " << chan->GetName() << " :" << action; 384 | PutIRC(ss.str()); 385 | 386 | AddJob(new GenericJob(this, "put_msg_job_" + chan->GetName(), "put msg helper", []() {}, [this, chan, mynick, action]() 387 | { 388 | PutUserChanMessage(chan, mynick, action); 389 | })); 390 | } 391 | 392 | CModule::EModRet TwitchTMI::OnChanTextMessage(CTextMessage &Message) 393 | { 394 | if(Message.GetNick().GetNick().Equals("jtv")) 395 | return CModule::HALT; 396 | 397 | if(Message.GetNick().GetNick().AsLower().Equals("hentaitheace")) 398 | return CModule::CONTINUE; 399 | 400 | CChan *chan = Message.GetChan(); 401 | const CString &cname = chan->GetName(); 402 | 403 | if(Message.GetText() == "FrankerZ" && std::time(nullptr) - lastFrankerZ > 10) 404 | { 405 | InjectMessageHelper(chan, "FrankerZ"); 406 | lastFrankerZ = std::time(nullptr); 407 | } 408 | else if(Message.GetText() == "!play" && !noLastPlay) 409 | { 410 | std::time_t now = std::time(nullptr); 411 | auto it = lastPlay.find(cname); 412 | 413 | if(it == lastPlay.end() || now - it->second.first >= 30) // seconds for related !play 414 | { 415 | lastPlay[cname] = std::make_pair(now, 1); 416 | } 417 | else if(now - it->second.first < 0) 418 | { 419 | // We are in cooldown, do nothing 420 | } 421 | else if(it->second.second < 3) // count of related !play 422 | { 423 | it->second.first = now; 424 | it->second.second += 1; 425 | } 426 | else 427 | { 428 | it->second = std::make_pair(now + 30, 1); // cooldown in seconds 429 | 430 | AddTimer(new GenericTimer(this, 5, 1, "play_timer_" + cname, "Writes !play in n seconds!", [this, chan]() 431 | { 432 | InjectMessageHelper(chan, "!play"); 433 | })); 434 | } 435 | } 436 | 437 | return CModule::CONTINUE; 438 | } 439 | 440 | bool TwitchTMI::OnServerCapAvailable(const CString &sCap) 441 | { 442 | if(sCap == "twitch.tv/membership") 443 | return true; 444 | else if(sCap == "twitch.tv/tags") 445 | return true; 446 | else if(sCap == "twitch.tv/commands") 447 | return true; 448 | 449 | return false; 450 | } 451 | 452 | CModule::EModRet TwitchTMI::OnUserTextMessage(CTextMessage &msg) 453 | { 454 | if(msg.GetTarget().Left(1).Equals("#")) 455 | return CModule::CONTINUE; 456 | 457 | msg.SetText(msg.GetText().insert(0, " ").insert(0, msg.GetTarget()).insert(0, "/w ")); 458 | msg.SetTarget("#jtv"); 459 | 460 | return CModule::CONTINUE; 461 | } 462 | 463 | 464 | TwitchTokenRefreshTimer::TwitchTokenRefreshTimer(TwitchTMI *tmod) 465 | :CTimer(tmod, 21600, 0, "TwitchTokenRefreshTimer", "Refreshes Twitch Auth-Token") 466 | ,mod(tmod) 467 | { 468 | } 469 | 470 | void TwitchTokenRefreshTimer::RunJob() 471 | { 472 | CString twUser = mod->GetNV("TwitchUser"); 473 | if(twUser.empty()) 474 | return; 475 | 476 | CString res = mod->GetTwitchAccessToken(); 477 | 478 | if(res.empty()) 479 | { 480 | CUtils::PrintError("Failed refreshing oauth token for " + twUser); 481 | } 482 | else 483 | { 484 | CUtils::PrintMessage("Refreshed oauth token for " + twUser); 485 | } 486 | } 487 | 488 | 489 | TwitchTMIUpdateTimer::TwitchTMIUpdateTimer(TwitchTMI *tmod) 490 | :CTimer(tmod, 10, 0, "TwitchTMIUpdateTimer", "Downloads Twitch information") 491 | ,mod(tmod) 492 | { 493 | } 494 | 495 | void TwitchTMIUpdateTimer::RunJob() 496 | { 497 | if(!mod->GetNetwork()) 498 | return; 499 | 500 | std::list channels; 501 | 502 | for(CChan *chan: mod->GetNetwork()->GetChans()) 503 | { 504 | channels.push_back(chan->GetName().substr(1)); 505 | } 506 | 507 | mod->AddJob(new TwitchTMIJob(mod, channels)); 508 | } 509 | 510 | 511 | void TwitchTMIJob::runThread() 512 | { 513 | return; 514 | 515 | std::unique_lock lock_guard(mod->job_thread_lock, std::try_to_lock); 516 | 517 | if(!lock_guard.owns_lock() || !channels.size()) 518 | { 519 | channels.clear(); 520 | return; 521 | } 522 | 523 | Json::Value root = getJsonFromUrl( 524 | "https://gql.twitch.tv/gql", 525 | { "Content-Type: application/json" }, 526 | "{ \"variables\": { \"logins\": [\"" + CString("\", \"").Join(channels.begin(), channels.end()) + "\"] }," 527 | " \"query\": \"query user_status($logins: [String!]){ users(logins: $logins) { broadcastSettings{ game{ name } title } login stream{ type } } }\" }"); 528 | if(root.isNull()) 529 | return; 530 | 531 | Json::Value data; 532 | try 533 | { 534 | data = root["data"]; 535 | if(!data.isObject()) 536 | return; 537 | data = data["users"]; 538 | if(!data.isArray()) 539 | return; 540 | } catch(const Json::Exception&) { 541 | return; 542 | } 543 | 544 | for(Json::ArrayIndex i = 0; i < data.size(); i++) 545 | { 546 | try 547 | { 548 | Json::Value val = data[i]; 549 | if(!val.isObject()) 550 | continue; 551 | 552 | CString channel = val["login"].asString(); 553 | CString title = val["broadcastSettings"]["title"].asString(); 554 | CString type = val["stream"]["type"].asString(); 555 | 556 | CString game; 557 | Json::Value gameVal = val["broadcastSettings"]["game"]; 558 | if(gameVal.isObject()) 559 | game = gameVal.get("name", "").asString(); 560 | 561 | channel.MakeLower(); 562 | 563 | if(!game.Equals("")) 564 | title += " [" + game + "]"; 565 | 566 | bool isLive = !type.Equals(""); 567 | lives[channel] = isLive; 568 | 569 | if(isLive && !type.Equals("live")) 570 | title += " [" + type.MakeUpper() + "]"; 571 | else if(!isLive) 572 | title += " [OFFLINE]"; 573 | 574 | titles[channel] = title; 575 | } catch(const Json::Exception&) { 576 | continue; 577 | } 578 | } 579 | } 580 | 581 | void TwitchTMIJob::runMain() 582 | { 583 | for(CString channel: channels) 584 | { 585 | CChan *chan = mod->GetNetwork()->FindChan(CString("#") + channel); 586 | if(!chan) 587 | continue; 588 | channel.MakeLower(); 589 | 590 | CString title; 591 | if(titles.count(channel)) 592 | title = titles[channel]; 593 | else 594 | title = mod->GetNV("tmi_topic_" + channel); 595 | 596 | if(!title.empty() && chan->GetTopic() != title) 597 | { 598 | unsigned long topic_time = (unsigned long)time(nullptr); 599 | chan->SetTopic(title); 600 | chan->SetTopicOwner("jtv"); 601 | chan->SetTopicDate(topic_time); 602 | 603 | std::stringstream ss; 604 | ss << ":jtv TOPIC #" << channel << " :" << title; 605 | 606 | mod->PutUser(ss.str()); 607 | 608 | mod->SetNV("tmi_topic_" + channel, title, true); 609 | mod->SetNV("tmi_topic_time_" + channel, CString(topic_time), true); 610 | } 611 | 612 | auto it = mod->liveChannels.find(channel); 613 | if(it != mod->liveChannels.end()) 614 | { 615 | if(!lives.count(channel) || !lives[channel]) 616 | { 617 | mod->liveChannels.erase(it); 618 | mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went offline! <<<"); 619 | } 620 | } 621 | else 622 | { 623 | if(lives.count(channel) && lives[channel]) 624 | { 625 | mod->liveChannels.insert(channel); 626 | mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went live! <<<"); 627 | } 628 | } 629 | } 630 | } 631 | 632 | 633 | template<> void TModInfo(CModInfo &info) 634 | { 635 | info.SetWikiPage("Twitch"); 636 | info.SetHasArgs(true); 637 | } 638 | 639 | NETWORKMODULEDEFS(TwitchTMI, "Twitch IRC helper module") 640 | -------------------------------------------------------------------------------- /twitchtmi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class TwitchTMIUpdateTimer; 14 | class TwitchTokenRefreshTimer; 15 | 16 | class TwitchTMI : public CModule 17 | { 18 | friend class TwitchTMIUpdateTimer; 19 | friend class TwitchTokenRefreshTimer; 20 | friend class TwitchTMIJob; 21 | 22 | public: 23 | MODCONSTRUCTOR(TwitchTMI),lastFrankerZ(0),noLastPlay(false) {} 24 | virtual ~TwitchTMI(); 25 | 26 | bool OnLoad(const CString &sArgsi, CString &sMessage) override; 27 | bool OnBoot() override; 28 | 29 | CString GetWebMenuTitle() override; 30 | bool OnWebRequest(CWebSock &sock, const CString &pageName, CTemplate &tmpl) override; 31 | 32 | CModule::EModRet OnIRCRegistration(CString &sPass, CString &sNick, CString &sIdent, CString &sRealName) override; 33 | void OnIRCConnected() override; 34 | 35 | CModule::EModRet OnUserRawMessage(CMessage &msg) override; 36 | CModule::EModRet OnRawMessage(CMessage &msg) override; 37 | CModule::EModRet OnUserJoin(CString &sChannel, CString &sKey) override; 38 | CModule::EModRet OnPrivTextMessage(CTextMessage &Message) override; 39 | CModule::EModRet OnChanTextMessage(CTextMessage &Message) override; 40 | CModule::EModRet OnUserTextMessage(CTextMessage &msg) override; 41 | bool OnServerCapAvailable(const CString &sCap) override; 42 | 43 | private: 44 | CString GetTwitchAccessToken(); 45 | void DoTwitchLogin(const CString &code); 46 | void InjectMessageHelper(CChan *chan, const CString &action); 47 | void PutUserChanMessage(CChan *chan, const CString &format, const CString &text); 48 | 49 | private: 50 | TwitchTMIUpdateTimer *updateTimer; 51 | TwitchTokenRefreshTimer *refreshTimer; 52 | std::time_t lastFrankerZ; 53 | std::unordered_map > lastPlay; 54 | bool noLastPlay; 55 | std::unordered_set liveChannels; 56 | std::mutex job_thread_lock; 57 | }; 58 | 59 | class TwitchTMIUpdateTimer : public CTimer 60 | { 61 | friend class TwitchTMI; 62 | 63 | public: 64 | TwitchTMIUpdateTimer(TwitchTMI *mod); 65 | 66 | private: 67 | void RunJob() override; 68 | 69 | private: 70 | TwitchTMI *mod; 71 | }; 72 | 73 | class TwitchTokenRefreshTimer : public CTimer 74 | { 75 | friend class TwitchTMI; 76 | 77 | public: 78 | TwitchTokenRefreshTimer(TwitchTMI *mod); 79 | 80 | private: 81 | void RunJob() override; 82 | 83 | private: 84 | TwitchTMI *mod; 85 | }; 86 | 87 | class TwitchTMIJob : public CModuleJob 88 | { 89 | public: 90 | TwitchTMIJob(TwitchTMI *mod, const std::list &channels) 91 | :CModuleJob(mod, "twitch_updates", "fetches updates from twitch") 92 | ,mod(mod) 93 | ,channels(channels) {} 94 | 95 | void runThread() override; 96 | void runMain() override; 97 | 98 | private: 99 | TwitchTMI *mod; 100 | std::list channels; 101 | std::unordered_map titles; 102 | std::unordered_map lives; 103 | }; 104 | 105 | class GenericJob : public CModuleJob 106 | { 107 | public: 108 | GenericJob(CModule* mod, const CString& name, const CString& desc, std::function threadFunc, std::function mainFunc) 109 | :CModuleJob(mod, name, desc) 110 | ,threadFunc(threadFunc),mainFunc(mainFunc) {} 111 | 112 | void runThread() override { threadFunc(); } 113 | void runMain() override { mainFunc(); } 114 | 115 | private: 116 | std::function threadFunc; 117 | std::function mainFunc; 118 | }; 119 | 120 | class GenericTimer : public CTimer 121 | { 122 | public: 123 | GenericTimer(CModule* mod, unsigned int interval, unsigned int cycles, const CString& label, const CString& desc, std::function runFunc) 124 | :CTimer(mod, interval, cycles, label, desc) 125 | ,runFunc(runFunc) {} 126 | 127 | protected: 128 | void RunJob() override { runFunc(); } 129 | 130 | private: 131 | std::function runFunc; 132 | }; 133 | 134 | --------------------------------------------------------------------------------