├── .clang-format ├── .clang_complete ├── .editorconfig ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── VERSION ├── data └── locale │ ├── en-US.ini │ └── ja-JP.ini ├── json └── json.hpp ├── nico-live-api.cpp ├── nico-live-api.hpp ├── nico-live-timer.cpp ├── nico-live-timer.hpp ├── nico-live-watcher.cpp ├── nico-live-watcher.hpp ├── nico-live.cpp ├── nico-live.hpp ├── nicolive-api.cpp ├── nicolive-api.h ├── nicolive-errno.c ├── nicolive-errno.h ├── nicolive-log.h ├── nicolive-operation.c ├── nicolive-operation.h ├── nicolive.cpp ├── nicolive.h ├── nicookie.cpp ├── nicookie.h ├── nicookie_memo.txt ├── pugixml ├── pugiconfig.hpp ├── pugixml.cpp └── pugixml.hpp ├── rtmp-nicolive.c ├── sqlite ├── shell.c ├── sqlite3.c ├── sqlite3.h └── sqlite3ext.h └── tools ├── cmake └── FindOBS.cmake ├── osx ├── MAC_INSTALL.md ├── convert_readme.rb ├── fix_rpath.rb └── make_package.sh ├── ubuntu └── make_package.sh └── win ├── WIN_INSTALL.md ├── convert_readme.rb └── make_package.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Linux Kernel Style 3 | BasedOnStyle: LLVM 4 | IndentWidth: 8 5 | UseTab: Always 6 | BreakBeforeBraces: Linux 7 | AllowShortIfStatementsOnASingleLine: true 8 | IndentCaseLabels: false 9 | # Others 10 | AlignAfterOpenBracket: DontAlign 11 | AlignEscapedNewlinesLeft: true 12 | AllowShortLoopsOnASingleLine: true 13 | AlwaysBreakBeforeMultilineStrings: true 14 | AlwaysBreakTemplateDeclarations: true 15 | ContinuationIndentWidth: 4 16 | AccessModifierOffset: -8 17 | IncludeCategories: 18 | - Regex: '^ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "curl/curl.h" 10 | #include "pugixml/pugixml.hpp" 11 | #include "nicolive-log.h" 12 | #include "nicolive.h" 13 | 14 | // static 15 | const std::string NicoLiveApi::LOGIN_SITE_URL = 16 | "https://secure.nicovideo.jp/secure/login"; 17 | const std::string NicoLiveApi::LOGIN_API_URL = 18 | "https://account.nicovideo.jp/api/v1/login"; 19 | const std::string NicoLiveApi::PUBSTAT_URL = 20 | "http://live.nicovideo.jp/api/getpublishstatus"; 21 | const std::string NicoLiveApi::USER_AGENT = "NicoLiveApi/1.0"; 22 | 23 | std::string NicoLiveApi::createWwwFormUrlencoded( 24 | const std::unordered_map &formData) 25 | { 26 | std::string encodedData; 27 | for (auto &data : formData) { 28 | if (!encodedData.empty()) { 29 | encodedData += "&"; 30 | } 31 | encodedData += NicoLiveApi::urlEncode(data.first); 32 | encodedData += "="; 33 | encodedData += NicoLiveApi::urlEncode(data.second); 34 | } 35 | return encodedData; 36 | } 37 | 38 | std::string NicoLiveApi::createCookieString( 39 | const std::unordered_map &cookie) 40 | { 41 | std::string cookieStr; 42 | for (auto &data : cookie) { 43 | if (!cookieStr.empty()) { 44 | cookieStr += "; "; 45 | } 46 | cookieStr += data.first; 47 | cookieStr += "="; 48 | cookieStr += data.second; 49 | } 50 | return cookieStr; 51 | } 52 | 53 | bool NicoLiveApi::parseXml(const std::string &xml, 54 | std::unordered_map> *data) 55 | { 56 | pugi::xml_document doc; 57 | pugi::xml_parse_result result = doc.load_string(xml.c_str()); 58 | if (result.status != pugi::status_ok) { 59 | return false; 60 | } 61 | if (data == nullptr) { 62 | // do nothing, but ok 63 | return true; 64 | } 65 | for (auto &entry : *data) { 66 | pugi::xpath_node_set nodes = 67 | doc.select_nodes(entry.first.c_str()); 68 | for (auto &node : nodes) { 69 | if (node.node() != nullptr) { 70 | entry.second.push_back( 71 | std::string(node.node().text().get())); 72 | } else { 73 | entry.second.push_back( 74 | std::string(node.attribute().value())); 75 | } 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | std::string NicoLiveApi::urlEncode(const std::string &str) 82 | { 83 | std::stringstream stream; 84 | for (const char &ch : str) { 85 | if (0x20 <= ch && ch <= 0x7F) { 86 | switch (ch) { 87 | case '&': 88 | case '=': 89 | case '+': 90 | case '%': 91 | stream << '%'; 92 | stream << std::setfill('0') << std::setw(2) 93 | << std::hex << std::uppercase 94 | << static_cast(ch); 95 | break; 96 | case ' ': 97 | stream << '+'; 98 | break; 99 | default: 100 | stream << ch; 101 | } 102 | } else { 103 | stream << '%'; 104 | stream << std::setfill('0') << std::setw(2) << std::hex 105 | << std::uppercase << static_cast(ch); 106 | break; 107 | } 108 | } 109 | return stream.str(); 110 | } 111 | 112 | size_t NicoLiveApi::writeString( 113 | char *ptr, size_t size, size_t nmemb, void *userdata) 114 | { 115 | size_t length = size * nmemb; 116 | std::string *str = static_cast(userdata); 117 | str->append(ptr, length); 118 | return length; 119 | }; 120 | 121 | // instance 122 | NicoLiveApi::NicoLiveApi() {} 123 | 124 | NicoLiveApi::~NicoLiveApi() {} 125 | 126 | void NicoLiveApi::setCookie(const std::string &name, const std::string &value) 127 | { 128 | this->cookie[name] = value; 129 | } 130 | 131 | void NicoLiveApi::deleteCookie(const std::string &name) 132 | { 133 | this->cookie.erase(name); 134 | } 135 | 136 | void NicoLiveApi::clearCookie() { this->cookie.clear(); } 137 | 138 | const std::string NicoLiveApi::getCookie(const std::string &name) const 139 | { 140 | if (this->cookie.count(name) > 0) { 141 | return this->cookie.at(name); 142 | } else { 143 | return ""; 144 | } 145 | } 146 | 147 | bool NicoLiveApi::accessWeb(const std::string &url, 148 | const NicoLiveApi::Method &method, 149 | const std::unordered_map &formData, int *code, 150 | std::string *response) 151 | { 152 | *code = 0; 153 | bool useSsl = false; 154 | if (url.find("https://") == 0) { 155 | useSsl = true; 156 | } else if (url.find("http://") == 0) { 157 | ; 158 | } else { 159 | nicolive_log_error("unknown scheam url: %s", url.c_str()); 160 | *code = -1; 161 | *response = "unknown schema"; 162 | return false; 163 | } 164 | 165 | bool hasPost = false; 166 | switch (method) { 167 | case NicoLiveApi::Method::GET: 168 | break; 169 | case NicoLiveApi::Method::POST: 170 | hasPost = true; 171 | break; 172 | default: 173 | nicolive_log_error("unknown method type: %d", method); 174 | *code = -2; 175 | *response = "unknown method"; 176 | return false; 177 | } 178 | 179 | std::string headerData; 180 | std::string bodyData; 181 | 182 | std::string postData; 183 | if (hasPost) { 184 | postData = NicoLiveApi::createWwwFormUrlencoded(formData); 185 | } 186 | 187 | CURL *curl; 188 | CURLcode res; 189 | 190 | curl_global_init(CURL_GLOBAL_DEFAULT); 191 | 192 | curl = curl_easy_init(); 193 | 194 | if (curl == nullptr) { 195 | nicolive_log_error("curl init error"); 196 | *code = -3; 197 | *response = "curl init error"; 198 | return false; 199 | } 200 | 201 | // URL 202 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); 203 | // User Agent 204 | curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT.c_str()); 205 | // header and body data 206 | curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headerData); 207 | curl_easy_setopt( 208 | curl, CURLOPT_HEADERFUNCTION, NicoLiveApi::writeString); 209 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &bodyData); 210 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NicoLiveApi::writeString); 211 | 212 | if (useSsl) { 213 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); 214 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); 215 | } 216 | 217 | // Cookie 218 | nicolive_log_debug("create cookie: %40s", 219 | NicoLiveApi::createCookieString(this->cookie).c_str()); 220 | if (!this->cookie.empty()) { 221 | curl_easy_setopt(curl, CURLOPT_COOKIE, 222 | NicoLiveApi::createCookieString(this->cookie).c_str()); 223 | } 224 | 225 | // POST data 226 | if (hasPost) { 227 | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 228 | static_cast(postData.size())); 229 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str()); 230 | } 231 | 232 | res = curl_easy_perform(curl); 233 | 234 | curl_easy_cleanup(curl); 235 | curl_global_cleanup(); 236 | 237 | if (res != CURLE_OK) { 238 | nicolive_log_error( 239 | "curl failed: %s\n", curl_easy_strerror(res)); 240 | *code = -4; 241 | *response = "curl init error"; 242 | return false; 243 | } 244 | 245 | // Get code and set cookie 246 | std::istringstream isHeader(headerData); 247 | std::regex httpRe( 248 | "HTTP/\\d+\\.\\d+\\s+(\\d+)\\s.*\\r?", std::regex_constants::icase); 249 | std::regex setCookieRe("Set-Cookie:\\s+([^=]+)=([^;]+);.*\\r?", 250 | std::regex_constants::icase); 251 | std::smatch results; 252 | for (std::string line; std::getline(isHeader, line);) { 253 | nicolive_log_debug("header: %s", line.c_str()); 254 | if (std::regex_match(line, results, httpRe)) { 255 | nicolive_log_debug( 256 | "header httpRe: %s", results.str(1).c_str()); 257 | *code = std::stoi(results.str(1)); 258 | } else if (std::regex_match(line, results, setCookieRe)) { 259 | nicolive_log_debug("header setCookieRe: %s, %s", 260 | results.str(1).c_str(), results.str(2).c_str()); 261 | this->cookie[results.str(1)] = results.str(2); 262 | } 263 | } 264 | nicolive_log_debug("body: %s", bodyData.c_str()); 265 | 266 | *response = bodyData; 267 | 268 | return true; 269 | } 270 | bool NicoLiveApi::getWeb( 271 | const std::string &url, int *code, std::string *response) 272 | { 273 | std::unordered_map formData; 274 | return this->accessWeb( 275 | url, NicoLiveApi::Method::GET, formData, code, response); 276 | } 277 | 278 | bool NicoLiveApi::postWeb(const std::string &url, 279 | const std::unordered_map &formData, int *code, 280 | std::string *response) 281 | { 282 | return this->accessWeb( 283 | url, NicoLiveApi::Method::POST, formData, code, response); 284 | } 285 | 286 | bool NicoLiveApi::loginSite(const std::string &site, const std::string &mail, 287 | const std::string &password) 288 | { 289 | std::string url; 290 | url += NicoLiveApi::LOGIN_SITE_URL; 291 | url += "?site="; 292 | url += NicoLiveApi::urlEncode(site); 293 | std::unordered_map formData; 294 | // FIXME: mail_tel? 295 | formData["mail"] = mail; 296 | formData["password"] = password; 297 | 298 | int code = 0; 299 | std::string response; 300 | 301 | this->clearCookie(); 302 | 303 | nicolive_log_info("login site: %s", site.c_str()); 304 | bool result = this->postWeb(url, formData, &code, &response); 305 | if (result) { 306 | if (code == 302) { 307 | // TODO: check redirect location? 308 | if (this->getCookie("user_session") 309 | .find("user_" 310 | "session_") == 0) { 311 | nicolive_log_info("login success"); 312 | return true; 313 | } else { 314 | nicolive_log_info("login fail"); 315 | return false; 316 | } 317 | } else { 318 | nicolive_log_error( 319 | "login invalid return code: %d", code); 320 | return false; 321 | } 322 | } else { 323 | nicolive_log_error("access login errror"); 324 | return false; 325 | } 326 | } 327 | 328 | // std::string NicoLiveApi::loginSiteTicket( 329 | // const std::string &site, 330 | // const std::string &mail, 331 | // const std::string &password) 332 | // {} 333 | 334 | bool NicoLiveApi::loginSiteNicolive( 335 | const std::string &mail, const std::string &password) 336 | { 337 | return this->loginSite("nicolive", mail, password); 338 | } 339 | 340 | std::string NicoLiveApi::loginApiTicket(const std::string &site, 341 | const std::string &mail, const std::string &password) 342 | { 343 | std::stringstream unixTimeStream; 344 | unixTimeStream << std::time(nullptr); 345 | std::unordered_map formData; 346 | formData["site"] = site; 347 | formData["time"] = unixTimeStream.str(); 348 | formData["mail"] = mail; 349 | formData["password"] = password; 350 | 351 | int code = 0; 352 | std::string response; 353 | 354 | this->clearCookie(); 355 | 356 | nicolive_log_info("login api site: %s", site.c_str()); 357 | bool result = this->postWeb( 358 | NicoLiveApi::LOGIN_API_URL, formData, &code, &response); 359 | 360 | if (!result) { 361 | nicolive_log_error("access login api errror"); 362 | return std::string(); 363 | } 364 | 365 | if (code != 200) { 366 | nicolive_log_error("login api invalid return code: %d", code); 367 | return std::string(); 368 | } 369 | 370 | std::unordered_map> data; 371 | data["/nicovideo_user_response/@status"] = std::vector(); 372 | data["/nicovideo_user_response/ticket/text()"] = 373 | std::vector(); 374 | 375 | if (!NicoLiveApi::parseXml(response, &data)) { 376 | nicolive_log_info("login api fail parse xml"); 377 | return std::string(); 378 | } 379 | 380 | if (data["/nicovideo_user_response/@status"].empty()) { 381 | nicolive_log_info("login api unknown status"); 382 | return std::string(); 383 | } 384 | 385 | if (data["/nicovideo_user_response/@status"].at(0) != "ok") { 386 | nicolive_log_info("login api fail status: %s", 387 | data["/nicovideo_user_response/@status"].at(0).c_str()); 388 | return std::string(); 389 | } 390 | 391 | if (data["/nicovideo_user_response/ticket/text()"].empty()) { 392 | nicolive_log_info("login api no ticket"); 393 | return std::string(); 394 | } 395 | 396 | return data["/nicovideo_user_response/ticket/text()"].at(0); 397 | } 398 | 399 | std::string NicoLiveApi::loginNicoliveEncoder( 400 | const std::string &mail, const std::string &password) 401 | { 402 | return this->loginApiTicket("nicolive_encoder", mail, password); 403 | } 404 | 405 | bool NicoLiveApi::getPublishStatus( 406 | std::unordered_map> *data) 407 | { 408 | int code; 409 | std::string response; 410 | if (!this->getWeb(NicoLiveApi::PUBSTAT_URL, &code, &response)) { 411 | nicolive_log_error("failed to get publish status"); 412 | return false; 413 | } 414 | 415 | if (code != 200) { 416 | nicolive_log_error( 417 | "failed to get publish status, return code = " 418 | "%d", 419 | code); 420 | return false; 421 | } 422 | 423 | return NicoLiveApi::parseXml(response, data); 424 | } 425 | 426 | bool NicoLiveApi::getPublishStatusTicket(const std::string &ticket, 427 | std::unordered_map> *data) 428 | { 429 | std::unordered_map formData; 430 | formData["ticket"] = ticket; 431 | formData["accept-multi"] = "0"; 432 | 433 | int code; 434 | std::string response; 435 | if (!this->postWeb( 436 | NicoLiveApi::PUBSTAT_URL, formData, &code, &response)) { 437 | nicolive_log_error("failed to get publish status ticket"); 438 | return false; 439 | } 440 | 441 | if (code != 200) { 442 | nicolive_log_error( 443 | "failed to get publish ticketstatus, return " 444 | "code = %d", 445 | code); 446 | return false; 447 | } 448 | 449 | return NicoLiveApi::parseXml(response, data); 450 | } 451 | -------------------------------------------------------------------------------- /nico-live-api.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class NicoLiveApi 8 | { 9 | enum class Method { 10 | GET, 11 | POST, 12 | }; 13 | 14 | public: 15 | static const std::string LOGIN_SITE_URL; 16 | static const std::string LOGIN_API_URL; 17 | static const std::string PUBSTAT_URL; 18 | static const std::string USER_AGENT; 19 | static std::string createWwwFormUrlencoded( 20 | const std::unordered_map &formData); 21 | static std::string createCookieString( 22 | const std::unordered_map &cookie); 23 | static bool parseXml(const std::string &xml, 24 | std::unordered_map> *data); 25 | static std::string urlEncode(const std::string &str); 26 | static size_t writeString( 27 | char *ptr, size_t size, size_t nmemb, void *userdata); 28 | 29 | private: 30 | std::unordered_map cookie; 31 | 32 | public: 33 | NicoLiveApi(); 34 | ~NicoLiveApi(); 35 | 36 | // Cookie 37 | void setCookie(const std::string &name, const std::string &value); 38 | void deleteCookie(const std::string &name); 39 | void clearCookie(); 40 | const std::string getCookie(const std::string &name) const; 41 | 42 | // Generic 43 | bool accessWeb(const std::string &url, const Method &method, 44 | const std::unordered_map &formData, 45 | int *code, std::string *response); 46 | bool getWeb(const std::string &url, int *code, std::string *response); 47 | bool postWeb(const std::string &url, 48 | const std::unordered_map &formData, 49 | int *code, std::string *response); 50 | 51 | // Nicovideo 52 | bool loginSite(const std::string &site, const std::string &mail, 53 | const std::string &password); 54 | // std::string loginSiteTicket( 55 | // const std::string &site, 56 | // const std::string &mail, 57 | // const std::string &password); 58 | bool loginSiteNicolive( 59 | const std::string &mail, const std::string &password); 60 | std::string loginApiTicket(const std::string &site, 61 | const std::string &mail, const std::string &password); 62 | std::string loginNicoliveEncoder( 63 | const std::string &mail, const std::string &password); 64 | bool getPublishStatus( 65 | std::unordered_map> *data = 66 | nullptr); 67 | bool getPublishStatusTicket(const std::string &ticket, 68 | std::unordered_map> *data = 69 | nullptr); 70 | }; 71 | -------------------------------------------------------------------------------- /nico-live-timer.cpp: -------------------------------------------------------------------------------- 1 | #include "nico-live-timer.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | NicoLiveTimer::NicoLiveTimer( 11 | std::function callable, 12 | std::chrono::system_clock::duration minInterval) 13 | : callable(callable), minInterval(minInterval), active(false), loopId(0), 14 | loopMutex(std::make_shared()), 15 | alive(std::make_shared(true)) 16 | { 17 | } 18 | 19 | NicoLiveTimer::~NicoLiveTimer() 20 | { 21 | Stop(); 22 | *this->alive = false; 23 | } 24 | void NicoLiveTimer::Start() 25 | { 26 | std::lock_guard lock(*this->loopMutex); 27 | if (IsActive()) return; 28 | this->loopId++; 29 | int currentLoopId = this->loopId; 30 | this->active = true; 31 | std::thread th(Loop, currentLoopId, this); 32 | th.detach(); 33 | } 34 | void NicoLiveTimer::Stop() 35 | { 36 | std::lock_guard lock(*this->loopMutex); 37 | this->active.store(false); 38 | } 39 | bool NicoLiveTimer::IsActive() const { return this->active; } 40 | 41 | void NicoLiveTimer::Loop(int id, NicoLiveTimer *timer) 42 | { 43 | auto timerAlive = timer->alive; 44 | auto timerLoopMutex = timer->loopMutex; 45 | auto intervalTime = timer->minInterval; 46 | while (true) { 47 | { 48 | std::lock_guard lock(*timerLoopMutex); 49 | if (!*timerAlive) break; 50 | if (!timer->active || id != timer->loopId) break; 51 | intervalTime = timer->callable(); 52 | } 53 | std::this_thread::sleep_for( 54 | std::max(timer->minInterval, intervalTime)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /nico-live-timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class NicoLiveTimer 11 | { 12 | const std::function callable; 13 | std::chrono::system_clock::duration minInterval; 14 | 15 | std::atomic_bool active; 16 | std::atomic_int loopId; 17 | std::shared_ptr loopMutex; 18 | std::shared_ptr alive; 19 | 20 | public: 21 | NicoLiveTimer() = delete; 22 | NicoLiveTimer( 23 | std::function callable, 24 | std::chrono::system_clock::duration minInterval = 25 | std::chrono::system_clock::duration(std::chrono::seconds(1))); 26 | ~NicoLiveTimer(); 27 | void Start(); 28 | void Stop(); 29 | bool IsActive() const; 30 | 31 | private: 32 | static void Loop(int id, NicoLiveTimer *timer); 33 | }; 34 | -------------------------------------------------------------------------------- /nico-live-watcher.cpp: -------------------------------------------------------------------------------- 1 | #include "nico-live-watcher.hpp" 2 | #include 3 | #include 4 | #include 5 | // #include 6 | #include "nico-live-timer.hpp" 7 | #include "nico-live.hpp" 8 | #include "nicolive-log.h" 9 | #include "nicolive-operation.h" 10 | #include "nicolive.h" 11 | 12 | // static consntexpr 13 | constexpr std::chrono::system_clock::duration NicoLiveWatcher::MARGIN_TIME; 14 | constexpr std::chrono::system_clock::duration 15 | NicoLiveWatcher::ON_AIR_INTERVAL_TIME; 16 | constexpr std::chrono::system_clock::duration 17 | NicoLiveWatcher::OFF_AIR_INTERVAL_TIME; 18 | constexpr std::chrono::system_clock::duration 19 | NicoLiveWatcher::BOOST_INTERVAL_TIME; 20 | 21 | NicoLiveWatcher::NicoLiveWatcher(NicoLive *nicolive) : nicolive(nicolive) 22 | { 23 | this->timer = std::unique_ptr( 24 | new NicoLiveTimer([this]() { return this->watch(); })); 25 | } 26 | 27 | NicoLiveWatcher::~NicoLiveWatcher() 28 | { 29 | if (isActive()) stop(); 30 | } 31 | 32 | void NicoLiveWatcher::start() 33 | { 34 | nicolive_log_debug("start watch"); 35 | this->boostCount = 0; 36 | this->timer->Start(); 37 | } 38 | 39 | void NicoLiveWatcher::stop() 40 | { 41 | nicolive_log_debug("stop watch "); 42 | this->timer->Stop(); 43 | } 44 | 45 | bool NicoLiveWatcher::isActive() { return this->timer->IsActive(); } 46 | 47 | std::chrono::system_clock::duration NicoLiveWatcher::watch() 48 | { 49 | nicolive_log_debug("watching! since epoch (s) %d", 50 | static_cast(std::chrono::duration_cast( 51 | std::chrono::system_clock::now().time_since_epoch()) 52 | .count())); 53 | if (this->boostCount > 0) { 54 | this->boostCount--; 55 | } 56 | 57 | this->nicolive->sitePubStat(); 58 | 59 | if (this->nicolive->isOnair()) { 60 | return this->watchOnAir(); 61 | } else { 62 | return this->watchOffAir(); 63 | } 64 | } 65 | 66 | std::chrono::system_clock::duration NicoLiveWatcher::watchOnAir() 67 | { 68 | if (!this->nicolive->enabledLive() || 69 | this->nicolive->getLiveId() != this->nicolive->getOnairLiveId()) { 70 | nicolive_streaming_stop(); 71 | this->boostCount = NicoLiveWatcher::BOOST_NUMBER_AFTER_STOP; 72 | return NicoLiveWatcher::MARGIN_TIME; 73 | } 74 | 75 | auto remainingEndTime = this->nicolive->getRemainingEndTime(); 76 | 77 | if (this->nicolive->enabledStopBeforeEndTime()) { 78 | if (remainingEndTime <= NicoLiveWatcher::MARGIN_TIME) { 79 | nicolive_streaming_stop(); 80 | this->boostCount = 81 | NicoLiveWatcher::BOOST_NUMBER_AFTER_STOP; 82 | return NicoLiveWatcher::MARGIN_TIME; 83 | } else { 84 | return std::min(NicoLiveWatcher::ON_AIR_INTERVAL_TIME, 85 | remainingEndTime - NicoLiveWatcher::MARGIN_TIME); 86 | } 87 | } 88 | 89 | return std::min(NicoLiveWatcher::ON_AIR_INTERVAL_TIME, 90 | remainingEndTime + NicoLiveWatcher::MARGIN_TIME); 91 | } 92 | 93 | std::chrono::system_clock::duration NicoLiveWatcher::watchOffAir() 94 | { 95 | if (!this->nicolive->enabledLive()) { 96 | if (this->boostCount > 0) { 97 | return NicoLiveWatcher::BOOST_INTERVAL_TIME; 98 | } else { 99 | return NicoLiveWatcher::OFF_AIR_INTERVAL_TIME; 100 | } 101 | } 102 | 103 | if (!this->nicolive->enabledStartBeforeStartTime()) { 104 | auto remainingStartTime = 105 | this->nicolive->getRemainingStartTime(); 106 | if (remainingStartTime < 107 | std::chrono::system_clock::duration::zero()) { 108 | return std::min(NicoLiveWatcher::OFF_AIR_INTERVAL_TIME, 109 | remainingStartTime); 110 | } 111 | } 112 | nicolive_streaming_start(); 113 | 114 | return NicoLiveWatcher::OFF_AIR_INTERVAL_TIME; 115 | } 116 | -------------------------------------------------------------------------------- /nico-live-watcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class NicoLive; 7 | class NicoLiveTimer; 8 | 9 | class NicoLiveWatcher 10 | { 11 | public: 12 | // stopping and starting margin time 2s 13 | static constexpr auto MARGIN_TIME = 14 | std::chrono::system_clock::duration(std::chrono::seconds(2)); 15 | // interval time on air 60s 16 | static constexpr auto ON_AIR_INTERVAL_TIME = 17 | std::chrono::system_clock::duration(std::chrono::seconds(60)); 18 | // interval time off air 10s 19 | static constexpr auto OFF_AIR_INTERVAL_TIME = 20 | std::chrono::system_clock::duration(std::chrono::seconds(10)); 21 | // interval time in boost 2s 22 | static constexpr auto BOOST_INTERVAL_TIME = 23 | std::chrono::system_clock::duration(std::chrono::seconds(2)); 24 | // boost the number of times after stopping 25 | static const int BOOST_NUMBER_AFTER_STOP = 5; 26 | 27 | private: 28 | NicoLive *nicolive; 29 | int boostCount = 0; 30 | std::unique_ptr timer; 31 | 32 | public: 33 | NicoLiveWatcher(NicoLive *nicolive); 34 | ~NicoLiveWatcher(); 35 | void start(); 36 | void stop(); 37 | bool isActive(); 38 | // int remainingTime(); 39 | 40 | private: 41 | std::chrono::system_clock::duration watch(); 42 | std::chrono::system_clock::duration watchOnAir(); 43 | std::chrono::system_clock::duration watchOffAir(); 44 | }; 45 | -------------------------------------------------------------------------------- /nico-live.cpp: -------------------------------------------------------------------------------- 1 | #include "nico-live.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "nicookie.h" 10 | #include "nico-live-api.hpp" 11 | #include "nico-live-watcher.hpp" 12 | #include "nicolive-log.h" 13 | #include "nicolive.h" 14 | 15 | namespace 16 | { 17 | // umsvs std::unordered_map 18 | // umss std::unordered_map 19 | inline const std::string &get_umsvs_str( 20 | const std::unordered_map> &data, 21 | const std::unordered_map &map, 22 | const std::string &name, int pos = 0) 23 | { 24 | return data.at(map.at(name)).at(pos); 25 | } 26 | inline int get_umsvs_int( 27 | const std::unordered_map> &data, 28 | const std::unordered_map &map, 29 | const std::string &name, int pos = 0) 30 | { 31 | return std::stoi(get_umsvs_str(data, map, name, pos)); 32 | } 33 | inline bool get_umsvs_bool( 34 | const std::unordered_map> &data, 35 | const std::unordered_map &map, 36 | const std::string &name, int pos = 0) 37 | { 38 | return !!get_umsvs_int(data, map, name, pos); 39 | } 40 | inline std::chrono::system_clock::time_point get_umsvs_time( 41 | const std::unordered_map> &data, 42 | const std::unordered_map &map, 43 | const std::string &name, int pos = 0) 44 | { 45 | return std::chrono::system_clock::from_time_t( 46 | get_umsvs_int(data, map, name, pos)); 47 | } 48 | /* const char *name */ 49 | inline const std::string &get_umsvs_str( 50 | const std::unordered_map> &data, 51 | const std::unordered_map &map, const char *name, 52 | int pos = 0) 53 | { 54 | return get_umsvs_str(data, map, std::string(name), pos); 55 | } 56 | inline int get_umsvs_int( 57 | const std::unordered_map> &data, 58 | const std::unordered_map &map, const char *name, 59 | int pos = 0) 60 | { 61 | return get_umsvs_int(data, map, std::string(name), pos); 62 | } 63 | inline bool get_umsvs_bool( 64 | const std::unordered_map> &data, 65 | const std::unordered_map &map, const char *name, 66 | int pos = 0) 67 | { 68 | return get_umsvs_bool(data, map, std::string(name), pos); 69 | } 70 | inline std::chrono::system_clock::time_point get_umsvs_time( 71 | const std::unordered_map> &data, 72 | const std::unordered_map &map, const char *name, 73 | int pos = 0) 74 | { 75 | return get_umsvs_time(data, map, std::string(name), pos); 76 | } 77 | } 78 | 79 | NicoLive::NicoLive() 80 | { 81 | watcher = std::unique_ptr(new NicoLiveWatcher(this)); 82 | webApi = std::unique_ptr(new NicoLiveApi()); 83 | } 84 | 85 | NicoLive::~NicoLive() {} 86 | 87 | void NicoLive::setSession(const std::string &session) 88 | { 89 | this->session = session; 90 | this->flags.session_valid = false; 91 | this->flags.load_viqo = false; 92 | this->webApi->setCookie("user_session", this->session); 93 | } 94 | 95 | void NicoLive::setSession(const char *session) 96 | { 97 | this->setSession(std::string(session)); 98 | } 99 | 100 | void NicoLive::setAccount(const std::string &mail, const std::string &password) 101 | { 102 | this->mail = mail; 103 | this->password = password; 104 | this->flags.session_valid = false; 105 | this->flags.load_viqo = false; 106 | } 107 | 108 | void NicoLive::setAccount(const char *mail, const char *password) 109 | { 110 | this->setAccount(std::string(mail), std::string(password)); 111 | } 112 | 113 | void NicoLive::setEnabledAdjustBitrate(bool enabled) 114 | { 115 | this->flags.adjust_bitrate = enabled; 116 | } 117 | 118 | const std::string &NicoLive::getMail() const { return this->mail; } 119 | 120 | const std::string &NicoLive::getPassword() const { return this->password; } 121 | 122 | const std::string &NicoLive::getSession() const { return this->session; } 123 | 124 | const std::string &NicoLive::getLiveId() const { return this->live_info.id; } 125 | 126 | const std::string &NicoLive::getLiveUrl() const { return this->live_url; } 127 | 128 | const std::string &NicoLive::getLiveKey() const { return this->live_key; } 129 | 130 | long long NicoLive::getLiveBitrate() const { return this->live_info.bitrate; } 131 | 132 | const std::string &NicoLive::getOnairLiveId() const 133 | { 134 | return this->onair_live_id; 135 | } 136 | 137 | bool NicoLive::enabledLive() const 138 | { 139 | auto now = std::chrono::system_clock::now(); 140 | return this->live_info.start_time <= now && 141 | this->live_info.end_time >= now; 142 | } 143 | 144 | NicoLive::LiveState NicoLive::getLiveState() const 145 | { 146 | auto now = std::chrono::system_clock::now(); 147 | if (now < getLiveStartTime()) { 148 | return NicoLive::LiveState::BEFORE_START; 149 | } else if (now <= getLiveEndTime()) { 150 | return NicoLive::LiveState::ENABLE_LIVE; 151 | } else { 152 | return NicoLive::LiveState::AFTER_END; 153 | } 154 | } 155 | 156 | std::chrono::system_clock::time_point NicoLive::getLiveStartTime() const 157 | { 158 | return this->live_info.start_time; 159 | } 160 | 161 | std::chrono::system_clock::time_point NicoLive::getLiveEndTime() const 162 | { 163 | return this->live_info.end_time; 164 | } 165 | 166 | std::chrono::system_clock::duration NicoLive::getRemainingStartTime() const 167 | { 168 | return this->live_info.start_time - this->live_info.server_time; 169 | } 170 | 171 | std::chrono::system_clock::duration NicoLive::getRemainingEndTime() const 172 | { 173 | return this->live_info.end_time - this->live_info.server_time; 174 | } 175 | 176 | std::chrono::system_clock::duration NicoLive::getRemainingLive() const 177 | { 178 | if (isOnair()) 179 | return this->live_info.end_time - 180 | std::chrono::system_clock::now(); 181 | else 182 | return std::chrono::milliseconds(0); 183 | } 184 | 185 | bool NicoLive::enabledStopBeforeEndTime() const 186 | { 187 | // TODO: Implement! 188 | return true; 189 | } 190 | 191 | bool NicoLive::enabledStartBeforeStartTime() const 192 | { 193 | // TODO: Implement! 194 | return true; 195 | } 196 | 197 | bool NicoLive::enabledAdjustBitrate() const 198 | { 199 | return this->flags.adjust_bitrate; 200 | } 201 | 202 | bool NicoLive::enabledSession() const { return this->flags.session_valid; } 203 | 204 | bool NicoLive::isOnair() const { return this->flags.onair; } 205 | 206 | void NicoLive::startStreaming() 207 | { 208 | this->onair_live_id = getLiveId(); 209 | this->flags.onair = true; 210 | } 211 | 212 | void NicoLive::stopStreaming() 213 | { 214 | this->onair_live_id = std::string(); 215 | this->flags.onair = false; 216 | } 217 | 218 | void NicoLive::startWatching() { this->watcher->start(); } 219 | 220 | void NicoLive::stopWatching() { this->watcher->stop(); } 221 | 222 | void NicoLive::nextSilentOnce() { this->flags.silent_once = true; } 223 | bool NicoLive::silentOnce() 224 | { 225 | bool once = this->flags.silent_once; 226 | this->flags.silent_once = false; 227 | return once; 228 | } 229 | 230 | bool NicoLive::checkSession() 231 | { 232 | return (sitePubStat() || (siteLoginNLE() && sitePubStat())); 233 | } 234 | 235 | bool NicoLive::checkLive() { return siteLiveProf(); } 236 | 237 | bool NicoLive::siteLogin() 238 | { 239 | if (this->mail.empty() || this->password.empty()) { 240 | nicolive_log_warn("no mail or password"); 241 | return false; 242 | } 243 | 244 | bool result = 245 | this->webApi->loginSiteNicolive(this->mail, this->password); 246 | if (result) { 247 | this->session = this->webApi->getCookie("user_session").c_str(); 248 | } 249 | return result; 250 | } 251 | 252 | bool NicoLive::siteLoginNLE() 253 | { 254 | if (this->mail.empty() || this->password.empty()) { 255 | nicolive_log_warn("no mail or password"); 256 | return false; 257 | } 258 | 259 | std::string ticket = 260 | this->webApi->loginNicoliveEncoder(this->mail, this->password); 261 | nicolive_log_debug("ticket: %20s", ticket.c_str()); 262 | if (!ticket.empty()) { 263 | this->ticket = ticket; 264 | return true; 265 | } else { 266 | return false; 267 | } 268 | } 269 | 270 | bool NicoLive::sitePubStat() 271 | { 272 | nicolive_log_debug("session: %20s", this->session.c_str()); 273 | nicolive_log_debug("ticket: %20s", this->ticket.c_str()); 274 | 275 | bool useTicket = false; 276 | if (this->session.empty()) { 277 | if (this->siteLoginNLE()) { 278 | useTicket = true; 279 | } else { 280 | nicolive_log_debug( 281 | "this->session and this->ticket" 282 | " are both empty."); 283 | this->flags.onair = false; 284 | clearLiveInfo(); 285 | return false; 286 | } 287 | } 288 | 289 | const std::string statusXpath = "/getpublishstatus/@status"; 290 | const std::string errorCodeXpath = 291 | "/getpublishstatus/error/code/text()"; 292 | const std::unordered_map xpathMap = { 293 | {"server_time", "/getpublishstatus/@time"}, 294 | {"id", "/getpublishstatus//stream/id/text()"}, 295 | {"exclude", "/getpublishstatus//stream/exclude/text()"}, 296 | {"base_time", "/getpublishstatus//stream/base_time/text()"}, 297 | {"open_time", "/getpublishstatus//stream/open_time/text()"}, 298 | {"start_time", "/getpublishstatus//stream/start_time/text()"}, 299 | {"end_time", "/getpublishstatus//stream/end_time/text()"}, 300 | {"url", "/getpublishstatus//rtmp/url/text()"}, 301 | {"stream", "/getpublishstatus//rtmp/stream/text()"}, 302 | {"ticket", "/getpublishstatus//rtmp/ticket/text()"}, 303 | {"bitrate", "/getpublishstatus//rtmp/bitrate/text()"}, 304 | }; 305 | 306 | std::unordered_map> data; 307 | data[statusXpath] = std::vector(); 308 | data[errorCodeXpath] = std::vector(); 309 | for (auto &xpathPair : xpathMap) { 310 | data[xpathPair.second] = std::vector(); 311 | } 312 | 313 | bool result = false; 314 | if (useTicket) { 315 | result = 316 | this->webApi->getPublishStatusTicket(this->ticket, &data); 317 | } else { 318 | result = this->webApi->getPublishStatus(&data); 319 | } 320 | 321 | if (!result) { 322 | nicolive_log_error("failed get publish status web page"); 323 | return false; 324 | } 325 | 326 | if (data[statusXpath].empty()) { 327 | nicolive_log_error("faield get publish status"); 328 | return false; 329 | } 330 | 331 | bool success = false; 332 | std::string status = data[statusXpath][0]; 333 | 334 | if (status == "ok") { 335 | try { 336 | this->live_info.server_time = 337 | get_umsvs_time(data, xpathMap, "server_time"); 338 | this->live_info.id = 339 | get_umsvs_str(data, xpathMap, "id"); 340 | this->live_info.exclude = 341 | get_umsvs_bool(data, xpathMap, "exclude"); 342 | this->live_info.base_time = 343 | get_umsvs_time(data, xpathMap, "base_time"); 344 | this->live_info.open_time = 345 | get_umsvs_time(data, xpathMap, "open_time"); 346 | this->live_info.start_time = 347 | get_umsvs_time(data, xpathMap, "start_time"); 348 | this->live_info.end_time = 349 | get_umsvs_time(data, xpathMap, "end_time"); 350 | this->live_info.url = 351 | get_umsvs_str(data, xpathMap, "url"); 352 | this->live_info.stream = 353 | get_umsvs_str(data, xpathMap, "stream"); 354 | if (data.find(xpathMap.at("ticket")) != data.end() && 355 | data[xpathMap.at("ticket")].size() != 0) { 356 | this->live_info.ticket = 357 | data[xpathMap.at("ticket")].at(0); 358 | } else { 359 | this->live_info.ticket = std::string(); 360 | } 361 | this->live_info.bitrate = 362 | get_umsvs_int(data, xpathMap, "bitrate"); 363 | nicolive_log_info( 364 | "live waku: %s", this->live_info.id.c_str()); 365 | success = true; 366 | } catch (const std::out_of_range &err) { 367 | nicolive_log_error("parse faild: %s", err.what()); 368 | clearLiveInfo(); 369 | success = false; 370 | } catch (const std::invalid_argument &err) { 371 | nicolive_log_error("parse faild: %s", err.what()); 372 | clearLiveInfo(); 373 | success = false; 374 | } 375 | } else if (status == "fail") { 376 | clearLiveInfo(); 377 | std::string errorCode = "null"; 378 | if (!data[errorCodeXpath].empty()) { 379 | errorCode = data[errorCodeXpath][0]; 380 | } 381 | if (errorCode == "notfound") { 382 | nicolive_log_info("no live waku"); 383 | success = true; 384 | } else if (errorCode == "unknown") { 385 | nicolive_log_warn("login session failed"); 386 | } else { 387 | nicolive_log_error( 388 | "unknow error code: %s", errorCode.c_str()); 389 | } 390 | } else { 391 | clearLiveInfo(); 392 | nicolive_log_error("unknow status: %s", status.c_str()); 393 | } 394 | 395 | if (success) { 396 | this->flags.session_valid = true; 397 | } else { 398 | this->flags.session_valid = false; 399 | } 400 | 401 | return success; 402 | } 403 | 404 | bool NicoLive::siteLiveProf() 405 | { 406 | 407 | if (this->live_info.id.empty()) { 408 | nicolive_log_debug("this->live_info.id is empty."); 409 | this->live_url = std::string(); 410 | this->live_key = std::string(); 411 | return false; 412 | } else if (this->live_info.ticket.empty()) { 413 | nicolive_log_info("found live url and key without ticket"); 414 | this->live_url = std::string(); 415 | this->live_url += this->live_info.url; 416 | this->live_key = this->live_info.stream; 417 | return true; 418 | } else { 419 | nicolive_log_info("found live url and key with ticket"); 420 | this->live_url = std::string(); 421 | this->live_url += this->live_info.url; 422 | this->live_url += "?"; 423 | this->live_url += this->live_info.ticket; 424 | this->live_key = this->live_info.stream; 425 | return true; 426 | } 427 | } 428 | 429 | bool NicoLive::loadViqoSettings() 430 | { 431 | const char *session = nicookie_get_session(NICOOKIE_APP_VIQO); 432 | if (session == nullptr) { 433 | const char *errMessage = nicookie_strerror(nicookie_errno); 434 | nicolive_log_error("%s", errMessage); 435 | return false; 436 | } 437 | this->setSession(session); 438 | return true; 439 | 440 | this->flags.load_viqo = true; 441 | return true; 442 | } 443 | 444 | void NicoLive::clearLiveInfo() 445 | { 446 | this->live_info = decltype(this->live_info)(); 447 | } 448 | -------------------------------------------------------------------------------- /nico-live.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class NicoLiveWatcher; 9 | class NicoLiveApi; 10 | 11 | class NicoLive 12 | { 13 | friend class NicoLiveWatcher; 14 | 15 | enum class LiveState { 16 | BEFORE_START, 17 | ENABLE_LIVE, 18 | AFTER_END, 19 | }; 20 | 21 | public: 22 | private: 23 | std::string mail; 24 | std::string password; 25 | std::string session; 26 | std::string ticket; 27 | struct { 28 | std::string id; 29 | std::string url; 30 | std::string stream; 31 | std::string ticket; 32 | std::chrono::system_clock::time_point server_time; 33 | std::chrono::system_clock::time_point base_time; 34 | std::chrono::system_clock::time_point open_time; 35 | std::chrono::system_clock::time_point start_time; 36 | std::chrono::system_clock::time_point end_time; 37 | long long bitrate = 0; 38 | bool exclude = false; 39 | } live_info; 40 | std::string live_url; 41 | std::string live_key; 42 | std::string onair_live_id; 43 | struct { 44 | bool session_valid = false; 45 | bool onair = false; 46 | bool load_viqo = false; 47 | bool adjust_bitrate = false; 48 | bool silent_once = false; 49 | } flags; 50 | std::unique_ptr watcher; 51 | std::unique_ptr webApi; 52 | 53 | public: 54 | NicoLive(); 55 | ~NicoLive(); 56 | void setSession(const char *session); 57 | void setSession(const std::string &session); 58 | void setAccount(const char *mail, const char *password); 59 | void setAccount(const std::string &mail, const std::string &password); 60 | void setEnabledAdjustBitrate(bool enabled); 61 | 62 | const std::string &getMail() const; 63 | const std::string &getPassword() const; 64 | const std::string &getSession() const; 65 | const std::string &getLiveId() const; 66 | const std::string &getLiveUrl() const; 67 | const std::string &getLiveKey() const; 68 | long long getLiveBitrate() const; 69 | const std::string &getOnairLiveId() const; 70 | bool enabledLive() const; 71 | LiveState getLiveState() const; 72 | std::chrono::system_clock::time_point getLiveStartTime() const; 73 | std::chrono::system_clock::time_point getLiveEndTime() const; 74 | std::chrono::system_clock::duration getRemainingStartTime() const; 75 | std::chrono::system_clock::duration getRemainingEndTime() const; 76 | std::chrono::system_clock::duration getRemainingLive() const; 77 | bool enabledStopBeforeEndTime() const; 78 | bool enabledStartBeforeStartTime() const; 79 | 80 | bool enabledAdjustBitrate() const; 81 | bool enabledSession() const; 82 | bool isOnair() const; 83 | 84 | void startStreaming(); 85 | void stopStreaming(); 86 | void startWatching(); 87 | void stopWatching(); 88 | 89 | bool checkSession(); 90 | bool checkLive(); 91 | bool loadViqoSettings(); 92 | 93 | void nextSilentOnce(); 94 | bool silentOnce(); 95 | 96 | private: 97 | // Access Niconico Site 98 | bool siteLogin(); 99 | bool siteLoginNLE(); 100 | bool sitePubStat(); 101 | bool siteLiveProf(); 102 | 103 | void clearLiveInfo(); 104 | }; 105 | -------------------------------------------------------------------------------- /nicolive-api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "nicookie.h" 4 | #include "nico-live-api.hpp" 5 | #include "nicolive-errno.h" 6 | #include "nicolive-log.h" 7 | 8 | namespace 9 | { 10 | char *acquired_session = nullptr; 11 | inline void nicolive_api_set_session(const char *str) 12 | { 13 | if (acquired_session != nullptr) { 14 | bfree(acquired_session); 15 | } 16 | acquired_session = bstrdup(str); 17 | } 18 | inline void nicolive_api_set_session(const std::string &str) 19 | { 20 | nicolive_api_set_session(str.c_str()); 21 | } 22 | inline const std::string nicolive_api_get_session_nla(const NicoLiveApi &nla) 23 | { 24 | return nla.getCookie("user_session"); 25 | } 26 | } 27 | 28 | extern "C" void nicolive_api_clear_session() 29 | { 30 | if (acquired_session != nullptr) { 31 | bfree(acquired_session); 32 | acquired_session = nullptr; 33 | } 34 | } 35 | 36 | extern "C" const char *nicolive_api_get_session() { return acquired_session; } 37 | extern "C" const char *nicolive_api_get_session_login( 38 | const char *mail, const char *password) 39 | { 40 | if (mail == nullptr) { 41 | nicolive_errno = NICOLIVE_ERROR_NO_MAIL_ADDRESS; 42 | return nullptr; 43 | } 44 | if (mail[0] == '\0') { 45 | nicolive_errno = NICOLIVE_ERROR_EMPTY_MAIL_ADDRESS; 46 | return nullptr; 47 | } 48 | if (password == nullptr) { 49 | nicolive_errno = NICOLIVE_ERROR_NO_PASSWORD; 50 | return nullptr; 51 | } 52 | if (password[0] == '\0') { 53 | nicolive_errno = NICOLIVE_ERROR_EMPTY_PASSWORD; 54 | return nullptr; 55 | } 56 | 57 | std::string mail_str(mail); 58 | std::string password_str(password); 59 | NicoLiveApi nla; 60 | if (nla.loginSiteNicolive(mail_str, password_str)) { 61 | nicolive_errno = NICOLIVE_ERROR_LOGIN_SUCCESSFUL; 62 | nicolive_api_set_session(nicolive_api_get_session_nla(nla)); 63 | } else { 64 | nicolive_errno = NICOLIVE_ERROR_LOGIN_FAILURE; 65 | nicolive_api_clear_session(); 66 | } 67 | return acquired_session; 68 | } 69 | extern "C" const char *nicolive_api_get_session_app(int app) 70 | { 71 | nicookie_errno = 0; 72 | const char *session = nicookie_get_session(app); 73 | if (session != nullptr) { 74 | nicolive_errno = NICOLIVE_ERROR_COOKIE_ACQUISITION_SUCCESSFUL; 75 | nicolive_api_set_session(session); 76 | } else { 77 | nicolive_errno = NICOLIVE_ERROR_NICOOKIE | nicookie_errno; 78 | nicolive_api_clear_session(); 79 | } 80 | return acquired_session; 81 | } 82 | 83 | extern "C" bool nicolive_api_check_session(const char *session) 84 | { 85 | if (session == nullptr) { 86 | nicolive_errno = NICOLIVE_ERROR_NO_USER_SESSION; 87 | return false; 88 | } 89 | if (session[0] == '\0') { 90 | nicolive_errno = NICOLIVE_ERROR_EMPTY_USER_SESSION; 91 | return false; 92 | } 93 | 94 | std::string session_str(session); 95 | NicoLiveApi nla; 96 | nla.setCookie("user_session", session_str); 97 | const std::string statusXpath = "/getpublishstatus/@status"; 98 | const std::string errorCodeXpath = 99 | "/getpublishstatus/error/code/text()"; 100 | std::unordered_map> data; 101 | data[statusXpath] = std::vector(); 102 | data[errorCodeXpath] = std::vector(); 103 | bool result = nla.getPublishStatus(&data); 104 | 105 | if (!result) { 106 | nicolive_errno = NICOLIVE_ERROR_ACCESS_ERROR; 107 | return false; 108 | } 109 | if (data[statusXpath].empty()) { 110 | // FIXME: access error? service down? 111 | nicolive_errno = NICOLIVE_ERROR_ACCESS_ERROR; 112 | return false; 113 | } 114 | 115 | std::string &status = data[statusXpath].front(); 116 | 117 | if (status == "ok") { 118 | nicolive_errno = NICOLIVE_ERROR_VALID_USER_SESSION; 119 | return true; 120 | } 121 | 122 | if (status == "fail") { 123 | std::string error_code(data[errorCodeXpath].empty() 124 | ? "" 125 | : data[errorCodeXpath].front()); 126 | if (error_code == "notfound" || 127 | error_code == "permison_denied") { 128 | nicolive_errno = NICOLIVE_ERROR_VALID_USER_SESSION; 129 | return true; 130 | } 131 | } 132 | nicolive_errno = NICOLIVE_ERROR_INVALID_USER_SESSION; 133 | return false; 134 | } 135 | 136 | extern "C" bool nicolive_api_check_login(const char *mail, const char *password) 137 | { 138 | const char *session = nicolive_api_get_session_login(mail, password); 139 | if (session != nullptr) { 140 | return nicolive_api_check_session(session); 141 | } else { 142 | return false; 143 | } 144 | } 145 | 146 | extern "C" bool nicolive_api_check_app(int app) 147 | { 148 | const char *session = nicolive_api_get_session_app(app); 149 | if (session != nullptr) { 150 | return nicolive_api_check_session(session); 151 | } else { 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /nicolive-api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #else 6 | #include 7 | #endif 8 | 9 | void nicolive_api_clear_session(void); 10 | 11 | const char *nicolive_api_get_session(void); 12 | const char *nicolive_api_get_session_login( 13 | const char *mail, const char *password); 14 | const char *nicolive_api_get_session_app(int app); 15 | 16 | bool nicolive_api_check_session(const char *session); 17 | bool nicolive_api_check_login(const char *mail, const char *password); 18 | bool nicolive_api_check_app(int app); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /nicolive-errno.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int nicolive_errno = NICOLIVE_ERROR_NONE; 6 | const char *nicolive_strerror(int errnum) 7 | { 8 | switch (errnum) { 9 | case NICOLIVE_ERROR_NONE: 10 | return obs_module_text("NoError"); 11 | case NICOLIVE_ERROR_ACCESS_ERROR: 12 | return obs_module_text("AccessError"); 13 | // login with mail address and password 14 | case NICOLIVE_ERROR_LOGIN_SUCCESSFUL: 15 | return obs_module_text("LoginSuccessful"); 16 | case NICOLIVE_ERROR_LOGIN_FAILURE: 17 | return obs_module_text("LoginFailure"); 18 | case NICOLIVE_ERROR_NO_MAIL_ADDRESS: 19 | return obs_module_text("NoMailAddress"); 20 | case NICOLIVE_ERROR_EMPTY_MAIL_ADDRESS: 21 | return obs_module_text("EmptyMailAddress"); 22 | case NICOLIVE_ERROR_ILLEGAL_TYPE_MAIL_ADDRESS: 23 | return obs_module_text("IllegalTypeMailAddress"); 24 | case NICOLIVE_ERROR_NO_PASSWORD: 25 | return obs_module_text("NoPassword"); 26 | case NICOLIVE_ERROR_EMPTY_PASSWORD: 27 | return obs_module_text("EmptyPassword"); 28 | // check session 29 | case NICOLIVE_ERROR_VALID_USER_SESSION: 30 | return obs_module_text("ValidUserSession"); 31 | case NICOLIVE_ERROR_INVALID_USER_SESSION: 32 | return obs_module_text("InvalidUserSession"); 33 | case NICOLIVE_ERROR_NO_USER_SESSION: 34 | return obs_module_text("NoUserSession"); 35 | case NICOLIVE_ERROR_EMPTY_USER_SESSION: 36 | return obs_module_text("EMPTYUserSession"); 37 | case NICOLIVE_ERROR_ILLEGAL_TYPE_USER_SESSION: 38 | return obs_module_text("IllegalTypeUserSession"); 39 | // cookie acquisition 40 | case NICOLIVE_ERROR_COOKIE_ACQUISITION_SUCCESSFUL: 41 | return obs_module_text("CookieAcquisitionSuccessful"); 42 | case NICOLIVE_ERROR_COOKIE_ACQUISITION_FAILURE: 43 | return obs_module_text("CookieAcquisitionFailure"); 44 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_NONE: 45 | return obs_module_text("NoError"); 46 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_NOT_IMPLEMENT: 47 | return obs_module_text("UnimplementedApplication"); 48 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_UNKNOWN_APP: 49 | return obs_module_text("UnknownApplication"); 50 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_NOT_SUPPORT_APP: 51 | return obs_module_text("UnsupportedApplication"); 52 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_NOT_FOUND_DATA: 53 | return obs_module_text("DataNotFound"); 54 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_NOT_FOUND_FILE: 55 | return obs_module_text("FileNotFound"); 56 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_INVALID_DATA_FORMAT: 57 | return obs_module_text("BrokenData"); 58 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_FAILED_DECRYT: 59 | return obs_module_text("DecryptionFailure"); 60 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_FAILED_OPEN_DATA_FILE: 61 | return obs_module_text("FailedOpenFile"); 62 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_FAILED_READ_DATA: 63 | return obs_module_text("FailedReadFile"); 64 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_SQLITE: 65 | return obs_module_text("SQLiteError"); 66 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_FAILED_PARSE_PROFILE: 67 | return obs_module_text("FailedParseProfile"); 68 | case NICOLIVE_ERROR_NICOOKIE | NICOOKIE_ERROR_UNKNOWN: 69 | case NICOLIVE_ERROR_UNKNOWN: 70 | default: 71 | return obs_module_text("UnknownError"); 72 | } 73 | } 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | -------------------------------------------------------------------------------- /nicolive-errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | enum nicolive_error { 8 | NICOLIVE_ERROR_NONE = 0, 9 | NICOLIVE_ERROR_ACCESS_ERROR, 10 | // login with mail address and password 11 | NICOLIVE_ERROR_LOGIN_SUCCESSFUL = 0x100, 12 | NICOLIVE_ERROR_LOGIN_FAILURE, 13 | NICOLIVE_ERROR_NO_MAIL_ADDRESS, 14 | NICOLIVE_ERROR_EMPTY_MAIL_ADDRESS, 15 | NICOLIVE_ERROR_ILLEGAL_TYPE_MAIL_ADDRESS, 16 | NICOLIVE_ERROR_NO_PASSWORD, 17 | NICOLIVE_ERROR_EMPTY_PASSWORD, 18 | // check session 19 | NICOLIVE_ERROR_VALID_USER_SESSION = 0x200, 20 | NICOLIVE_ERROR_INVALID_USER_SESSION, 21 | NICOLIVE_ERROR_NO_USER_SESSION, 22 | NICOLIVE_ERROR_EMPTY_USER_SESSION, 23 | NICOLIVE_ERROR_ILLEGAL_TYPE_USER_SESSION, 24 | // cookie acquisition 25 | NICOLIVE_ERROR_COOKIE_ACQUISITION_SUCCESSFUL = 0x300, 26 | NICOLIVE_ERROR_COOKIE_ACQUISITION_FAILURE, 27 | NICOLIVE_ERROR_NICOOKIE = 0x1000, 28 | NICOLIVE_ERROR_UNKNOWN = 0xFFFF, 29 | }; 30 | 31 | extern int nicolive_errno; 32 | const char *nicolive_strerror(int errnum); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /nicolive-log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifndef __func__ 7 | #define __func__ __FUNCTION__ 8 | #endif 9 | 10 | #define nicolive_log_error(format, ...) \ 11 | blog(LOG_ERROR, "[nicolive] " format, ##__VA_ARGS__) 12 | #define nicolive_log_warn(format, ...) \ 13 | blog(LOG_WARNING, "[nicolive] " format, ##__VA_ARGS__) 14 | #define nicolive_log_info(format, ...) \ 15 | blog(LOG_INFO, "[nicolive] " format, ##__VA_ARGS__) 16 | 17 | #ifdef _DEBUG 18 | #define nicolive_log_debug(format, ...) \ 19 | blog(LOG_DEBUG, "[nicolive] " format, ##__VA_ARGS__) 20 | #define nicolive_log_debug_call_func() \ 21 | nicolive_log_debug("call func: %s", __func__) 22 | #else 23 | #define nicolive_log_debug(format, ...) 24 | #define nicolive_log_debug_call_func() 25 | #endif 26 | -------------------------------------------------------------------------------- /nicolive-operation.c: -------------------------------------------------------------------------------- 1 | #include "nicolive.h" 2 | #include 3 | #include 4 | #include 5 | #include "nicolive-log.h" 6 | #include "nicolive-operation.h" 7 | 8 | #define START_STREAMING_HOTKEY_NAME "OBSBasic.StartStreaming" 9 | // #define STOP_STREAMING_HOTKEY_NAME "OBSBasic.StopStreaming" 10 | #define STOP_STREAMING_HOTKEY_NAME "OBSBasic.ForceStopStreaming" 11 | 12 | inline static bool nicolive_operation_click_enum_func( 13 | void *data, obs_hotkey_id id, obs_hotkey_t *key) 14 | { 15 | const char *target = data; 16 | const char *name = obs_hotkey_get_name(key); 17 | if (strcmp(name, target) == 0) { 18 | nicolive_log_debug("click hotkey: %s", name); 19 | obs_hotkey_trigger_routed_callback(id, true); 20 | return false; 21 | } 22 | return true; 23 | } 24 | 25 | void nicolive_streaming_start(void) 26 | { 27 | nicolive_log_info("click start streaming hotkey"); 28 | obs_enum_hotkeys( 29 | nicolive_operation_click_enum_func, START_STREAMING_HOTKEY_NAME); 30 | } 31 | 32 | void nicolive_streaming_stop(void) 33 | { 34 | nicolive_log_info("click stop streaming hotkey"); 35 | obs_enum_hotkeys( 36 | nicolive_operation_click_enum_func, STOP_STREAMING_HOTKEY_NAME); 37 | } 38 | -------------------------------------------------------------------------------- /nicolive-operation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | void nicolive_streaming_start(void); 10 | void nicolive_streaming_stop(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /nicolive.cpp: -------------------------------------------------------------------------------- 1 | #include "nicolive.h" 2 | #include 3 | #include "nico-live-api.hpp" 4 | #include "nico-live.hpp" 5 | #include "nicolive-log.h" 6 | 7 | // cannot use anonymouse struct because VS2013 bug 8 | // https://connect.microsoft.com/VisualStudio/feedback/details/808506/nsdmi-silently-ignored-on-nested-anonymous-classes-and-structs 9 | namespace 10 | { 11 | struct nicolive_buff_s { 12 | char *mail = nullptr; 13 | char *password = nullptr; 14 | char *session = nullptr; 15 | char *live_id = nullptr; 16 | char *live_url = nullptr; 17 | char *live_key = nullptr; 18 | } nicolive_buff; 19 | } 20 | 21 | extern "C" void *nicolive_create(void) { return new NicoLive(); } 22 | 23 | extern "C" void nicolive_destroy(void *data) 24 | { 25 | NicoLive *nicolive = static_cast(data); 26 | delete nicolive; 27 | } 28 | 29 | extern "C" void nicolive_set_settings( 30 | void *data, const char *mail, const char *password, const char *session) 31 | { 32 | NicoLive *nicolive = static_cast(data); 33 | nicolive_log_debug("password: %s", password); 34 | nicolive->setAccount(mail, password); 35 | nicolive->setSession(session); 36 | } 37 | 38 | extern "C" void nicolive_set_enabled_adjust_bitrate(void *data, bool enabled) 39 | { 40 | NicoLive *nicolive = static_cast(data); 41 | nicolive->setEnabledAdjustBitrate(enabled); 42 | } 43 | 44 | extern "C" const char *nicolive_get_mail(const void *data) 45 | { 46 | const NicoLive *nicolive = static_cast(data); 47 | bfree(nicolive_buff.mail); 48 | nicolive_buff.mail = bstrdup(nicolive->getMail().c_str()); 49 | return nicolive_buff.mail; 50 | } 51 | 52 | extern "C" const char *nicolive_get_password(const void *data) 53 | { 54 | const NicoLive *nicolive = static_cast(data); 55 | bfree(nicolive_buff.password); 56 | nicolive_buff.password = bstrdup(nicolive->getPassword().c_str()); 57 | return nicolive_buff.password; 58 | } 59 | 60 | extern "C" const char *nicolive_get_session(const void *data) 61 | { 62 | const NicoLive *nicolive = static_cast(data); 63 | bfree(nicolive_buff.session); 64 | nicolive_buff.session = bstrdup(nicolive->getSession().c_str()); 65 | return nicolive_buff.session; 66 | } 67 | 68 | extern "C" const char *nicolive_get_live_id(const void *data) 69 | { 70 | const NicoLive *nicolive = static_cast(data); 71 | bfree(nicolive_buff.live_id); 72 | nicolive_buff.live_id = bstrdup(nicolive->getLiveId().c_str()); 73 | return nicolive_buff.live_id; 74 | } 75 | 76 | extern "C" const char *nicolive_get_live_url(const void *data) 77 | { 78 | const NicoLive *nicolive = static_cast(data); 79 | bfree(nicolive_buff.live_url); 80 | nicolive_buff.live_url = bstrdup(nicolive->getLiveUrl().c_str()); 81 | return nicolive_buff.live_url; 82 | } 83 | 84 | extern "C" const char *nicolive_get_live_key(const void *data) 85 | { 86 | const NicoLive *nicolive = static_cast(data); 87 | bfree(nicolive_buff.live_key); 88 | nicolive_buff.live_key = bstrdup(nicolive->getLiveKey().c_str()); 89 | return nicolive_buff.live_key; 90 | } 91 | 92 | extern "C" long long nicolive_get_live_bitrate(const void *data) 93 | { 94 | const NicoLive *nicolive = static_cast(data); 95 | return nicolive->getLiveBitrate(); 96 | } 97 | 98 | extern "C" bool nicolive_enabled_adjust_bitrate(const void *data) 99 | { 100 | const NicoLive *nicolive = static_cast(data); 101 | return nicolive->enabledAdjustBitrate(); 102 | } 103 | 104 | extern "C" bool nicolive_load_viqo_settings(void *data) 105 | { 106 | NicoLive *nicolive = static_cast(data); 107 | return nicolive->loadViqoSettings(); 108 | } 109 | 110 | extern "C" bool nicolive_check_session(void *data) 111 | { 112 | NicoLive *nicolive = static_cast(data); 113 | return nicolive->checkSession(); 114 | } 115 | 116 | extern "C" bool nicolive_check_live(void *data) 117 | { 118 | NicoLive *nicolive = static_cast(data); 119 | return nicolive->checkLive(); 120 | } 121 | 122 | extern "C" void nicolive_start_streaming(void *data) 123 | { 124 | NicoLive *nicolive = static_cast(data); 125 | nicolive->startStreaming(); 126 | } 127 | 128 | extern "C" void nicolive_stop_streaming(void *data) 129 | { 130 | NicoLive *nicolive = static_cast(data); 131 | nicolive->stopStreaming(); 132 | } 133 | 134 | extern "C" void nicolive_start_watching(void *data) 135 | { 136 | NicoLive *nicolive = static_cast(data); 137 | nicolive->startWatching(); 138 | } 139 | 140 | extern "C" void nicolive_stop_watching(void *data) 141 | { 142 | NicoLive *nicolive = static_cast(data); 143 | nicolive->stopWatching(); 144 | } 145 | 146 | extern "C" bool nicolive_silent_once(void *data) 147 | { 148 | NicoLive *nicolive = static_cast(data); 149 | return nicolive->silentOnce(); 150 | } 151 | -------------------------------------------------------------------------------- /nicolive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | void *nicolive_create(void); 10 | void nicolive_destroy(void *data); 11 | 12 | void nicolive_set_settings( 13 | void *data, const char *mail, const char *password, const char *session); 14 | void nicolive_set_enabled_adjust_bitrate(void *data, bool enabled); 15 | 16 | const char *nicolive_get_mail(const void *data); 17 | const char *nicolive_get_password(const void *data); 18 | const char *nicolive_get_session(const void *data); 19 | const char *nicolive_get_live_url(const void *data); 20 | const char *nicolive_get_live_key(const void *data); 21 | long long nicolive_get_live_bitrate(const void *data); 22 | 23 | bool nicolive_enabled_adjust_bitrate(const void *data); 24 | 25 | bool nicolive_load_viqo_settings(void *data); 26 | bool nicolive_check_session(void *data); 27 | bool nicolive_check_live(void *data); 28 | 29 | void nicolive_start_streaming(void *data); 30 | void nicolive_stop_streaming(void *data); 31 | 32 | void nicolive_start_watching(void *data); 33 | void nicolive_stop_watching(void *data); 34 | 35 | bool nicolive_silent_once(void *data); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /nicookie.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Nicookie C++ (better C) 3 | */ 4 | #include "nicookie.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "json/json.hpp" 11 | // #include "sqlite/sqlite3.h" 12 | 13 | // Globale Objects 14 | int nicookie_errno = NICOOKIE_ERROR_NONE; 15 | 16 | namespace 17 | { 18 | using json = nlohmann::json; 19 | // Private Globale Objects 20 | const int availableApps[] = { 21 | #ifdef _WIN32 22 | NICOOKIE_APP_IE, NICOOKIE_APP_EDGE, 23 | #elif __APPLE__ 24 | NICOOKIE_APP_SAFARI, 25 | #endif 26 | NICOOKIE_APP_FIREFOX, NICOOKIE_APP_CHROME, NICOOKIE_APP_OPERA, 27 | NICOOKIE_APP_VIQO, NICOOKIE_APP_NONE}; 28 | std::string cookieSession = ""; 29 | 30 | // Directory Paths 31 | #ifdef _WIN32 32 | const char *PATH_SEPARATOR = "\\"; 33 | #else 34 | const char *PATH_SEPARATOR = "/"; 35 | #endif 36 | inline std::string HomeDirectory() 37 | { 38 | std::string str = ""; 39 | #ifdef _WIN32 40 | str += getenv("HOMEDRIVE"); 41 | str += getenv("HOMEPATH"); 42 | #else 43 | str += getenv("HOME"); 44 | #endif 45 | return str; 46 | } 47 | #ifdef _WIN32 48 | inline std::string AppDataDirectory() { return getenv("APPDATA"); } 49 | inline std::string LocalAppDataDirectory() { return getenv("LOCALAPPDATA"); } 50 | #elif __APPLE__ 51 | inline std::string AppDataDirectory() 52 | { 53 | std::string str = ""; 54 | str += HomeDirectory(); 55 | str += "/Library/Application Support"; 56 | return str; 57 | } 58 | #else 59 | #endif 60 | 61 | std::string FindViqo(); 62 | 63 | std::string FindViqo() 64 | { 65 | std::string settingsJson = ""; 66 | #ifdef _WIN32 67 | settingsJson += LocalAppDataDirectory(); 68 | #elif __APPLE__ 69 | settingsJson += AppDataDirectory(); 70 | #else 71 | settingsJson += HomeDirectory(); 72 | settingsJson += "/.local/share"; 73 | #endif 74 | settingsJson += PATH_SEPARATOR; 75 | settingsJson += "Viqo"; 76 | settingsJson += PATH_SEPARATOR; 77 | settingsJson += "settings.json"; 78 | 79 | errno = 0; 80 | std::ifstream ifs(settingsJson); 81 | if (ifs.fail()) { 82 | if (errno == ENOENT) { 83 | nicookie_errno = NICOOKIE_ERROR_NOT_FOUND_FILE; 84 | } else { 85 | nicookie_errno = NICOOKIE_ERROR_FAILED_OPEN_DATA_FILE; 86 | } 87 | return ""; 88 | } 89 | json j; 90 | ifs >> j; 91 | std::string session = j["login_way"]["user_session"]; 92 | if (session.empty()) { 93 | nicookie_errno = NICOOKIE_ERROR_NOT_FOUND_DATA; 94 | return ""; 95 | } 96 | return session; 97 | }; 98 | 99 | // class Nicookie 100 | // { 101 | // enum nicookie_error error; 102 | // std::string session; 103 | // std::vector appList; 104 | // 105 | // public: 106 | // Nicookie(); 107 | // const std::vector &GetAppList() const; 108 | // const std::string GetSession(); 109 | // enum nicookie_error GetError() const; 110 | // static const std::string &AppName(enum nicookie_app app); 111 | // static const std::string &StrError(enum nicookie_error errnum); 112 | // }; 113 | // 114 | // Nicookie::Nicookie() : error(NICOOKIE_ERROR_NONE), session(""), 115 | // appList() 116 | // { 117 | // #ifdef _WIN32 118 | // this->appList.push_back(NICOOKIE_APP_IE); 119 | // this->appList.push_back(NICOOKIE_APP_EDGE); 120 | // #elif __APPLE__ 121 | // this->appList.push_back(NICOOKIE_APP_SAFARI); 122 | // #endif 123 | // this->appList.push_back(NICOOKIE_APP_FIREFOX); 124 | // this->appList.push_back(NICOOKIE_APP_CHROME); 125 | // this->appList.push_back(NICOOKIE_APP_OPERA); 126 | // this->appList.push_back(NICOOKIE_APP_NONE); 127 | // } 128 | // const std::vector &Nicookie::GetAppList() const 129 | // { 130 | // return this->appList; 131 | // } 132 | // const std::string Nicookie::GetSession() 133 | // { 134 | // // TODO 135 | // return this->session; 136 | // } 137 | // enum nicookie_error Nicookie::GetError() const { return this->error; 138 | // }; 139 | // const std::string &Nicookie::AppName(enum nicookie_app app) {} 140 | // const std::string &Nicookie::StrError(enum nicookie_error errnum) {} 141 | // static Nicookie nc; 142 | } 143 | 144 | // extern C interface 145 | extern "C" const int *nicookie_available_apps() { return availableApps; } 146 | extern "C" const char *nicookie_app_name(int app) 147 | { 148 | switch (app) { 149 | case NICOOKIE_APP_NONE: 150 | return ""; 151 | break; 152 | case NICOOKIE_APP_IE: 153 | return "Internet Explorer"; 154 | break; 155 | case NICOOKIE_APP_EDGE: 156 | return "Microsoft Edge"; 157 | break; 158 | case NICOOKIE_APP_SAFARI: 159 | return "Safari"; 160 | break; 161 | case NICOOKIE_APP_FIREFOX: 162 | return "Mozilla Firefox"; 163 | break; 164 | case NICOOKIE_APP_CHROME: 165 | return "Google Chrome"; 166 | break; 167 | case NICOOKIE_APP_OPERA: 168 | return "Opera"; 169 | break; 170 | case NICOOKIE_APP_VIQO: 171 | return "Viqo"; 172 | break; 173 | } 174 | return "Unknown"; 175 | } 176 | 177 | extern "C" const char *nicookie_get_session(int app) 178 | { 179 | cookieSession.clear(); 180 | switch (app) { 181 | case NICOOKIE_APP_NONE: 182 | nicookie_errno = NICOOKIE_ERROR_UNKNOWN_APP; 183 | break; 184 | case NICOOKIE_APP_IE: 185 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 186 | break; 187 | case NICOOKIE_APP_EDGE: 188 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 189 | break; 190 | case NICOOKIE_APP_SAFARI: 191 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 192 | break; 193 | case NICOOKIE_APP_FIREFOX: 194 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 195 | break; 196 | case NICOOKIE_APP_CHROME: 197 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 198 | break; 199 | case NICOOKIE_APP_OPERA: 200 | nicookie_errno = NICOOKIE_ERROR_NOT_IMPLEMENT; 201 | break; 202 | case NICOOKIE_APP_VIQO: 203 | cookieSession = FindViqo(); 204 | break; 205 | default: 206 | nicookie_errno = NICOOKIE_ERROR_UNKNOWN_APP; 207 | } 208 | if (cookieSession.empty()) { 209 | return nullptr; 210 | } 211 | return cookieSession.c_str(); 212 | } 213 | extern "C" const char *nicookie_strerror(int errnum) 214 | { 215 | switch (errnum) { 216 | case NICOOKIE_ERROR_NONE: 217 | return "No error."; 218 | break; 219 | case NICOOKIE_ERROR_NOT_IMPLEMENT: 220 | return "The operation is not implemented."; 221 | break; 222 | case NICOOKIE_ERROR_UNKNOWN_APP: 223 | return "An unknown applaction was specified."; 224 | break; 225 | case NICOOKIE_ERROR_NOT_SUPPORT_APP: 226 | return "The specified application is not supported."; 227 | break; 228 | case NICOOKIE_ERROR_NOT_FOUND_DATA: 229 | return "Data was not found."; 230 | break; 231 | case NICOOKIE_ERROR_NOT_FOUND_FILE: 232 | return "Data file was not found."; 233 | break; 234 | case NICOOKIE_ERROR_INVALID_DATA_FORMAT: 235 | return "The data format is invaild."; 236 | break; 237 | case NICOOKIE_ERROR_FAILED_DECRYT: 238 | return "Decryption failed."; 239 | break; 240 | case NICOOKIE_ERROR_FAILED_OPEN_DATA_FILE: 241 | return "Could not open data file."; 242 | break; 243 | case NICOOKIE_ERROR_FAILED_READ_DATA: 244 | return "Could not read file."; 245 | break; 246 | case NICOOKIE_ERROR_SQLITE: 247 | return "Error was occured in reading SQLite3"; 248 | break; 249 | case NICOOKIE_ERROR_FAILED_PARSE_PROFILE: 250 | return "Error was occured in parsing a profile file"; 251 | break; 252 | } 253 | return "Unknown error hos occured."; 254 | } 255 | -------------------------------------------------------------------------------- /nicookie.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | enum nicookie_error { 8 | NICOOKIE_ERROR_NONE = 0, 9 | NICOOKIE_ERROR_UNKNOWN, 10 | NICOOKIE_ERROR_NOT_IMPLEMENT, 11 | NICOOKIE_ERROR_UNKNOWN_APP, 12 | NICOOKIE_ERROR_NOT_SUPPORT_APP, 13 | NICOOKIE_ERROR_NOT_FOUND_DATA, 14 | NICOOKIE_ERROR_NOT_FOUND_FILE, 15 | NICOOKIE_ERROR_INVALID_DATA_FORMAT, 16 | NICOOKIE_ERROR_FAILED_DECRYT, 17 | NICOOKIE_ERROR_FAILED_OPEN_DATA_FILE, 18 | NICOOKIE_ERROR_FAILED_READ_DATA, 19 | NICOOKIE_ERROR_SQLITE, 20 | NICOOKIE_ERROR_FAILED_PARSE_PROFILE, 21 | }; 22 | 23 | enum nicookie_app { 24 | NICOOKIE_APP_NONE = 0, 25 | NICOOKIE_APP_UNKNOWN, 26 | NICOOKIE_APP_IE, 27 | NICOOKIE_APP_EDGE, 28 | NICOOKIE_APP_SAFARI, 29 | NICOOKIE_APP_FIREFOX, 30 | NICOOKIE_APP_CHROME, 31 | NICOOKIE_APP_OPERA, 32 | NICOOKIE_APP_VIQO, 33 | }; 34 | 35 | extern int nicookie_errno; 36 | // return array end with NICOOKIE_APP_NONE 37 | const int *nicookie_available_apps(void); 38 | const char *nicookie_app_name(int app); 39 | // if error then return NULL 40 | const char *nicookie_get_session(int app); 41 | const char *nicookie_strerror(int errnum); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /nicookie_memo.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * original code by https://github.com/diginatu/Viqo 3 | * file: src/nicookie.h, src/nicookie.cpp 4 | * Licensed under the MIT License Copyright (c) 2014 diginatu 5 | * see https://github.com/diginatu/Viqo/raw/master/LICENSE 6 | * first original code by https://github.com/raccy/nicookie 7 | * Licensed under the MIT License Copyright (c) 2014 raccy 8 | */ 9 | /* 10 | * Nicookie.h 11 | ニコニコ動画ユーザセッションクッキー取得ライブラリ for Qt 12 | * 使い方 13 | * Nicookie *nicookie = new Nicookie(this); 14 | * // リストを取得 15 | * QStringList list = nicooki->getBrowserList(); 16 | * // どれか一個指定して実行。 17 | * QString user_session = 18 | nicookie->getUserSession(list[0]); 19 | * if (nicookie->hasError()) { 20 | * // エラー処理 21 | * qError() << nicookie->errorString(); 22 | * } 23 | */ 24 | #ifndef NICOOKIE_H 25 | #define NICOOKIE_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | class Nicookie : public QObject 36 | { 37 | Q_OBJECT 38 | public: 39 | static const QString COOKIE_URL; 40 | static const QString COOKIE_HOST; 41 | static const QString COOKIE_NAME; 42 | static const QString COOKIE_PATH; 43 | 44 | static const QString INTERNET_EXPLORER; 45 | static const QString SAFARI; 46 | static const QString FIREFOX; 47 | static const QString CHROME; 48 | static const QString OPERA; 49 | 50 | static const QStringList BROWSER_LIST; 51 | 52 | enum Error : int { 53 | NoError = 0, 54 | NotImplementError, 55 | UnknownBrowserError, 56 | NotSupportBrowserError, 57 | NotFoundDataError, 58 | NotFoundCookiesFileError, 59 | InvalidDataFormtaError, 60 | FailedDecrytError, 61 | FailedOpenCookiesFileError, 62 | FailedReadDataError, 63 | SQLiteError, 64 | FailedParseProfileError, 65 | }; 66 | 67 | private: 68 | enum Error errorNum; 69 | QString userSession; 70 | 71 | public: 72 | explicit Nicookie(QObject *parent = 0); 73 | ~Nicookie(); 74 | QString getUserSession(QString browser); 75 | const QStringList &getBrowserList() const; 76 | Error error() const; 77 | const QString errorString() const; 78 | bool hasError() const; 79 | void clear(); 80 | 81 | private: 82 | // find Cookie 83 | #ifdef Q_OS_WIN 84 | bool findInternetExplorer(); 85 | #endif // Q_OS_WIN 86 | #ifdef Q_OS_OSX 87 | bool findSafari(); 88 | bool safariFindFile(QIODevice &device); 89 | bool safariFindPage(QIODevice &device); 90 | bool safariFindCookie(QIODevice &device); 91 | #endif // Q_OS_OSX 92 | bool findFirefox(); 93 | QStringList firefoxGetProfileList(const QString &profile_ini); 94 | bool firefoxFindValue(const QString &cookies_path); 95 | 96 | bool findChrome(); 97 | bool chromeFindValue(const QString &cookies_path); 98 | QString chromeDecrypt(const QByteArray &encrypt_data); 99 | 100 | bool findOpera(); 101 | 102 | // utility 103 | void setError(Error num); 104 | bool querySqlite3(const QString &sqlite3_file, const QString &query, 105 | const QMap &placeholders, 106 | QMap &values); 107 | 108 | #ifdef Q_OS_OSX 109 | quint32 readUint32BE(QIODevice &device); 110 | quint32 readUint32LE(QIODevice &device); 111 | #endif // Q_OS_OSX 112 | bool checkSameStr(QIODevice &device, const QString &str); 113 | QString readStr(QIODevice &device); 114 | signals: 115 | 116 | public slots: 117 | }; 118 | 119 | #endif // NICOOKIE_H 120 | 121 | /* 122 | * Nicookie.cpp 123 | ニコニコ動画ユーザセッションクッキー取得ライブラリ for Qt 124 | */ 125 | #include 126 | #include 127 | #include 128 | #include 129 | #include 130 | #include 131 | #include 132 | 133 | // for Firefox and Chrome 134 | #include 135 | #include 136 | #include 137 | 138 | #include 139 | 140 | // for Safari 141 | #ifdef Q_OS_OSX 142 | #include 143 | #endif 144 | 145 | // for IE 146 | #ifdef Q_OS_WIN 147 | #include 148 | #include 149 | #ifdef _MSC_VER 150 | #include 151 | #endif // _MSC_VER 152 | #endif 153 | 154 | // for Chrome 155 | #if defined(Q_OS_WIN) 156 | #include 157 | #elif defined(Q_OS_OSX) 158 | #include 159 | #include 160 | #include 161 | #else 162 | #include 163 | #include 164 | #include 165 | #endif 166 | 167 | #include "nicookie.h" 168 | 169 | const QString Nicookie::COOKIE_URL = "http://www.nicovideo.jp/"; 170 | const QString Nicookie::COOKIE_HOST = ".nicovideo.jp"; 171 | const QString Nicookie::COOKIE_NAME = "user_session"; 172 | const QString Nicookie::COOKIE_PATH = "/"; 173 | 174 | const QString Nicookie::INTERNET_EXPLORER = "Internet 175 | Explorer "; 176 | const QString Nicookie::SAFARI = "Safari"; 177 | const QString Nicookie::FIREFOX = "Mozilla Firefox"; 178 | const QString Nicookie::CHROME = "Google Chrome"; 179 | const QString Nicookie::OPERA = "Opera"; 180 | 181 | const QStringList Nicookie::BROWSER_LIST = { 182 | #ifdef Q_OS_WIN 183 | Nicookie::INTERNET_EXPLORER, 184 | #endif // Q_OS_WIN 185 | #ifdef Q_OS_MAC 186 | Nicookie::SAFARI, 187 | #endif // Q_OS_MAC 188 | Nicookie::FIREFOX, Nicookie::CHROME, 189 | // Nicookie::OPERA, 190 | }; 191 | 192 | Nicookie::Nicookie(QObject *parent) : QObject(parent) 193 | { 194 | this->errorNum = Nicookie::NoError; 195 | this->userSession = QString(); 196 | } 197 | 198 | Nicookie::~Nicookie() {} 199 | 200 | QString Nicookie::getUserSession(QString browser) 201 | { 202 | clear(); 203 | if (browser == Nicookie::INTERNET_EXPLORER) { 204 | #ifdef Q_OS_WIN 205 | findInternetExplorer(); 206 | #else 207 | setError(Nicookie::NotSupportBrowserError); 208 | #endif // Q_OS_WIN 209 | } else if (browser == Nicookie::SAFARI) { 210 | #ifdef Q_OS_OSX 211 | findSafari(); 212 | #else 213 | setError(Nicookie::NotSupportBrowserError); 214 | #endif // Q_OS_OSX 215 | } else if (browser == Nicookie::FIREFOX) { 216 | findFirefox(); 217 | } else if (browser == Nicookie::CHROME) { 218 | findChrome(); 219 | } else if (browser == Nicookie::OPERA) { 220 | findOpera(); 221 | } else { 222 | setError(Nicookie::UnknownBrowserError); 223 | } 224 | return this->userSession; 225 | } 226 | 227 | const QStringList &Nicookie::getBrowserList() const 228 | { 229 | return Nicookie::BROWSER_LIST; 230 | } 231 | 232 | Nicookie::Error Nicookie::error() const { return this->errorNum; } 233 | 234 | const QString Nicookie::errorString() const 235 | { 236 | switch (this->errorNum) { 237 | case Nicookie::NoError: 238 | return QStringLiteral(u"エラーはありません。"); 239 | break; 240 | case Nicookie::NotImplementError: 241 | return QStringLiteral(u"まだ、実装していません。"); 242 | break; 243 | case Nicookie::UnknownBrowserError: 244 | return QStringLiteral(u"不明なブラウザです。"); 245 | break; 246 | case Nicookie::NotSupportBrowserError: 247 | return QStringLiteral(u"未対応のブラウザです。"); 248 | break; 249 | case Nicookie::NotFoundDataError: 250 | return QStringLiteral(u"データが見つかりませんでした。"); 251 | break; 252 | case Nicookie::NotFoundCookiesFileError: 253 | return QStringLiteral( 254 | u"クッキーファイルが見つかりませんでした。"); 255 | break; 256 | case Nicookie::InvalidDataFormtaError: 257 | return QStringLiteral(u"データフォーマットが不正です。"); 258 | break; 259 | case Nicookie::FailedDecrytError: 260 | return QStringLiteral(u"復号に失敗しました。"); 261 | break; 262 | case Nicookie::FailedOpenCookiesFileError: 263 | return QStringLiteral(u"クッキーファイルを開けませんでした。"); 264 | break; 265 | case Nicookie::FailedReadDataError: 266 | return QStringLiteral(u"データを読み込めませんでした。"); 267 | break; 268 | case Nicookie::SQLiteError: 269 | return QStringLiteral(u"SQLite3の処理でエラーが発生しました。"); 270 | break; 271 | case Nicookie::FailedParseProfileError: 272 | return QStringLiteral( 273 | u"プロファイルの解析でエラーが発生しました。"); 274 | break; 275 | default: 276 | return QStringLiteral(u"不明なエラーです。"); 277 | } 278 | } 279 | 280 | bool Nicookie::hasError() const { return this->errorNum != Nicookie::NoError; } 281 | 282 | void Nicookie::clear() 283 | { 284 | setError(Nicookie::NoError); 285 | this->userSession = QString(); 286 | } 287 | 288 | #ifdef Q_OS_WIN 289 | // # Windows # 290 | 291 | // ## Internet Explorer ## 292 | bool Nicookie::findInternetExplorer() 293 | { 294 | WCHAR cookie_data[256]; 295 | DWORD cookie_data_size = 256; 296 | BOOL result = false; 297 | #ifdef _MSC_VER 298 | // 保護モードでの取得。VisualStudioでのみ有効。 299 | // 32bitは32bitだけ、64bitは64bitだけしか取得できない。 300 | HRESULT hr; 301 | hr = IEGetProtectedModeCookie( 302 | Nicookie::COOKIE_URL.toStdWString().c_str(), 303 | Nicookie::COOKIE_NAME.toStdWString().c_str(), cookie_data, 304 | &cookie_data_size, 0); 305 | result = (hr == S_OK); 306 | #endif // _MSC_VER 307 | if (!result) { 308 | result = InternetGetCookieW( 309 | Nicookie::COOKIE_URL.toStdWString().c_str(), 310 | Nicookie::COOKIE_NAME.toStdWString().c_str(), cookie_data, 311 | &cookie_data_size); 312 | } 313 | if (result) { 314 | this->userSession = 315 | QString::fromStdWString(std::wstring(cookie_data)); 316 | return true; 317 | } else { 318 | setError(Nicookie::NotFoundDataError); 319 | return false; 320 | } 321 | } 322 | #endif // Q_OS_WIN 323 | 324 | #ifdef Q_OS_OSX 325 | // # Mac OSX # 326 | 327 | // ## Safari ## 328 | bool Nicookie::findSafari() 329 | { 330 | QString cookies_path; 331 | cookies_path += QProcessEnvironment::systemEnvironment().value("HOME"); 332 | cookies_path += "/Library/Cookies/Cookies.binarycookies"; 333 | 334 | QFile cookies_file(cookies_path); 335 | if (!cookies_file.exists()) { 336 | setError(Nicookie::NotFoundCookiesFileError); 337 | return false; 338 | } 339 | 340 | if (!cookies_file.open(QIODevice::ReadOnly)) { 341 | setError(Nicookie::FailedOpenCookiesFileError); 342 | return false; 343 | } 344 | 345 | bool result = safariFindFile(cookies_file); 346 | cookies_file.close(); 347 | return result; 348 | } 349 | bool Nicookie::safariFindFile(QIODevice &device) 350 | { 351 | // Signature 352 | QByteArray headr_signature("cook", 4); 353 | if (headr_signature != device.read(4)) { 354 | setError(Nicookie::InvalidDataFormtaError); 355 | return false; 356 | } 357 | 358 | // No. of pages 359 | quint32 page_num = readUint32BE(device); 360 | if (page_num == 0) { 361 | setError(Nicookie::NotFoundDataError); 362 | return false; 363 | } 364 | 365 | // Page N Size 366 | QList page_size_list; 367 | for (quint32 i = 0; i < page_num; ++i) { 368 | page_size_list.append(readUint32BE(device)); 369 | } 370 | if (device.atEnd()) { 371 | setError(Nicookie::InvalidDataFormtaError); 372 | return false; 373 | } 374 | 375 | // Page N 376 | for (auto &page_size : page_size_list) { 377 | qint64 pos = device.pos(); 378 | if (safariFindPage(device)) return true; 379 | device.seek(pos + page_size); 380 | } 381 | 382 | if (!hasError()) { 383 | setError(Nicookie::NotFoundDataError); 384 | } 385 | return false; 386 | } 387 | 388 | bool Nicookie::safariFindPage(QIODevice &device) 389 | { 390 | qint64 begin_pos = device.pos(); 391 | 392 | // Page Header 393 | quint32 page_header = readUint32BE(device); 394 | if (page_header != 0x00000100) { 395 | setError(Nicookie::InvalidDataFormtaError); 396 | return false; 397 | } 398 | 399 | // No. of cookies 400 | quint32 cookie_num = readUint32LE(device); 401 | if (cookie_num == 0) { 402 | // エラーじゃ無い? 403 | return false; 404 | } 405 | 406 | // Cookie N offset 407 | QList cookie_offset_list; 408 | for (quint32 i = 0; i < cookie_num; i++) { 409 | cookie_offset_list.append(readUint32LE(device)); 410 | } 411 | 412 | // Cookie N 413 | for (auto &cookie_offset : cookie_offset_list) { 414 | device.seek(begin_pos + cookie_offset); 415 | if (safariFindCookie(device)) return true; 416 | } 417 | 418 | return false; 419 | } 420 | 421 | bool Nicookie::safariFindCookie(QIODevice &device) 422 | { 423 | qint64 begin_pos = device.pos(); 424 | 425 | readUint32LE(device); // cookie_size not use 426 | readUint32LE(device); // unknown 427 | readUint32LE(device); // flags not use 428 | readUint32LE(device); // unknown 429 | quint32 url_offset = readUint32LE(device); 430 | quint32 name_offset = readUint32LE(device); 431 | quint32 path_offset = readUint32LE(device); 432 | quint32 value_offset = readUint32LE(device); 433 | 434 | // check url 435 | device.seek(begin_pos + url_offset); 436 | if (!checkSameStr(device, Nicookie::COOKIE_HOST)) return false; 437 | 438 | // check name 439 | device.seek(begin_pos + name_offset); 440 | if (!checkSameStr(device, Nicookie::COOKIE_NAME)) return false; 441 | 442 | // check path 443 | device.seek(begin_pos + path_offset); 444 | if (!checkSameStr(device, Nicookie::COOKIE_PATH)) return false; 445 | 446 | device.seek(begin_pos + value_offset); 447 | QString str = readStr(device); 448 | if (str.isEmpty()) { 449 | setError(Nicookie::NotFoundDataError); 450 | return false; 451 | } else { 452 | this->userSession = str; 453 | return true; 454 | } 455 | } 456 | #endif // Q_OS_OSX 457 | 458 | // # Windows and Mac OSX and Linux # 459 | 460 | // ## Firefox ## 461 | bool Nicookie::findFirefox() 462 | { 463 | QString profile_ini; 464 | #if defined(Q_OS_WIN) 465 | profile_ini += 466 | QProcessEnvironment::systemEnvironment().value("APPDATA"); 467 | profile_ini += "\\Mozilla\\Firefox\\profiles.ini"; 468 | #elif defined(Q_OS_OSX) 469 | profile_ini += QProcessEnvironment::systemEnvironment().value("HOME"); 470 | profile_ini += 471 | "/Library/Application 472 | Support / 473 | Firefox / 474 | profiles.ini "; 475 | #else 476 | profile_ini += QProcessEnvironment::systemEnvironment().value("HOME"); 477 | profile_ini += "/.mozilla/firefox/profiles.ini"; 478 | #endif 479 | QStringList profile_list = firefoxGetProfileList(profile_ini); 480 | if (profile_list.isEmpty()) return false; 481 | for (auto &profile_path : profile_list) { 482 | if (firefoxFindValue( 483 | QDir(profile_path).filePath("cookies.sqlite"))) { 484 | return true; 485 | } 486 | } 487 | return false; 488 | } 489 | 490 | QStringList Nicookie::firefoxGetProfileList(const QString &profile_ini) 491 | { 492 | QStringList list; 493 | QFile profile_file(profile_ini); 494 | if (!profile_file.exists()) { 495 | setError(Nicookie::FailedParseProfileError); 496 | return list; 497 | } 498 | 499 | QTemporaryFile profile_temp; 500 | if (!profile_temp.open()) { 501 | setError(Nicookie::FailedParseProfileError); 502 | return list; 503 | } 504 | if (!profile_file.open(QIODevice::ReadOnly)) { 505 | setError(Nicookie::FailedParseProfileError); 506 | profile_temp.close(); 507 | return list; 508 | } 509 | profile_temp.write(profile_file.readAll()); 510 | profile_file.close(); 511 | profile_temp.close(); 512 | 513 | QSettings profile_settings( 514 | profile_temp.fileName(), QSettings::IniFormat); 515 | if (profile_settings.status() != QSettings::NoError) { 516 | setError(Nicookie::FailedParseProfileError); 517 | return list; 518 | } 519 | 520 | for (auto &group : profile_settings.childGroups()) { 521 | if (group.startsWith("Profile")) { 522 | profile_settings.beginGroup(group); 523 | QString path; 524 | if (profile_settings.value("IsRelative", 1).toInt() == 525 | 1) { 526 | path = 527 | QFileInfo(profile_file) 528 | .dir() 529 | .filePath(profile_settings.value("Path") 530 | .toString()); 531 | } else { 532 | path = 533 | profile_settings.value("Path").toString(); 534 | } 535 | if (profile_settings.value("Default", 0).toInt() == 1) { 536 | list.prepend(path); 537 | } else { 538 | list.append(path); 539 | } 540 | profile_settings.endGroup(); 541 | } 542 | } 543 | 544 | if (list.isEmpty()) { 545 | setError(Nicookie::FailedParseProfileError); 546 | } 547 | return list; 548 | } 549 | 550 | bool Nicookie::firefoxFindValue(const QString &cookies_path) 551 | { 552 | QString query = 553 | "SELECT value FROM moz_cookies WHERE " 554 | "host = :host AND " 555 | "name = :name AND " 556 | "path = :path;"; 557 | QMap placeholders; 558 | placeholders[":host"] = Nicookie::COOKIE_HOST; 559 | placeholders[":name"] = Nicookie::COOKIE_NAME; 560 | placeholders[":path"] = Nicookie::COOKIE_PATH; 561 | QMap values; 562 | values["value"] = QVariant(); 563 | 564 | if (!querySqlite3(cookies_path, query, placeholders, values)) { 565 | return false; 566 | } 567 | 568 | this->userSession = values["value"].toString(); 569 | return true; 570 | } 571 | 572 | bool Nicookie::findChrome() 573 | { 574 | QString cookies_path; 575 | #if defined(Q_OS_WIN) 576 | cookies_path += 577 | QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA"); 578 | cookies_path += "\\Google\\Chrome\\User 579 | Data\\Default\\Cookies "; 580 | #elif defined(Q_OS_OSX) 581 | cookies_path += QProcessEnvironment::systemEnvironment().value("HOME"); 582 | cookies_path += "/Library/Application 583 | Support / 584 | Google / Chrome / Default / 585 | Cookies "; 586 | #else 587 | cookies_path += QProcessEnvironment::systemEnvironment().value("HOME"); 588 | cookies_path += "/.config/chromium/Default/Cookies"; 589 | #endif 590 | return chromeFindValue(cookies_path); 591 | } 592 | 593 | bool Nicookie::chromeFindValue(const QString &cookies_path) 594 | { 595 | QString query = 596 | "SELECT value,encrypted_value FROM cookies WHERE " 597 | "host_key = :host AND " 598 | "name = :name AND " 599 | "path = :path;"; 600 | QMap placeholders; 601 | placeholders[":host"] = Nicookie::COOKIE_HOST; 602 | placeholders[":name"] = Nicookie::COOKIE_NAME; 603 | placeholders[":path"] = Nicookie::COOKIE_PATH; 604 | QMap values; 605 | values["value"] = QVariant(); 606 | values["encrypted_value"] = QVariant(); 607 | 608 | if (!querySqlite3(cookies_path, query, placeholders, values)) { 609 | return false; 610 | } 611 | 612 | this->userSession = values["value"].toString(); 613 | if (this->userSession.isEmpty()) { 614 | this->userSession = 615 | chromeDecrypt(values["encrypted_value"].toByteArray()); 616 | } 617 | return !this->userSession.isEmpty(); 618 | } 619 | 620 | QString Nicookie::chromeDecrypt(const QByteArray &encrypt_data) 621 | { 622 | QString data; 623 | #if defined(Q_OS_WIN) 624 | DATA_BLOB encrypt_data_blob; 625 | encrypt_data_blob.pbData = (BYTE *)(encrypt_data.data()); 626 | encrypt_data_blob.cbData = static_cast(encrypt_data.size()); 627 | DATA_BLOB plain_data_blob; 628 | BOOL result = CryptUnprotectData( 629 | &encrypt_data_blob, NULL, NULL, NULL, NULL, 0, &plain_data_blob); 630 | if (!result) { 631 | setError(Nicookie::FailedDecrytError); 632 | return QString(); 633 | } 634 | data = (QByteArray( 635 | (char *)(plain_data_blob.pbData), plain_data_blob.cbData)); 636 | LocalFree(plain_data_blob.pbData); 637 | #elif defined(Q_OS_OSX) 638 | // 639 | https 640 | : // developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/index.html#//apple_ref/c/func/SecKeychainFindGenericPassword 641 | UInt32 password_size = 0; 642 | void *password = NULL; 643 | OSStatus os_status; 644 | os_status = SecKeychainFindGenericPassword(NULL, 19, 645 | "Chrome 646 | Safe Storage 647 | ", 6, 648 | "Chrome", 649 | &password_size, &password, NULL); 650 | if (password_size == 0) { 651 | setError(Nicookie::FailedDecrytError); 652 | SecKeychainItemFreeContent(NULL, password); 653 | return data; 654 | } 655 | 656 | const size_t enc_key_size = 16; 657 | unsigned char enc_key[enc_key_size]; 658 | int iterations = 1003; 659 | const char *salt = "saltysalt"; 660 | int pbkdf2_r = CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)password, 661 | password_size, (const uint8_t *)salt, strlen(salt), 662 | kCCPRFHmacAlgSHA1, iterations, (uint8_t *)enc_key, enc_key_size); 663 | if (pbkdf2_r != kCCSuccess) { 664 | setError(Nicookie::FailedDecrytError); 665 | SecKeychainItemFreeContent(NULL, password); 666 | return data; 667 | } 668 | 669 | const size_t iv_size = 16; 670 | unsigned char iv[iv_size]; 671 | for (size_t i = 0; i < iv_size; i++) iv[i] = ' '; 672 | 673 | // alwayes enc size >= dec size 674 | size_t plain_value_size = encrypt_data.size(); 675 | char *plain_value = (char *)malloc(plain_value_size); 676 | if (plain_value == NULL) { 677 | setError(Nicookie::FailedDecrytError); 678 | SecKeychainItemFreeContent(NULL, password); 679 | return data; 680 | } 681 | size_t palin_value_moved = 0; 682 | 683 | CCCryptorStatus cryptStatus = 684 | CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, enc_key, enc_key_size, 685 | iv, encrypt_data.data() + 3, encrypt_data.size() - 3, 686 | plain_value, plain_value_size - 1, &palin_value_moved); 687 | if (cryptStatus != kCCSuccess) { 688 | setError(Nicookie::FailedDecrytError); 689 | SecKeychainItemFreeContent(NULL, password); 690 | free(plain_value); 691 | return data; 692 | } 693 | plain_value[palin_value_moved] = '\0'; 694 | // TODO: 695 | なぜか末尾に '\x0B'がついてくるので含めなくする。 for ( 696 | size_t i = 0; i < palin_value_moved; ++i) 697 | { 698 | if (plain_value[i] == '\x0B') { 699 | plain_value[i] = '\0'; 700 | break; 701 | } 702 | } 703 | data = plain_value; 704 | 705 | free(plain_value); 706 | SecKeychainItemFreeContent(NULL, password); 707 | #else 708 | int password_size = 7; 709 | void *password = (void *)"peanuts"; 710 | const int enc_key_size = 16; 711 | unsigned char enc_key[enc_key_size]; 712 | int iterations = 1; 713 | const char *salt = "saltysalt"; 714 | int pbkdf2_r = PKCS5_PBKDF2_HMAC_SHA1((char *)password, password_size, 715 | (unsigned char *)salt, strlen(salt), iterations, enc_key_size, 716 | enc_key); 717 | if (!pbkdf2_r) { 718 | setError(Nicookie::FailedDecrytError); 719 | return data; 720 | } 721 | 722 | const int iv_size = 16; 723 | unsigned char iv[iv_size]; 724 | for (int i = 0; i < iv_size; i++) iv[i] = ' '; 725 | 726 | // alwayes enc size >= dec size 727 | int plain_value_size = encrypt_data.size(); 728 | char *plain_value = (char *)malloc(plain_value_size); 729 | if (plain_value == NULL) { 730 | setError(Nicookie::FailedDecrytError); 731 | return data; 732 | } 733 | 734 | int result = 1; 735 | EVP_CIPHER_CTX ctx; 736 | EVP_CIPHER_CTX_init(&ctx); 737 | 738 | result = EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, enc_key, iv); 739 | if (!result) { 740 | setError(Nicookie::FailedDecrytError); 741 | EVP_CIPHER_CTX_cleanup(&ctx); 742 | free(plain_value); 743 | return data; 744 | } 745 | 746 | result = EVP_DecryptUpdate(&ctx, (unsigned char *)plain_value, 747 | &plain_value_size, (unsigned char *)(encrypt_data.data() + 3), 748 | encrypt_data.size() - 3); 749 | if (!result) { 750 | setError(Nicookie::FailedDecrytError); 751 | EVP_CIPHER_CTX_cleanup(&ctx); 752 | free(plain_value); 753 | return data; 754 | } 755 | 756 | int fin_size = 0; 757 | result = EVP_DecryptFinal_ex( 758 | &ctx, (unsigned char *)(plain_value + plain_value_size), &fin_size); 759 | if (!result) { 760 | setError(Nicookie::FailedDecrytError); 761 | EVP_CIPHER_CTX_cleanup(&ctx); 762 | free(plain_value); 763 | return data; 764 | } 765 | 766 | EVP_CIPHER_CTX_cleanup(&ctx); 767 | 768 | plain_value[plain_value_size + fin_size] = '\0'; 769 | data = plain_value; 770 | 771 | free(plain_value); 772 | #endif 773 | return data; 774 | } 775 | 776 | bool Nicookie::findOpera() 777 | { 778 | setError(Nicookie::NotImplementError); 779 | return false; 780 | } 781 | 782 | void Nicookie::setError(Error num) { this->errorNum = num; } 783 | 784 | bool Nicookie::querySqlite3(const QString &sqlite3_file, const QString &query, 785 | const QMap &placeholders, 786 | QMap &values) 787 | { 788 | bool result = false; 789 | do { 790 | QSqlDatabase db = 791 | QSqlDatabase::addDatabase("QSQLITE", "nicookie_sqlite3"); 792 | if (!db.isValid()) { 793 | setError(Nicookie::SQLiteError); 794 | break; 795 | } 796 | 797 | db.setDatabaseName(sqlite3_file); 798 | if (!db.open()) { 799 | setError(Nicookie::SQLiteError); 800 | db.close(); 801 | break; 802 | } 803 | 804 | QSqlQuery sql_query(db); 805 | if (!sql_query.prepare(query)) { 806 | setError(Nicookie::SQLiteError); 807 | db.close(); 808 | break; 809 | } 810 | 811 | for (auto &name : placeholders.keys()) { 812 | sql_query.bindValue(name, placeholders[name]); 813 | } 814 | if (!sql_query.exec()) { 815 | setError(Nicookie::SQLiteError); 816 | sql_query.finish(); 817 | db.close(); 818 | break; 819 | } 820 | 821 | if (!sql_query.first()) { 822 | setError(Nicookie::NotFoundDataError); 823 | sql_query.finish(); 824 | db.close(); 825 | break; 826 | } 827 | 828 | for (auto &name : values.keys()) { 829 | values[name] = sql_query.value(name); 830 | } 831 | sql_query.finish(); 832 | db.close(); 833 | result = true; 834 | } while (false); 835 | QSqlDatabase::removeDatabase("nicookie_sqlite3"); 836 | return result; 837 | } 838 | 839 | #ifdef Q_OS_OSX 840 | quint32 Nicookie::readUint32BE(QIODevice &device) 841 | { 842 | QDataStream stream(device.read(4)); 843 | stream.setByteOrder(QDataStream::BigEndian); 844 | quint32 i; 845 | stream >> i; 846 | return i; 847 | } 848 | 849 | quint32 Nicookie::readUint32LE(QIODevice &device) 850 | { 851 | QDataStream stream(device.read(4)); 852 | stream.setByteOrder(QDataStream::LittleEndian); 853 | quint32 i; 854 | stream >> i; 855 | return i; 856 | } 857 | #endif // Q_OS_OSX 858 | 859 | bool Nicookie::checkSameStr(QIODevice &device, const QString &str) 860 | { 861 | char input_c; 862 | for (auto &c : str) { 863 | if (!device.getChar(&input_c)) { 864 | setError(Nicookie::FailedReadDataError); 865 | return false; 866 | } 867 | if (c != input_c) return false; 868 | } 869 | if (!device.getChar(&input_c)) { 870 | setError(Nicookie::FailedReadDataError); 871 | return false; 872 | } 873 | if (input_c != '\0') return false; 874 | return true; 875 | } 876 | 877 | QString Nicookie::readStr(QIODevice &device) 878 | { 879 | QString str; 880 | while (true) { 881 | char input_c; 882 | if (!device.getChar(&input_c)) { 883 | setError(Nicookie::FailedReadDataError); 884 | return QString(); 885 | } 886 | if (input_c == '\0') { 887 | break; 888 | } else { 889 | str.append(input_c); 890 | } 891 | } 892 | return str; 893 | } 894 | -------------------------------------------------------------------------------- /pugixml/pugiconfig.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.8 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at http://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef HEADER_PUGICONFIG_HPP 15 | #define HEADER_PUGICONFIG_HPP 16 | 17 | // Uncomment this to enable wchar_t mode 18 | // #define PUGIXML_WCHAR_MODE 19 | 20 | // Uncomment this to enable compact mode 21 | // #define PUGIXML_COMPACT 22 | 23 | // Uncomment this to disable XPath 24 | // #define PUGIXML_NO_XPATH 25 | 26 | // Uncomment this to disable STL 27 | // #define PUGIXML_NO_STL 28 | 29 | // Uncomment this to disable exceptions 30 | // #define PUGIXML_NO_EXCEPTIONS 31 | 32 | // Set this to control attributes for public classes/functions, i.e.: 33 | // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL 34 | // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL 35 | // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall 36 | // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead 37 | 38 | // Tune these constants to adjust memory-related behavior 39 | // #define PUGIXML_MEMORY_PAGE_SIZE 32768 40 | // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 41 | // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 42 | 43 | // Uncomment this to switch to header-only version 44 | // #define PUGIXML_HEADER_ONLY 45 | 46 | // Uncomment this to enable long long support 47 | // #define PUGIXML_HAS_LONG_LONG 48 | 49 | #endif 50 | 51 | /** 52 | * Copyright (c) 2006-2016 Arseny Kapoulkine 53 | * 54 | * Permission is hereby granted, free of charge, to any person 55 | * obtaining a copy of this software and associated documentation 56 | * files (the "Software"), to deal in the Software without 57 | * restriction, including without limitation the rights to use, 58 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 59 | * copies of the Software, and to permit persons to whom the 60 | * Software is furnished to do so, subject to the following 61 | * conditions: 62 | * 63 | * The above copyright notice and this permission notice shall be 64 | * included in all copies or substantial portions of the Software. 65 | * 66 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 67 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 68 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 69 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 70 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 71 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 72 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 73 | * OTHER DEALINGS IN THE SOFTWARE. 74 | */ 75 | -------------------------------------------------------------------------------- /pugixml/pugixml.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.8 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2016, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at http://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef PUGIXML_VERSION 15 | // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons 16 | # define PUGIXML_VERSION 180 17 | #endif 18 | 19 | // Include user configuration file (this can define various configuration macros) 20 | #include "pugiconfig.hpp" 21 | 22 | #ifndef HEADER_PUGIXML_HPP 23 | #define HEADER_PUGIXML_HPP 24 | 25 | // Include stddef.h for size_t and ptrdiff_t 26 | #include 27 | 28 | // Include exception header for XPath 29 | #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) 30 | # include 31 | #endif 32 | 33 | // Include STL headers 34 | #ifndef PUGIXML_NO_STL 35 | # include 36 | # include 37 | # include 38 | #endif 39 | 40 | // Macro for deprecated features 41 | #ifndef PUGIXML_DEPRECATED 42 | # if defined(__GNUC__) 43 | # define PUGIXML_DEPRECATED __attribute__((deprecated)) 44 | # elif defined(_MSC_VER) && _MSC_VER >= 1300 45 | # define PUGIXML_DEPRECATED __declspec(deprecated) 46 | # else 47 | # define PUGIXML_DEPRECATED 48 | # endif 49 | #endif 50 | 51 | // If no API is defined, assume default 52 | #ifndef PUGIXML_API 53 | # define PUGIXML_API 54 | #endif 55 | 56 | // If no API for classes is defined, assume default 57 | #ifndef PUGIXML_CLASS 58 | # define PUGIXML_CLASS PUGIXML_API 59 | #endif 60 | 61 | // If no API for functions is defined, assume default 62 | #ifndef PUGIXML_FUNCTION 63 | # define PUGIXML_FUNCTION PUGIXML_API 64 | #endif 65 | 66 | // If the platform is known to have long long support, enable long long functions 67 | #ifndef PUGIXML_HAS_LONG_LONG 68 | # if __cplusplus >= 201103 69 | # define PUGIXML_HAS_LONG_LONG 70 | # elif defined(_MSC_VER) && _MSC_VER >= 1400 71 | # define PUGIXML_HAS_LONG_LONG 72 | # endif 73 | #endif 74 | 75 | // If the platform is known to have move semantics support, compile move ctor/operator implementation 76 | #ifndef PUGIXML_HAS_MOVE 77 | # if __cplusplus >= 201103 78 | # define PUGIXML_HAS_MOVE 79 | # elif defined(_MSC_VER) && _MSC_VER >= 1600 80 | # define PUGIXML_HAS_MOVE 81 | # endif 82 | #endif 83 | 84 | // If C++ is 2011 or higher, add 'override' qualifiers 85 | #ifndef PUGIXML_OVERRIDE 86 | # if __cplusplus >= 201103 87 | # define PUGIXML_OVERRIDE override 88 | # else 89 | # define PUGIXML_OVERRIDE 90 | # endif 91 | #endif 92 | 93 | // Character interface macros 94 | #ifdef PUGIXML_WCHAR_MODE 95 | # define PUGIXML_TEXT(t) L ## t 96 | # define PUGIXML_CHAR wchar_t 97 | #else 98 | # define PUGIXML_TEXT(t) t 99 | # define PUGIXML_CHAR char 100 | #endif 101 | 102 | namespace pugi 103 | { 104 | // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE 105 | typedef PUGIXML_CHAR char_t; 106 | 107 | #ifndef PUGIXML_NO_STL 108 | // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE 109 | typedef std::basic_string, std::allocator > string_t; 110 | #endif 111 | } 112 | 113 | // The PugiXML namespace 114 | namespace pugi 115 | { 116 | // Tree node types 117 | enum xml_node_type 118 | { 119 | node_null, // Empty (null) node handle 120 | node_document, // A document tree's absolute root 121 | node_element, // Element tag, i.e. '' 122 | node_pcdata, // Plain character data, i.e. 'text' 123 | node_cdata, // Character data, i.e. '' 124 | node_comment, // Comment tag, i.e. '' 125 | node_pi, // Processing instruction, i.e. '' 126 | node_declaration, // Document declaration, i.e. '' 127 | node_doctype // Document type declaration, i.e. '' 128 | }; 129 | 130 | // Parsing options 131 | 132 | // Minimal parsing mode (equivalent to turning all other flags off). 133 | // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. 134 | const unsigned int parse_minimal = 0x0000; 135 | 136 | // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. 137 | const unsigned int parse_pi = 0x0001; 138 | 139 | // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. 140 | const unsigned int parse_comments = 0x0002; 141 | 142 | // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. 143 | const unsigned int parse_cdata = 0x0004; 144 | 145 | // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. 146 | // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. 147 | const unsigned int parse_ws_pcdata = 0x0008; 148 | 149 | // This flag determines if character and entity references are expanded during parsing. This flag is on by default. 150 | const unsigned int parse_escapes = 0x0010; 151 | 152 | // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. 153 | const unsigned int parse_eol = 0x0020; 154 | 155 | // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. 156 | const unsigned int parse_wconv_attribute = 0x0040; 157 | 158 | // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. 159 | const unsigned int parse_wnorm_attribute = 0x0080; 160 | 161 | // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. 162 | const unsigned int parse_declaration = 0x0100; 163 | 164 | // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. 165 | const unsigned int parse_doctype = 0x0200; 166 | 167 | // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only 168 | // of whitespace is added to the DOM tree. 169 | // This flag is off by default; turning it on may result in slower parsing and more memory consumption. 170 | const unsigned int parse_ws_pcdata_single = 0x0400; 171 | 172 | // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. 173 | const unsigned int parse_trim_pcdata = 0x0800; 174 | 175 | // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document 176 | // is a valid document. This flag is off by default. 177 | const unsigned int parse_fragment = 0x1000; 178 | 179 | // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of 180 | // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. 181 | // This flag is off by default. 182 | const unsigned int parse_embed_pcdata = 0x2000; 183 | 184 | // The default parsing mode. 185 | // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, 186 | // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. 187 | const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; 188 | 189 | // The full parsing mode. 190 | // Nodes of all types are added to the DOM tree, character/reference entities are expanded, 191 | // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. 192 | const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; 193 | 194 | // These flags determine the encoding of input data for XML document 195 | enum xml_encoding 196 | { 197 | encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range 264 | { 265 | public: 266 | typedef It const_iterator; 267 | typedef It iterator; 268 | 269 | xml_object_range(It b, It e): _begin(b), _end(e) 270 | { 271 | } 272 | 273 | It begin() const { return _begin; } 274 | It end() const { return _end; } 275 | 276 | private: 277 | It _begin, _end; 278 | }; 279 | 280 | // Writer interface for node printing (see xml_node::print) 281 | class PUGIXML_CLASS xml_writer 282 | { 283 | public: 284 | virtual ~xml_writer() {} 285 | 286 | // Write memory chunk into stream/file/whatever 287 | virtual void write(const void* data, size_t size) = 0; 288 | }; 289 | 290 | // xml_writer implementation for FILE* 291 | class PUGIXML_CLASS xml_writer_file: public xml_writer 292 | { 293 | public: 294 | // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio 295 | xml_writer_file(void* file); 296 | 297 | virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; 298 | 299 | private: 300 | void* file; 301 | }; 302 | 303 | #ifndef PUGIXML_NO_STL 304 | // xml_writer implementation for streams 305 | class PUGIXML_CLASS xml_writer_stream: public xml_writer 306 | { 307 | public: 308 | // Construct writer from an output stream object 309 | xml_writer_stream(std::basic_ostream >& stream); 310 | xml_writer_stream(std::basic_ostream >& stream); 311 | 312 | virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; 313 | 314 | private: 315 | std::basic_ostream >* narrow_stream; 316 | std::basic_ostream >* wide_stream; 317 | }; 318 | #endif 319 | 320 | // A light-weight handle for manipulating attributes in DOM tree 321 | class PUGIXML_CLASS xml_attribute 322 | { 323 | friend class xml_attribute_iterator; 324 | friend class xml_node; 325 | 326 | private: 327 | xml_attribute_struct* _attr; 328 | 329 | typedef void (*unspecified_bool_type)(xml_attribute***); 330 | 331 | public: 332 | // Default constructor. Constructs an empty attribute. 333 | xml_attribute(); 334 | 335 | // Constructs attribute from internal pointer 336 | explicit xml_attribute(xml_attribute_struct* attr); 337 | 338 | // Safe bool conversion operator 339 | operator unspecified_bool_type() const; 340 | 341 | // Borland C++ workaround 342 | bool operator!() const; 343 | 344 | // Comparison operators (compares wrapped attribute pointers) 345 | bool operator==(const xml_attribute& r) const; 346 | bool operator!=(const xml_attribute& r) const; 347 | bool operator<(const xml_attribute& r) const; 348 | bool operator>(const xml_attribute& r) const; 349 | bool operator<=(const xml_attribute& r) const; 350 | bool operator>=(const xml_attribute& r) const; 351 | 352 | // Check if attribute is empty 353 | bool empty() const; 354 | 355 | // Get attribute name/value, or "" if attribute is empty 356 | const char_t* name() const; 357 | const char_t* value() const; 358 | 359 | // Get attribute value, or the default value if attribute is empty 360 | const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; 361 | 362 | // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty 363 | int as_int(int def = 0) const; 364 | unsigned int as_uint(unsigned int def = 0) const; 365 | double as_double(double def = 0) const; 366 | float as_float(float def = 0) const; 367 | 368 | #ifdef PUGIXML_HAS_LONG_LONG 369 | long long as_llong(long long def = 0) const; 370 | unsigned long long as_ullong(unsigned long long def = 0) const; 371 | #endif 372 | 373 | // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty 374 | bool as_bool(bool def = false) const; 375 | 376 | // Set attribute name/value (returns false if attribute is empty or there is not enough memory) 377 | bool set_name(const char_t* rhs); 378 | bool set_value(const char_t* rhs); 379 | 380 | // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") 381 | bool set_value(int rhs); 382 | bool set_value(unsigned int rhs); 383 | bool set_value(long rhs); 384 | bool set_value(unsigned long rhs); 385 | bool set_value(double rhs); 386 | bool set_value(float rhs); 387 | bool set_value(bool rhs); 388 | 389 | #ifdef PUGIXML_HAS_LONG_LONG 390 | bool set_value(long long rhs); 391 | bool set_value(unsigned long long rhs); 392 | #endif 393 | 394 | // Set attribute value (equivalent to set_value without error checking) 395 | xml_attribute& operator=(const char_t* rhs); 396 | xml_attribute& operator=(int rhs); 397 | xml_attribute& operator=(unsigned int rhs); 398 | xml_attribute& operator=(long rhs); 399 | xml_attribute& operator=(unsigned long rhs); 400 | xml_attribute& operator=(double rhs); 401 | xml_attribute& operator=(float rhs); 402 | xml_attribute& operator=(bool rhs); 403 | 404 | #ifdef PUGIXML_HAS_LONG_LONG 405 | xml_attribute& operator=(long long rhs); 406 | xml_attribute& operator=(unsigned long long rhs); 407 | #endif 408 | 409 | // Get next/previous attribute in the attribute list of the parent node 410 | xml_attribute next_attribute() const; 411 | xml_attribute previous_attribute() const; 412 | 413 | // Get hash value (unique for handles to the same object) 414 | size_t hash_value() const; 415 | 416 | // Get internal pointer 417 | xml_attribute_struct* internal_object() const; 418 | }; 419 | 420 | #ifdef __BORLANDC__ 421 | // Borland C++ workaround 422 | bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); 423 | bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); 424 | #endif 425 | 426 | // A light-weight handle for manipulating nodes in DOM tree 427 | class PUGIXML_CLASS xml_node 428 | { 429 | friend class xml_attribute_iterator; 430 | friend class xml_node_iterator; 431 | friend class xml_named_node_iterator; 432 | 433 | protected: 434 | xml_node_struct* _root; 435 | 436 | typedef void (*unspecified_bool_type)(xml_node***); 437 | 438 | public: 439 | // Default constructor. Constructs an empty node. 440 | xml_node(); 441 | 442 | // Constructs node from internal pointer 443 | explicit xml_node(xml_node_struct* p); 444 | 445 | // Safe bool conversion operator 446 | operator unspecified_bool_type() const; 447 | 448 | // Borland C++ workaround 449 | bool operator!() const; 450 | 451 | // Comparison operators (compares wrapped node pointers) 452 | bool operator==(const xml_node& r) const; 453 | bool operator!=(const xml_node& r) const; 454 | bool operator<(const xml_node& r) const; 455 | bool operator>(const xml_node& r) const; 456 | bool operator<=(const xml_node& r) const; 457 | bool operator>=(const xml_node& r) const; 458 | 459 | // Check if node is empty. 460 | bool empty() const; 461 | 462 | // Get node type 463 | xml_node_type type() const; 464 | 465 | // Get node name, or "" if node is empty or it has no name 466 | const char_t* name() const; 467 | 468 | // Get node value, or "" if node is empty or it has no value 469 | // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. 470 | const char_t* value() const; 471 | 472 | // Get attribute list 473 | xml_attribute first_attribute() const; 474 | xml_attribute last_attribute() const; 475 | 476 | // Get children list 477 | xml_node first_child() const; 478 | xml_node last_child() const; 479 | 480 | // Get next/previous sibling in the children list of the parent node 481 | xml_node next_sibling() const; 482 | xml_node previous_sibling() const; 483 | 484 | // Get parent node 485 | xml_node parent() const; 486 | 487 | // Get root of DOM tree this node belongs to 488 | xml_node root() const; 489 | 490 | // Get text object for the current node 491 | xml_text text() const; 492 | 493 | // Get child, attribute or next/previous sibling with the specified name 494 | xml_node child(const char_t* name) const; 495 | xml_attribute attribute(const char_t* name) const; 496 | xml_node next_sibling(const char_t* name) const; 497 | xml_node previous_sibling(const char_t* name) const; 498 | 499 | // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) 500 | xml_attribute attribute(const char_t* name, xml_attribute& hint) const; 501 | 502 | // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA 503 | const char_t* child_value() const; 504 | 505 | // Get child value of child with specified name. Equivalent to child(name).child_value(). 506 | const char_t* child_value(const char_t* name) const; 507 | 508 | // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) 509 | bool set_name(const char_t* rhs); 510 | bool set_value(const char_t* rhs); 511 | 512 | // Add attribute with specified name. Returns added attribute, or empty attribute on errors. 513 | xml_attribute append_attribute(const char_t* name); 514 | xml_attribute prepend_attribute(const char_t* name); 515 | xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); 516 | xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); 517 | 518 | // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. 519 | xml_attribute append_copy(const xml_attribute& proto); 520 | xml_attribute prepend_copy(const xml_attribute& proto); 521 | xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); 522 | xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); 523 | 524 | // Add child node with specified type. Returns added node, or empty node on errors. 525 | xml_node append_child(xml_node_type type = node_element); 526 | xml_node prepend_child(xml_node_type type = node_element); 527 | xml_node insert_child_after(xml_node_type type, const xml_node& node); 528 | xml_node insert_child_before(xml_node_type type, const xml_node& node); 529 | 530 | // Add child element with specified name. Returns added node, or empty node on errors. 531 | xml_node append_child(const char_t* name); 532 | xml_node prepend_child(const char_t* name); 533 | xml_node insert_child_after(const char_t* name, const xml_node& node); 534 | xml_node insert_child_before(const char_t* name, const xml_node& node); 535 | 536 | // Add a copy of the specified node as a child. Returns added node, or empty node on errors. 537 | xml_node append_copy(const xml_node& proto); 538 | xml_node prepend_copy(const xml_node& proto); 539 | xml_node insert_copy_after(const xml_node& proto, const xml_node& node); 540 | xml_node insert_copy_before(const xml_node& proto, const xml_node& node); 541 | 542 | // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. 543 | xml_node append_move(const xml_node& moved); 544 | xml_node prepend_move(const xml_node& moved); 545 | xml_node insert_move_after(const xml_node& moved, const xml_node& node); 546 | xml_node insert_move_before(const xml_node& moved, const xml_node& node); 547 | 548 | // Remove specified attribute 549 | bool remove_attribute(const xml_attribute& a); 550 | bool remove_attribute(const char_t* name); 551 | 552 | // Remove specified child 553 | bool remove_child(const xml_node& n); 554 | bool remove_child(const char_t* name); 555 | 556 | // Parses buffer as an XML document fragment and appends all nodes as children of the current node. 557 | // Copies/converts the buffer, so it may be deleted or changed after the function returns. 558 | // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. 559 | xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 560 | 561 | // Find attribute using predicate. Returns first attribute for which predicate returned true. 562 | template xml_attribute find_attribute(Predicate pred) const 563 | { 564 | if (!_root) return xml_attribute(); 565 | 566 | for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) 567 | if (pred(attrib)) 568 | return attrib; 569 | 570 | return xml_attribute(); 571 | } 572 | 573 | // Find child node using predicate. Returns first child for which predicate returned true. 574 | template xml_node find_child(Predicate pred) const 575 | { 576 | if (!_root) return xml_node(); 577 | 578 | for (xml_node node = first_child(); node; node = node.next_sibling()) 579 | if (pred(node)) 580 | return node; 581 | 582 | return xml_node(); 583 | } 584 | 585 | // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. 586 | template xml_node find_node(Predicate pred) const 587 | { 588 | if (!_root) return xml_node(); 589 | 590 | xml_node cur = first_child(); 591 | 592 | while (cur._root && cur._root != _root) 593 | { 594 | if (pred(cur)) return cur; 595 | 596 | if (cur.first_child()) cur = cur.first_child(); 597 | else if (cur.next_sibling()) cur = cur.next_sibling(); 598 | else 599 | { 600 | while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); 601 | 602 | if (cur._root != _root) cur = cur.next_sibling(); 603 | } 604 | } 605 | 606 | return xml_node(); 607 | } 608 | 609 | // Find child node by attribute name/value 610 | xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; 611 | xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; 612 | 613 | #ifndef PUGIXML_NO_STL 614 | // Get the absolute node path from root as a text string. 615 | string_t path(char_t delimiter = '/') const; 616 | #endif 617 | 618 | // Search for a node by path consisting of node names and . or .. elements. 619 | xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; 620 | 621 | // Recursively traverse subtree with xml_tree_walker 622 | bool traverse(xml_tree_walker& walker); 623 | 624 | #ifndef PUGIXML_NO_XPATH 625 | // Select single node by evaluating XPath query. Returns first node from the resulting node set. 626 | xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; 627 | xpath_node select_node(const xpath_query& query) const; 628 | 629 | // Select node set by evaluating XPath query 630 | xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; 631 | xpath_node_set select_nodes(const xpath_query& query) const; 632 | 633 | // (deprecated: use select_node instead) Select single node by evaluating XPath query. 634 | xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; 635 | xpath_node select_single_node(const xpath_query& query) const; 636 | 637 | #endif 638 | 639 | // Print subtree using a writer object 640 | void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; 641 | 642 | #ifndef PUGIXML_NO_STL 643 | // Print subtree to stream 644 | void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; 645 | void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; 646 | #endif 647 | 648 | // Child nodes iterators 649 | typedef xml_node_iterator iterator; 650 | 651 | iterator begin() const; 652 | iterator end() const; 653 | 654 | // Attribute iterators 655 | typedef xml_attribute_iterator attribute_iterator; 656 | 657 | attribute_iterator attributes_begin() const; 658 | attribute_iterator attributes_end() const; 659 | 660 | // Range-based for support 661 | xml_object_range children() const; 662 | xml_object_range children(const char_t* name) const; 663 | xml_object_range attributes() const; 664 | 665 | // Get node offset in parsed file/string (in char_t units) for debugging purposes 666 | ptrdiff_t offset_debug() const; 667 | 668 | // Get hash value (unique for handles to the same object) 669 | size_t hash_value() const; 670 | 671 | // Get internal pointer 672 | xml_node_struct* internal_object() const; 673 | }; 674 | 675 | #ifdef __BORLANDC__ 676 | // Borland C++ workaround 677 | bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); 678 | bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); 679 | #endif 680 | 681 | // A helper for working with text inside PCDATA nodes 682 | class PUGIXML_CLASS xml_text 683 | { 684 | friend class xml_node; 685 | 686 | xml_node_struct* _root; 687 | 688 | typedef void (*unspecified_bool_type)(xml_text***); 689 | 690 | explicit xml_text(xml_node_struct* root); 691 | 692 | xml_node_struct* _data_new(); 693 | xml_node_struct* _data() const; 694 | 695 | public: 696 | // Default constructor. Constructs an empty object. 697 | xml_text(); 698 | 699 | // Safe bool conversion operator 700 | operator unspecified_bool_type() const; 701 | 702 | // Borland C++ workaround 703 | bool operator!() const; 704 | 705 | // Check if text object is empty 706 | bool empty() const; 707 | 708 | // Get text, or "" if object is empty 709 | const char_t* get() const; 710 | 711 | // Get text, or the default value if object is empty 712 | const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; 713 | 714 | // Get text as a number, or the default value if conversion did not succeed or object is empty 715 | int as_int(int def = 0) const; 716 | unsigned int as_uint(unsigned int def = 0) const; 717 | double as_double(double def = 0) const; 718 | float as_float(float def = 0) const; 719 | 720 | #ifdef PUGIXML_HAS_LONG_LONG 721 | long long as_llong(long long def = 0) const; 722 | unsigned long long as_ullong(unsigned long long def = 0) const; 723 | #endif 724 | 725 | // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty 726 | bool as_bool(bool def = false) const; 727 | 728 | // Set text (returns false if object is empty or there is not enough memory) 729 | bool set(const char_t* rhs); 730 | 731 | // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") 732 | bool set(int rhs); 733 | bool set(unsigned int rhs); 734 | bool set(long rhs); 735 | bool set(unsigned long rhs); 736 | bool set(double rhs); 737 | bool set(float rhs); 738 | bool set(bool rhs); 739 | 740 | #ifdef PUGIXML_HAS_LONG_LONG 741 | bool set(long long rhs); 742 | bool set(unsigned long long rhs); 743 | #endif 744 | 745 | // Set text (equivalent to set without error checking) 746 | xml_text& operator=(const char_t* rhs); 747 | xml_text& operator=(int rhs); 748 | xml_text& operator=(unsigned int rhs); 749 | xml_text& operator=(long rhs); 750 | xml_text& operator=(unsigned long rhs); 751 | xml_text& operator=(double rhs); 752 | xml_text& operator=(float rhs); 753 | xml_text& operator=(bool rhs); 754 | 755 | #ifdef PUGIXML_HAS_LONG_LONG 756 | xml_text& operator=(long long rhs); 757 | xml_text& operator=(unsigned long long rhs); 758 | #endif 759 | 760 | // Get the data node (node_pcdata or node_cdata) for this object 761 | xml_node data() const; 762 | }; 763 | 764 | #ifdef __BORLANDC__ 765 | // Borland C++ workaround 766 | bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); 767 | bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); 768 | #endif 769 | 770 | // Child node iterator (a bidirectional iterator over a collection of xml_node) 771 | class PUGIXML_CLASS xml_node_iterator 772 | { 773 | friend class xml_node; 774 | 775 | private: 776 | mutable xml_node _wrap; 777 | xml_node _parent; 778 | 779 | xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); 780 | 781 | public: 782 | // Iterator traits 783 | typedef ptrdiff_t difference_type; 784 | typedef xml_node value_type; 785 | typedef xml_node* pointer; 786 | typedef xml_node& reference; 787 | 788 | #ifndef PUGIXML_NO_STL 789 | typedef std::bidirectional_iterator_tag iterator_category; 790 | #endif 791 | 792 | // Default constructor 793 | xml_node_iterator(); 794 | 795 | // Construct an iterator which points to the specified node 796 | xml_node_iterator(const xml_node& node); 797 | 798 | // Iterator operators 799 | bool operator==(const xml_node_iterator& rhs) const; 800 | bool operator!=(const xml_node_iterator& rhs) const; 801 | 802 | xml_node& operator*() const; 803 | xml_node* operator->() const; 804 | 805 | const xml_node_iterator& operator++(); 806 | xml_node_iterator operator++(int); 807 | 808 | const xml_node_iterator& operator--(); 809 | xml_node_iterator operator--(int); 810 | }; 811 | 812 | // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) 813 | class PUGIXML_CLASS xml_attribute_iterator 814 | { 815 | friend class xml_node; 816 | 817 | private: 818 | mutable xml_attribute _wrap; 819 | xml_node _parent; 820 | 821 | xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); 822 | 823 | public: 824 | // Iterator traits 825 | typedef ptrdiff_t difference_type; 826 | typedef xml_attribute value_type; 827 | typedef xml_attribute* pointer; 828 | typedef xml_attribute& reference; 829 | 830 | #ifndef PUGIXML_NO_STL 831 | typedef std::bidirectional_iterator_tag iterator_category; 832 | #endif 833 | 834 | // Default constructor 835 | xml_attribute_iterator(); 836 | 837 | // Construct an iterator which points to the specified attribute 838 | xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); 839 | 840 | // Iterator operators 841 | bool operator==(const xml_attribute_iterator& rhs) const; 842 | bool operator!=(const xml_attribute_iterator& rhs) const; 843 | 844 | xml_attribute& operator*() const; 845 | xml_attribute* operator->() const; 846 | 847 | const xml_attribute_iterator& operator++(); 848 | xml_attribute_iterator operator++(int); 849 | 850 | const xml_attribute_iterator& operator--(); 851 | xml_attribute_iterator operator--(int); 852 | }; 853 | 854 | // Named node range helper 855 | class PUGIXML_CLASS xml_named_node_iterator 856 | { 857 | friend class xml_node; 858 | 859 | public: 860 | // Iterator traits 861 | typedef ptrdiff_t difference_type; 862 | typedef xml_node value_type; 863 | typedef xml_node* pointer; 864 | typedef xml_node& reference; 865 | 866 | #ifndef PUGIXML_NO_STL 867 | typedef std::bidirectional_iterator_tag iterator_category; 868 | #endif 869 | 870 | // Default constructor 871 | xml_named_node_iterator(); 872 | 873 | // Construct an iterator which points to the specified node 874 | xml_named_node_iterator(const xml_node& node, const char_t* name); 875 | 876 | // Iterator operators 877 | bool operator==(const xml_named_node_iterator& rhs) const; 878 | bool operator!=(const xml_named_node_iterator& rhs) const; 879 | 880 | xml_node& operator*() const; 881 | xml_node* operator->() const; 882 | 883 | const xml_named_node_iterator& operator++(); 884 | xml_named_node_iterator operator++(int); 885 | 886 | const xml_named_node_iterator& operator--(); 887 | xml_named_node_iterator operator--(int); 888 | 889 | private: 890 | mutable xml_node _wrap; 891 | xml_node _parent; 892 | const char_t* _name; 893 | 894 | xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); 895 | }; 896 | 897 | // Abstract tree walker class (see xml_node::traverse) 898 | class PUGIXML_CLASS xml_tree_walker 899 | { 900 | friend class xml_node; 901 | 902 | private: 903 | int _depth; 904 | 905 | protected: 906 | // Get current traversal depth 907 | int depth() const; 908 | 909 | public: 910 | xml_tree_walker(); 911 | virtual ~xml_tree_walker(); 912 | 913 | // Callback that is called when traversal begins 914 | virtual bool begin(xml_node& node); 915 | 916 | // Callback that is called for each node traversed 917 | virtual bool for_each(xml_node& node) = 0; 918 | 919 | // Callback that is called when traversal ends 920 | virtual bool end(xml_node& node); 921 | }; 922 | 923 | // Parsing status, returned as part of xml_parse_result object 924 | enum xml_parse_status 925 | { 926 | status_ok = 0, // No error 927 | 928 | status_file_not_found, // File was not found during load_file() 929 | status_io_error, // Error reading from file/stream 930 | status_out_of_memory, // Could not allocate memory 931 | status_internal_error, // Internal error occurred 932 | 933 | status_unrecognized_tag, // Parser could not determine tag type 934 | 935 | status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction 936 | status_bad_comment, // Parsing error occurred while parsing comment 937 | status_bad_cdata, // Parsing error occurred while parsing CDATA section 938 | status_bad_doctype, // Parsing error occurred while parsing document type declaration 939 | status_bad_pcdata, // Parsing error occurred while parsing PCDATA section 940 | status_bad_start_element, // Parsing error occurred while parsing start element tag 941 | status_bad_attribute, // Parsing error occurred while parsing element attribute 942 | status_bad_end_element, // Parsing error occurred while parsing end element tag 943 | status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) 944 | 945 | status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) 946 | 947 | status_no_document_element // Parsing resulted in a document without element nodes 948 | }; 949 | 950 | // Parsing result 951 | struct PUGIXML_CLASS xml_parse_result 952 | { 953 | // Parsing status (see xml_parse_status) 954 | xml_parse_status status; 955 | 956 | // Last parsed offset (in char_t units from start of input data) 957 | ptrdiff_t offset; 958 | 959 | // Source document encoding 960 | xml_encoding encoding; 961 | 962 | // Default constructor, initializes object to failed state 963 | xml_parse_result(); 964 | 965 | // Cast to bool operator 966 | operator bool() const; 967 | 968 | // Get error description 969 | const char* description() const; 970 | }; 971 | 972 | // Document class (DOM tree root) 973 | class PUGIXML_CLASS xml_document: public xml_node 974 | { 975 | private: 976 | char_t* _buffer; 977 | 978 | char _memory[192]; 979 | 980 | // Non-copyable semantics 981 | xml_document(const xml_document&); 982 | xml_document& operator=(const xml_document&); 983 | 984 | void _create(); 985 | void _destroy(); 986 | 987 | public: 988 | // Default constructor, makes empty document 989 | xml_document(); 990 | 991 | // Destructor, invalidates all node/attribute handles to this document 992 | ~xml_document(); 993 | 994 | // Removes all nodes, leaving the empty document 995 | void reset(); 996 | 997 | // Removes all nodes, then copies the entire contents of the specified document 998 | void reset(const xml_document& proto); 999 | 1000 | #ifndef PUGIXML_NO_STL 1001 | // Load document from stream. 1002 | xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1003 | xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); 1004 | #endif 1005 | 1006 | // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. 1007 | xml_parse_result load(const char_t* contents, unsigned int options = parse_default); 1008 | 1009 | // Load document from zero-terminated string. No encoding conversions are applied. 1010 | xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); 1011 | 1012 | // Load document from file 1013 | xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1014 | xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1015 | 1016 | // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. 1017 | xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1018 | 1019 | // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). 1020 | // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. 1021 | xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1022 | 1023 | // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). 1024 | // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). 1025 | xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 1026 | 1027 | // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). 1028 | void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 1029 | 1030 | #ifndef PUGIXML_NO_STL 1031 | // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). 1032 | void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 1033 | void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; 1034 | #endif 1035 | 1036 | // Save XML to file 1037 | bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 1038 | bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 1039 | 1040 | // Get document element 1041 | xml_node document_element() const; 1042 | }; 1043 | 1044 | #ifndef PUGIXML_NO_XPATH 1045 | // XPath query return type 1046 | enum xpath_value_type 1047 | { 1048 | xpath_type_none, // Unknown type (query failed to compile) 1049 | xpath_type_node_set, // Node set (xpath_node_set) 1050 | xpath_type_number, // Number 1051 | xpath_type_string, // String 1052 | xpath_type_boolean // Boolean 1053 | }; 1054 | 1055 | // XPath parsing result 1056 | struct PUGIXML_CLASS xpath_parse_result 1057 | { 1058 | // Error message (0 if no error) 1059 | const char* error; 1060 | 1061 | // Last parsed offset (in char_t units from string start) 1062 | ptrdiff_t offset; 1063 | 1064 | // Default constructor, initializes object to failed state 1065 | xpath_parse_result(); 1066 | 1067 | // Cast to bool operator 1068 | operator bool() const; 1069 | 1070 | // Get error description 1071 | const char* description() const; 1072 | }; 1073 | 1074 | // A single XPath variable 1075 | class PUGIXML_CLASS xpath_variable 1076 | { 1077 | friend class xpath_variable_set; 1078 | 1079 | protected: 1080 | xpath_value_type _type; 1081 | xpath_variable* _next; 1082 | 1083 | xpath_variable(xpath_value_type type); 1084 | 1085 | // Non-copyable semantics 1086 | xpath_variable(const xpath_variable&); 1087 | xpath_variable& operator=(const xpath_variable&); 1088 | 1089 | public: 1090 | // Get variable name 1091 | const char_t* name() const; 1092 | 1093 | // Get variable type 1094 | xpath_value_type type() const; 1095 | 1096 | // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error 1097 | bool get_boolean() const; 1098 | double get_number() const; 1099 | const char_t* get_string() const; 1100 | const xpath_node_set& get_node_set() const; 1101 | 1102 | // Set variable value; no type conversion is performed, false is returned on type mismatch error 1103 | bool set(bool value); 1104 | bool set(double value); 1105 | bool set(const char_t* value); 1106 | bool set(const xpath_node_set& value); 1107 | }; 1108 | 1109 | // A set of XPath variables 1110 | class PUGIXML_CLASS xpath_variable_set 1111 | { 1112 | private: 1113 | xpath_variable* _data[64]; 1114 | 1115 | void _assign(const xpath_variable_set& rhs); 1116 | void _swap(xpath_variable_set& rhs); 1117 | 1118 | xpath_variable* _find(const char_t* name) const; 1119 | 1120 | static bool _clone(xpath_variable* var, xpath_variable** out_result); 1121 | static void _destroy(xpath_variable* var); 1122 | 1123 | public: 1124 | // Default constructor/destructor 1125 | xpath_variable_set(); 1126 | ~xpath_variable_set(); 1127 | 1128 | // Copy constructor/assignment operator 1129 | xpath_variable_set(const xpath_variable_set& rhs); 1130 | xpath_variable_set& operator=(const xpath_variable_set& rhs); 1131 | 1132 | #ifdef PUGIXML_HAS_MOVE 1133 | // Move semantics support 1134 | xpath_variable_set(xpath_variable_set&& rhs); 1135 | xpath_variable_set& operator=(xpath_variable_set&& rhs); 1136 | #endif 1137 | 1138 | // Add a new variable or get the existing one, if the types match 1139 | xpath_variable* add(const char_t* name, xpath_value_type type); 1140 | 1141 | // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch 1142 | bool set(const char_t* name, bool value); 1143 | bool set(const char_t* name, double value); 1144 | bool set(const char_t* name, const char_t* value); 1145 | bool set(const char_t* name, const xpath_node_set& value); 1146 | 1147 | // Get existing variable by name 1148 | xpath_variable* get(const char_t* name); 1149 | const xpath_variable* get(const char_t* name) const; 1150 | }; 1151 | 1152 | // A compiled XPath query object 1153 | class PUGIXML_CLASS xpath_query 1154 | { 1155 | private: 1156 | void* _impl; 1157 | xpath_parse_result _result; 1158 | 1159 | typedef void (*unspecified_bool_type)(xpath_query***); 1160 | 1161 | // Non-copyable semantics 1162 | xpath_query(const xpath_query&); 1163 | xpath_query& operator=(const xpath_query&); 1164 | 1165 | public: 1166 | // Construct a compiled object from XPath expression. 1167 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. 1168 | explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); 1169 | 1170 | // Constructor 1171 | xpath_query(); 1172 | 1173 | // Destructor 1174 | ~xpath_query(); 1175 | 1176 | #ifdef PUGIXML_HAS_MOVE 1177 | // Move semantics support 1178 | xpath_query(xpath_query&& rhs); 1179 | xpath_query& operator=(xpath_query&& rhs); 1180 | #endif 1181 | 1182 | // Get query expression return type 1183 | xpath_value_type return_type() const; 1184 | 1185 | // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. 1186 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1187 | bool evaluate_boolean(const xpath_node& n) const; 1188 | 1189 | // Evaluate expression as double value in the specified context; performs type conversion if necessary. 1190 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1191 | double evaluate_number(const xpath_node& n) const; 1192 | 1193 | #ifndef PUGIXML_NO_STL 1194 | // Evaluate expression as string value in the specified context; performs type conversion if necessary. 1195 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1196 | string_t evaluate_string(const xpath_node& n) const; 1197 | #endif 1198 | 1199 | // Evaluate expression as string value in the specified context; performs type conversion if necessary. 1200 | // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). 1201 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1202 | // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. 1203 | size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; 1204 | 1205 | // Evaluate expression as node set in the specified context. 1206 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. 1207 | // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. 1208 | xpath_node_set evaluate_node_set(const xpath_node& n) const; 1209 | 1210 | // Evaluate expression as node set in the specified context. 1211 | // Return first node in document order, or empty node if node set is empty. 1212 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. 1213 | // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. 1214 | xpath_node evaluate_node(const xpath_node& n) const; 1215 | 1216 | // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) 1217 | const xpath_parse_result& result() const; 1218 | 1219 | // Safe bool conversion operator 1220 | operator unspecified_bool_type() const; 1221 | 1222 | // Borland C++ workaround 1223 | bool operator!() const; 1224 | }; 1225 | 1226 | #ifndef PUGIXML_NO_EXCEPTIONS 1227 | // XPath exception class 1228 | class PUGIXML_CLASS xpath_exception: public std::exception 1229 | { 1230 | private: 1231 | xpath_parse_result _result; 1232 | 1233 | public: 1234 | // Construct exception from parse result 1235 | explicit xpath_exception(const xpath_parse_result& result); 1236 | 1237 | // Get error message 1238 | virtual const char* what() const throw() PUGIXML_OVERRIDE; 1239 | 1240 | // Get parse result 1241 | const xpath_parse_result& result() const; 1242 | }; 1243 | #endif 1244 | 1245 | // XPath node class (either xml_node or xml_attribute) 1246 | class PUGIXML_CLASS xpath_node 1247 | { 1248 | private: 1249 | xml_node _node; 1250 | xml_attribute _attribute; 1251 | 1252 | typedef void (*unspecified_bool_type)(xpath_node***); 1253 | 1254 | public: 1255 | // Default constructor; constructs empty XPath node 1256 | xpath_node(); 1257 | 1258 | // Construct XPath node from XML node/attribute 1259 | xpath_node(const xml_node& node); 1260 | xpath_node(const xml_attribute& attribute, const xml_node& parent); 1261 | 1262 | // Get node/attribute, if any 1263 | xml_node node() const; 1264 | xml_attribute attribute() const; 1265 | 1266 | // Get parent of contained node/attribute 1267 | xml_node parent() const; 1268 | 1269 | // Safe bool conversion operator 1270 | operator unspecified_bool_type() const; 1271 | 1272 | // Borland C++ workaround 1273 | bool operator!() const; 1274 | 1275 | // Comparison operators 1276 | bool operator==(const xpath_node& n) const; 1277 | bool operator!=(const xpath_node& n) const; 1278 | }; 1279 | 1280 | #ifdef __BORLANDC__ 1281 | // Borland C++ workaround 1282 | bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); 1283 | bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); 1284 | #endif 1285 | 1286 | // A fixed-size collection of XPath nodes 1287 | class PUGIXML_CLASS xpath_node_set 1288 | { 1289 | public: 1290 | // Collection type 1291 | enum type_t 1292 | { 1293 | type_unsorted, // Not ordered 1294 | type_sorted, // Sorted by document order (ascending) 1295 | type_sorted_reverse // Sorted by document order (descending) 1296 | }; 1297 | 1298 | // Constant iterator type 1299 | typedef const xpath_node* const_iterator; 1300 | 1301 | // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work 1302 | typedef const xpath_node* iterator; 1303 | 1304 | // Default constructor. Constructs empty set. 1305 | xpath_node_set(); 1306 | 1307 | // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful 1308 | xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); 1309 | 1310 | // Destructor 1311 | ~xpath_node_set(); 1312 | 1313 | // Copy constructor/assignment operator 1314 | xpath_node_set(const xpath_node_set& ns); 1315 | xpath_node_set& operator=(const xpath_node_set& ns); 1316 | 1317 | #ifdef PUGIXML_HAS_MOVE 1318 | // Move semantics support 1319 | xpath_node_set(xpath_node_set&& rhs); 1320 | xpath_node_set& operator=(xpath_node_set&& rhs); 1321 | #endif 1322 | 1323 | // Get collection type 1324 | type_t type() const; 1325 | 1326 | // Get collection size 1327 | size_t size() const; 1328 | 1329 | // Indexing operator 1330 | const xpath_node& operator[](size_t index) const; 1331 | 1332 | // Collection iterators 1333 | const_iterator begin() const; 1334 | const_iterator end() const; 1335 | 1336 | // Sort the collection in ascending/descending order by document order 1337 | void sort(bool reverse = false); 1338 | 1339 | // Get first node in the collection by document order 1340 | xpath_node first() const; 1341 | 1342 | // Check if collection is empty 1343 | bool empty() const; 1344 | 1345 | private: 1346 | type_t _type; 1347 | 1348 | xpath_node _storage; 1349 | 1350 | xpath_node* _begin; 1351 | xpath_node* _end; 1352 | 1353 | void _assign(const_iterator begin, const_iterator end, type_t type); 1354 | void _move(xpath_node_set& rhs); 1355 | }; 1356 | #endif 1357 | 1358 | #ifndef PUGIXML_NO_STL 1359 | // Convert wide string to UTF8 1360 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); 1361 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); 1362 | 1363 | // Convert UTF8 to wide string 1364 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); 1365 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); 1366 | #endif 1367 | 1368 | // Memory allocation function interface; returns pointer to allocated memory or NULL on failure 1369 | typedef void* (*allocation_function)(size_t size); 1370 | 1371 | // Memory deallocation function interface 1372 | typedef void (*deallocation_function)(void* ptr); 1373 | 1374 | // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. 1375 | void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); 1376 | 1377 | // Get current memory management functions 1378 | allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); 1379 | deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); 1380 | } 1381 | 1382 | #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) 1383 | namespace std 1384 | { 1385 | // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) 1386 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); 1387 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); 1388 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); 1389 | } 1390 | #endif 1391 | 1392 | #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) 1393 | namespace std 1394 | { 1395 | // Workarounds for (non-standard) iterator category detection 1396 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); 1397 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); 1398 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); 1399 | } 1400 | #endif 1401 | 1402 | #endif 1403 | 1404 | // Make sure implementation is included in header-only mode 1405 | // Use macro expansion in #include to work around QMake (QTBUG-11923) 1406 | #if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) 1407 | # define PUGIXML_SOURCE "pugixml.cpp" 1408 | # include PUGIXML_SOURCE 1409 | #endif 1410 | 1411 | /** 1412 | * Copyright (c) 2006-2016 Arseny Kapoulkine 1413 | * 1414 | * Permission is hereby granted, free of charge, to any person 1415 | * obtaining a copy of this software and associated documentation 1416 | * files (the "Software"), to deal in the Software without 1417 | * restriction, including without limitation the rights to use, 1418 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 1419 | * copies of the Software, and to permit persons to whom the 1420 | * Software is furnished to do so, subject to the following 1421 | * conditions: 1422 | * 1423 | * The above copyright notice and this permission notice shall be 1424 | * included in all copies or substantial portions of the Software. 1425 | * 1426 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1427 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 1428 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1429 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 1430 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 1431 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1432 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1433 | * OTHER DEALINGS IN THE SOFTWARE. 1434 | */ 1435 | -------------------------------------------------------------------------------- /rtmp-nicolive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "nicookie.h" 5 | #include "nicolive-api.h" 6 | #include "nicolive-errno.h" 7 | #include "nicolive-log.h" 8 | #include "nicolive.h" 9 | 10 | // use in set_data_nicolive for reset default settigs 11 | // #define reset_obs_data(type, settings, name) \ 12 | // obs_data_set_##type( \ 13 | // (settings), (name), obs_data_get_##type((settings), (name))) 14 | 15 | // enum rtmp_nicolive_login_type { 16 | // RTMP_NICOLIVE_LOGIN_MAIL, 17 | // RTMP_NICOLIVE_LOGIN_SESSION, 18 | // RTMP_NICOLIVE_LOGIN_APP, 19 | // }; 20 | 21 | enum rtmp_nicolive_session_method { 22 | RTMP_NICOLIVE_SESSION_LOGIN, 23 | RTMP_NICOLIVE_SESSION_APP, 24 | RTMP_NICOLIVE_SESSION_MANUAL, 25 | }; 26 | 27 | // use on callback for check button 28 | static obs_data_t *current_settings = NULL; 29 | // uso on callback for modified settings 30 | static obs_data_t *checked_settings = NULL; 31 | 32 | /* utilities */ 33 | 34 | inline static bool same_data_string( 35 | obs_data_t *a, obs_data_t *b, const char *name) 36 | { 37 | return strcmp(obs_data_get_string(a, name), 38 | obs_data_get_string(b, name)) == 0; 39 | } 40 | 41 | inline static bool same_data_int(obs_data_t *a, obs_data_t *b, const char *name) 42 | { 43 | return obs_data_get_int(a, name) == obs_data_get_int(b, name); 44 | } 45 | 46 | inline static bool same_checked_settings(obs_data_t *settings) 47 | { 48 | if (checked_settings == NULL) return false; 49 | if (!same_data_int(checked_settings, settings, "session_method")) 50 | return false; 51 | switch (obs_data_get_int(settings, "session_method")) { 52 | case RTMP_NICOLIVE_SESSION_LOGIN: 53 | return same_data_string(checked_settings, settings, "mail") && 54 | same_data_string(checked_settings, settings, "password"); 55 | case RTMP_NICOLIVE_SESSION_APP: 56 | return same_data_int(checked_settings, settings, "cookie_app"); 57 | case RTMP_NICOLIVE_SESSION_MANUAL: 58 | return same_data_string(checked_settings, settings, "session"); 59 | } 60 | return false; 61 | } 62 | 63 | /** get session and check session. 64 | * If a session is valid, then set session on settings and return true, 65 | * else clear session on settings and false. 66 | * @param settings read and write obs settings 67 | * @return boolean that the session is valid 68 | */ 69 | inline static bool check_settings(obs_data_t *settings) 70 | { 71 | // first check that the setting has checkd, if ok then return 72 | // true 73 | if (obs_data_get_bool(settings, "checked")) { 74 | // do not change 75 | return true; 76 | } 77 | 78 | nicolive_errno = 0; 79 | bool check_ok = false; 80 | const char *additional_message = NULL; 81 | const char *session = obs_data_get_string(settings, "session"); 82 | // second check that the session is valid, if ok then return 83 | // true 84 | check_ok = nicolive_api_check_session(session); 85 | 86 | // third get session and check session 87 | if (!check_ok) { 88 | switch (obs_data_get_int(settings, "session_method")) { 89 | case RTMP_NICOLIVE_SESSION_LOGIN: 90 | session = nicolive_api_get_session_login( 91 | obs_data_get_string(settings, "mail"), 92 | obs_data_get_string(settings, "password")); 93 | 94 | if (session == NULL) { 95 | nicolive_log_warn("failed to login"); 96 | } else { 97 | nicolive_log_info("succeded to login"); 98 | check_ok = nicolive_api_check_session(session); 99 | } 100 | break; 101 | case RTMP_NICOLIVE_SESSION_APP: 102 | session = nicolive_api_get_session_app( 103 | obs_data_get_int(settings, "cookie_app")); 104 | if (session == NULL) { 105 | nicolive_log_warn("failed to get session"); 106 | } else { 107 | nicolive_log_info("succeded to get session"); 108 | check_ok = nicolive_api_check_session(session); 109 | } 110 | break; 111 | case RTMP_NICOLIVE_SESSION_MANUAL: 112 | // already checkd, and faield, so do nothing 113 | break; 114 | default: 115 | nicolive_log_error("unknown session method"); 116 | additional_message = 117 | obs_module_text("UnknownSessionMethod"); 118 | check_ok = false; 119 | } 120 | } 121 | 122 | const char *result_message = NULL; 123 | if (check_ok) { 124 | nicolive_log_info("valid user session cookie"); 125 | obs_data_set_string(settings, "session", session); 126 | obs_data_set_bool(settings, "checked", true); 127 | if (checked_settings == NULL) 128 | checked_settings = obs_data_create(); 129 | obs_data_apply(checked_settings, settings); 130 | result_message = obs_module_text("Succeeded"); 131 | } else { 132 | nicolive_log_warn("invalid user session cookie"); 133 | obs_data_set_bool(settings, "checked", false); 134 | result_message = obs_module_text("Failed"); 135 | } 136 | 137 | if (nicolive_errno != 0 && additional_message == NULL) { 138 | additional_message = nicolive_strerror(nicolive_errno); 139 | } 140 | 141 | char *message = NULL; 142 | if (additional_message == NULL) { 143 | message = bstrdup(result_message); 144 | } else { 145 | message = bzalloc( 146 | strlen(result_message) + strlen(additional_message) + 2); 147 | strcat(message, result_message); 148 | strcat(message, " "); 149 | strcat(message, additional_message); 150 | } 151 | obs_data_set_string(settings, "check_message", message); 152 | bfree(message); 153 | 154 | return check_ok; 155 | } 156 | 157 | inline static void set_data_nicolive(void *data, obs_data_t *settings) 158 | { 159 | if (check_settings(settings)) { 160 | nicolive_set_settings( 161 | data, "", "", obs_data_get_string(settings, "session")); 162 | // OPTIMIZE: double check? 163 | // if (!nicolive_check_session(data)) { 164 | // nicolive_log_warn("invalid session"); 165 | // } 166 | } else { 167 | // reset session 168 | nicolive_set_settings(data, "", "", ""); 169 | } 170 | 171 | nicolive_set_enabled_adjust_bitrate( 172 | data, obs_data_get_bool(settings, "adjust_bitrate")); 173 | 174 | if (obs_data_get_bool(settings, "auto_start")) { 175 | nicolive_start_watching(data); 176 | } else { 177 | nicolive_stop_watching(data); 178 | } 179 | } 180 | 181 | inline static bool adjust_bitrate(long long bitrate, 182 | obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings) 183 | { 184 | if (bitrate <= 0) { 185 | nicolive_log_warn("total bitrate is zero or negative"); 186 | return false; 187 | } 188 | 189 | long long video_bitrate = 190 | obs_data_get_int(video_encoder_settings, "bitrate"); 191 | long long audio_bitrate = 192 | obs_data_get_int(audio_encoder_settings, "bitrate"); 193 | nicolive_log_debug("video bitrate: %lld", video_bitrate); 194 | nicolive_log_debug("audio bitrate: %lld", audio_bitrate); 195 | 196 | // FIXME: audio 0 ... bug? 197 | if (audio_bitrate <= 0) { 198 | nicolive_log_warn("audo bitrate is zero or negative"); 199 | return false; 200 | } 201 | 202 | long long adjust_bitrate = bitrate - audio_bitrate; 203 | 204 | // the smallest video bitrate is ... 205 | if (adjust_bitrate < 0) { 206 | nicolive_log_warn("audio bitrate is too high"); 207 | return false; 208 | } 209 | 210 | if (adjust_bitrate != video_bitrate) { 211 | obs_data_set_int( 212 | video_encoder_settings, "bitrate", adjust_bitrate); 213 | nicolive_log_debug( 214 | "adjust video bitrate: %lld", adjust_bitrate); 215 | } else { 216 | nicolive_log_debug("need not to adjust video bitrate"); 217 | } 218 | return true; 219 | } 220 | 221 | /* property events */ 222 | 223 | inline static bool on_modified_settings( 224 | obs_properties_t *props, obs_property_t *property, obs_data_t *settings) 225 | { 226 | nicolive_log_debug_call_func(); 227 | if (same_checked_settings(settings)) { 228 | if (!obs_data_get_bool(settings, "checked")) { 229 | obs_data_set_bool(settings, "checked", true); 230 | obs_data_set_string(settings, "check_message", 231 | obs_module_text("Checked")); 232 | } 233 | 234 | } else { 235 | if (obs_data_get_bool(settings, "checked")) { 236 | obs_data_set_bool(settings, "checked", false); 237 | obs_data_set_string(settings, "check_message", 238 | obs_module_text("Unchecked")); 239 | } 240 | } 241 | return true; 242 | } 243 | 244 | inline static bool on_modified_session_method( 245 | obs_properties_t *props, obs_property_t *property, obs_data_t *settings) 246 | { 247 | on_modified_settings(props, property, settings); 248 | // update current settings 249 | current_settings = settings; 250 | switch (obs_data_get_int(settings, "session_method")) { 251 | case RTMP_NICOLIVE_SESSION_LOGIN: 252 | obs_property_set_visible( 253 | obs_properties_get(props, "mail"), true); 254 | obs_property_set_visible( 255 | obs_properties_get(props, "password"), true); 256 | obs_property_set_visible( 257 | obs_properties_get(props, "session"), false); 258 | obs_property_set_visible( 259 | obs_properties_get(props, "cookie_app"), false); 260 | break; 261 | case RTMP_NICOLIVE_SESSION_APP: 262 | obs_property_set_visible( 263 | obs_properties_get(props, "mail"), false); 264 | obs_property_set_visible( 265 | obs_properties_get(props, "password"), false); 266 | obs_property_set_visible( 267 | obs_properties_get(props, "session"), false); 268 | obs_property_set_visible( 269 | obs_properties_get(props, "cookie_app"), true); 270 | break; 271 | case RTMP_NICOLIVE_SESSION_MANUAL: 272 | obs_property_set_visible( 273 | obs_properties_get(props, "mail"), false); 274 | obs_property_set_visible( 275 | obs_properties_get(props, "password"), false); 276 | obs_property_set_visible( 277 | obs_properties_get(props, "session"), true); 278 | obs_property_set_visible( 279 | obs_properties_get(props, "cookie_app"), false); 280 | break; 281 | 282 | default: 283 | nicolive_log_error("unknown login type"); 284 | return false; 285 | } 286 | return true; 287 | } 288 | 289 | inline static bool on_clicked_check( 290 | obs_properties_t *props, obs_property_t *property, void *data) 291 | { 292 | UNUSED_PARAMETER(props); 293 | UNUSED_PARAMETER(property); 294 | UNUSED_PARAMETER(data); 295 | return check_settings(current_settings); 296 | } 297 | 298 | /* module operations */ 299 | 300 | static const char *rtmp_nicolive_getname(void *type_data) 301 | { 302 | nicolive_log_debug_call_func(); 303 | UNUSED_PARAMETER(type_data); 304 | return obs_module_text("NiconicoLive"); 305 | } 306 | 307 | static void *rtmp_nicolive_create(obs_data_t *settings, obs_service_t *service) 308 | { 309 | nicolive_log_debug_call_func(); 310 | UNUSED_PARAMETER(service); 311 | void *data = nicolive_create(); 312 | if (check_settings(settings)) { 313 | set_data_nicolive(data, settings); 314 | } 315 | return data; 316 | } 317 | 318 | static void rtmp_nicolive_destroy(void *data) 319 | { 320 | nicolive_log_debug_call_func(); 321 | nicolive_destroy(data); 322 | } 323 | 324 | static void rtmp_nicolive_activate(void *data, obs_data_t *settings) 325 | { 326 | nicolive_log_debug_call_func(); 327 | UNUSED_PARAMETER(settings); 328 | nicolive_start_streaming(data); 329 | } 330 | 331 | static void rtmp_nicolive_deactivate(void *data) 332 | { 333 | nicolive_log_debug_call_func(); 334 | nicolive_stop_streaming(data); 335 | } 336 | 337 | // FIXME: why do not call this func. obs-studio 0.8.3 bug? 338 | static void rtmp_nicolive_update(void *data, obs_data_t *settings) 339 | { 340 | nicolive_log_debug_call_func(); 341 | if (check_settings(settings)) { 342 | set_data_nicolive(data, settings); 343 | } 344 | } 345 | 346 | static void rtmp_nicolive_defaults(obs_data_t *settings) 347 | { 348 | nicolive_log_debug_call_func(); 349 | obs_data_set_default_int( 350 | settings, "session_method", RTMP_NICOLIVE_SESSION_LOGIN); 351 | obs_data_set_default_string(settings, "mail", ""); 352 | obs_data_set_default_string(settings, "password", ""); 353 | obs_data_set_default_string(settings, "session", ""); 354 | obs_data_set_default_int(settings, "cookie_app", NICOOKIE_APP_NONE); 355 | obs_data_set_default_string( 356 | settings, "check_message", obs_module_text("Unchecked")); 357 | obs_data_set_default_bool(settings, "adjust_bitrate", true); 358 | obs_data_set_default_bool(settings, "auto_start", false); 359 | } 360 | 361 | static obs_properties_t *rtmp_nicolive_properties(void *data) 362 | { 363 | nicolive_log_debug_call_func(); 364 | UNUSED_PARAMETER(data); 365 | obs_properties_t *ppts = obs_properties_create(); 366 | 367 | obs_property_t *prop_session_method = obs_properties_add_list(ppts, 368 | "session_method", obs_module_text("SessionSettingMethod"), 369 | OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); 370 | obs_property_list_add_int(prop_session_method, 371 | obs_module_text("LoginMailPassowrd"), RTMP_NICOLIVE_SESSION_LOGIN); 372 | obs_property_list_add_int(prop_session_method, 373 | obs_module_text("LoadAppSettings"), RTMP_NICOLIVE_SESSION_APP); 374 | obs_property_list_add_int(prop_session_method, 375 | obs_module_text("ManualInputUserSession"), 376 | RTMP_NICOLIVE_SESSION_MANUAL); 377 | obs_property_set_modified_callback( 378 | prop_session_method, on_modified_session_method); 379 | 380 | // login mail 381 | obs_property_t *prop_mail = obs_properties_add_text( 382 | ppts, "mail", obs_module_text("MailAddress"), OBS_TEXT_DEFAULT); 383 | obs_property_set_modified_callback(prop_mail, on_modified_settings); 384 | obs_property_t *prop_password = obs_properties_add_text( 385 | ppts, "password", obs_module_text("Password"), OBS_TEXT_PASSWORD); 386 | obs_property_set_modified_callback(prop_password, on_modified_settings); 387 | 388 | // login session 389 | obs_property_t *prop_session = obs_properties_add_text( 390 | ppts, "session", obs_module_text("Session"), OBS_TEXT_PASSWORD); 391 | obs_property_set_modified_callback(prop_session, on_modified_settings); 392 | 393 | // login app 394 | obs_property_t *prop_cookie_app = obs_properties_add_list(ppts, 395 | "cookie_app", obs_module_text("LoadCookieApp"), OBS_COMBO_TYPE_LIST, 396 | OBS_COMBO_FORMAT_INT); 397 | const int *available_apps = nicookie_available_apps(); 398 | for (const int *app_p = available_apps; *app_p != NICOOKIE_APP_NONE; 399 | app_p++) { 400 | nicolive_log_debug("add list app: %d", *app_p); 401 | obs_property_list_add_int( 402 | prop_cookie_app, nicookie_app_name(*app_p), *app_p); 403 | } 404 | obs_property_set_modified_callback( 405 | prop_cookie_app, on_modified_settings); 406 | 407 | obs_properties_add_button( 408 | ppts, "check", obs_module_text("Check"), on_clicked_check); 409 | obs_property_t *prop_check_message = obs_properties_add_text( 410 | ppts, "check_message", "", OBS_TEXT_DEFAULT); 411 | obs_property_set_enabled(prop_check_message, false); 412 | 413 | obs_properties_add_bool( 414 | ppts, "adjust_bitrate", obs_module_text("AdjustBitrate")); 415 | 416 | obs_properties_add_bool( 417 | ppts, "auto_start", obs_module_text("AutoStart")); 418 | 419 | return ppts; 420 | } 421 | 422 | static bool rtmp_nicolive_initialize(void *data, obs_output_t *output) 423 | { 424 | nicolive_log_debug_call_func(); 425 | UNUSED_PARAMETER(output); 426 | bool success = false; 427 | 428 | if (nicolive_check_session(data)) { 429 | if (nicolive_check_live(data)) { 430 | success = true; 431 | } else { 432 | nicolive_log_warn("cannot start streaming: no live"); 433 | success = false; 434 | } 435 | } else { 436 | nicolive_log_warn("cannot start streaming: failed login"); 437 | success = false; 438 | } 439 | 440 | // force to adjust bitrate before starting 441 | long long bitrate = nicolive_get_live_bitrate(data); 442 | if (success && nicolive_enabled_adjust_bitrate(data) && bitrate > 0) { 443 | // ignore fails 444 | adjust_bitrate(bitrate, 445 | obs_encoder_get_settings( 446 | obs_output_get_video_encoder(output)), 447 | obs_encoder_get_settings( 448 | obs_output_get_audio_encoder(output, 0))); 449 | } 450 | 451 | return success; 452 | } 453 | 454 | static const char *rtmp_nicolive_url(void *data) 455 | { 456 | nicolive_log_debug_call_func(); 457 | return nicolive_get_live_url(data); 458 | } 459 | 460 | static const char *rtmp_nicolive_key(void *data) 461 | { 462 | nicolive_log_debug_call_func(); 463 | return nicolive_get_live_key(data); 464 | } 465 | 466 | static bool rtmp_nicolive_supports_multitrack(void *data) 467 | { 468 | nicolive_log_debug_call_func(); 469 | UNUSED_PARAMETER(data); 470 | return false; 471 | } 472 | 473 | // call func with to update settings 474 | static void rtmp_nicolive_apply_encoder_settings(void *data, 475 | obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings) 476 | { 477 | nicolive_log_debug_call_func(); 478 | if (!(nicolive_check_session(data) && nicolive_check_live(data))) { 479 | return; 480 | } 481 | 482 | long long bitrate = nicolive_get_live_bitrate(data); 483 | if (nicolive_enabled_adjust_bitrate(data) && bitrate > 0) { 484 | adjust_bitrate( 485 | bitrate, video_encoder_settings, audio_encoder_settings); 486 | } 487 | } 488 | 489 | /* module structure */ 490 | 491 | struct obs_service_info rtmp_nicolive_service = {.id = "rtmp_nicolive", 492 | .get_name = rtmp_nicolive_getname, 493 | .create = rtmp_nicolive_create, 494 | .destroy = rtmp_nicolive_destroy, 495 | .activate = rtmp_nicolive_activate, 496 | .deactivate = rtmp_nicolive_deactivate, 497 | .update = rtmp_nicolive_update, 498 | .get_defaults = rtmp_nicolive_defaults, 499 | .get_properties = rtmp_nicolive_properties, 500 | .initialize = rtmp_nicolive_initialize, 501 | .get_url = rtmp_nicolive_url, 502 | .get_key = rtmp_nicolive_key, 503 | .supports_multitrack = rtmp_nicolive_supports_multitrack, 504 | .apply_encoder_settings = rtmp_nicolive_apply_encoder_settings}; 505 | 506 | /* module initialize */ 507 | 508 | OBS_DECLARE_MODULE() 509 | OBS_MODULE_USE_DEFAULT_LOCALE("rtmp-nicolive", "en-US") 510 | OBS_MODULE_AUTHOR("raccy") 511 | 512 | bool obs_module_load(void) 513 | { 514 | obs_register_service(&rtmp_nicolive_service); 515 | return true; 516 | } 517 | 518 | const char *obs_module_name(void) 519 | { 520 | return obs_module_text("NiconicoLiveModule"); 521 | } 522 | 523 | const char *obs_module_description(void) 524 | { 525 | return obs_module_text("NiconicoLiveDescription"); 526 | } 527 | -------------------------------------------------------------------------------- /sqlite/sqlite3ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2006 June 7 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** 7 | ** May you do good and not evil. 8 | ** May you find forgiveness for yourself and forgive others. 9 | ** May you share freely, never taking more than you give. 10 | ** 11 | ************************************************************************* 12 | ** This header file defines the SQLite interface for use by 13 | ** shared libraries that want to be imported as extensions into 14 | ** an SQLite instance. Shared libraries that intend to be loaded 15 | ** as extensions by SQLite should #include this file instead of 16 | ** sqlite3.h. 17 | */ 18 | #ifndef SQLITE3EXT_H 19 | #define SQLITE3EXT_H 20 | #include "sqlite3.h" 21 | 22 | /* 23 | ** The following structure holds pointers to all of the SQLite API 24 | ** routines. 25 | ** 26 | ** WARNING: In order to maintain backwards compatibility, add new 27 | ** interfaces to the end of this structure only. If you insert new 28 | ** interfaces in the middle of this structure, then older different 29 | ** versions of SQLite will not be able to load each other's shared 30 | ** libraries! 31 | */ 32 | struct sqlite3_api_routines { 33 | void * (*aggregate_context)(sqlite3_context*,int nBytes); 34 | int (*aggregate_count)(sqlite3_context*); 35 | int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); 36 | int (*bind_double)(sqlite3_stmt*,int,double); 37 | int (*bind_int)(sqlite3_stmt*,int,int); 38 | int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); 39 | int (*bind_null)(sqlite3_stmt*,int); 40 | int (*bind_parameter_count)(sqlite3_stmt*); 41 | int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); 42 | const char * (*bind_parameter_name)(sqlite3_stmt*,int); 43 | int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); 44 | int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); 45 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); 46 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); 47 | int (*busy_timeout)(sqlite3*,int ms); 48 | int (*changes)(sqlite3*); 49 | int (*close)(sqlite3*); 50 | int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, 51 | int eTextRep,const char*)); 52 | int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, 53 | int eTextRep,const void*)); 54 | const void * (*column_blob)(sqlite3_stmt*,int iCol); 55 | int (*column_bytes)(sqlite3_stmt*,int iCol); 56 | int (*column_bytes16)(sqlite3_stmt*,int iCol); 57 | int (*column_count)(sqlite3_stmt*pStmt); 58 | const char * (*column_database_name)(sqlite3_stmt*,int); 59 | const void * (*column_database_name16)(sqlite3_stmt*,int); 60 | const char * (*column_decltype)(sqlite3_stmt*,int i); 61 | const void * (*column_decltype16)(sqlite3_stmt*,int); 62 | double (*column_double)(sqlite3_stmt*,int iCol); 63 | int (*column_int)(sqlite3_stmt*,int iCol); 64 | sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); 65 | const char * (*column_name)(sqlite3_stmt*,int); 66 | const void * (*column_name16)(sqlite3_stmt*,int); 67 | const char * (*column_origin_name)(sqlite3_stmt*,int); 68 | const void * (*column_origin_name16)(sqlite3_stmt*,int); 69 | const char * (*column_table_name)(sqlite3_stmt*,int); 70 | const void * (*column_table_name16)(sqlite3_stmt*,int); 71 | const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); 72 | const void * (*column_text16)(sqlite3_stmt*,int iCol); 73 | int (*column_type)(sqlite3_stmt*,int iCol); 74 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); 75 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); 76 | int (*complete)(const char*sql); 77 | int (*complete16)(const void*sql); 78 | int (*create_collation)(sqlite3*,const char*,int,void*, 79 | int(*)(void*,int,const void*,int,const void*)); 80 | int (*create_collation16)(sqlite3*,const void*,int,void*, 81 | int(*)(void*,int,const void*,int,const void*)); 82 | int (*create_function)(sqlite3*,const char*,int,int,void*, 83 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 84 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 85 | void (*xFinal)(sqlite3_context*)); 86 | int (*create_function16)(sqlite3*,const void*,int,int,void*, 87 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 88 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 89 | void (*xFinal)(sqlite3_context*)); 90 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); 91 | int (*data_count)(sqlite3_stmt*pStmt); 92 | sqlite3 * (*db_handle)(sqlite3_stmt*); 93 | int (*declare_vtab)(sqlite3*,const char*); 94 | int (*enable_shared_cache)(int); 95 | int (*errcode)(sqlite3*db); 96 | const char * (*errmsg)(sqlite3*); 97 | const void * (*errmsg16)(sqlite3*); 98 | int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); 99 | int (*expired)(sqlite3_stmt*); 100 | int (*finalize)(sqlite3_stmt*pStmt); 101 | void (*free)(void*); 102 | void (*free_table)(char**result); 103 | int (*get_autocommit)(sqlite3*); 104 | void * (*get_auxdata)(sqlite3_context*,int); 105 | int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); 106 | int (*global_recover)(void); 107 | void (*interruptx)(sqlite3*); 108 | sqlite_int64 (*last_insert_rowid)(sqlite3*); 109 | const char * (*libversion)(void); 110 | int (*libversion_number)(void); 111 | void *(*malloc)(int); 112 | char * (*mprintf)(const char*,...); 113 | int (*open)(const char*,sqlite3**); 114 | int (*open16)(const void*,sqlite3**); 115 | int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 116 | int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 117 | void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); 118 | void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); 119 | void *(*realloc)(void*,int); 120 | int (*reset)(sqlite3_stmt*pStmt); 121 | void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); 122 | void (*result_double)(sqlite3_context*,double); 123 | void (*result_error)(sqlite3_context*,const char*,int); 124 | void (*result_error16)(sqlite3_context*,const void*,int); 125 | void (*result_int)(sqlite3_context*,int); 126 | void (*result_int64)(sqlite3_context*,sqlite_int64); 127 | void (*result_null)(sqlite3_context*); 128 | void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); 129 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); 130 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); 131 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); 132 | void (*result_value)(sqlite3_context*,sqlite3_value*); 133 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); 134 | int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, 135 | const char*,const char*),void*); 136 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); 137 | char * (*snprintf)(int,char*,const char*,...); 138 | int (*step)(sqlite3_stmt*); 139 | int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, 140 | char const**,char const**,int*,int*,int*); 141 | void (*thread_cleanup)(void); 142 | int (*total_changes)(sqlite3*); 143 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); 144 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); 145 | void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, 146 | sqlite_int64),void*); 147 | void * (*user_data)(sqlite3_context*); 148 | const void * (*value_blob)(sqlite3_value*); 149 | int (*value_bytes)(sqlite3_value*); 150 | int (*value_bytes16)(sqlite3_value*); 151 | double (*value_double)(sqlite3_value*); 152 | int (*value_int)(sqlite3_value*); 153 | sqlite_int64 (*value_int64)(sqlite3_value*); 154 | int (*value_numeric_type)(sqlite3_value*); 155 | const unsigned char * (*value_text)(sqlite3_value*); 156 | const void * (*value_text16)(sqlite3_value*); 157 | const void * (*value_text16be)(sqlite3_value*); 158 | const void * (*value_text16le)(sqlite3_value*); 159 | int (*value_type)(sqlite3_value*); 160 | char *(*vmprintf)(const char*,va_list); 161 | /* Added ??? */ 162 | int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); 163 | /* Added by 3.3.13 */ 164 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); 165 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); 166 | int (*clear_bindings)(sqlite3_stmt*); 167 | /* Added by 3.4.1 */ 168 | int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, 169 | void (*xDestroy)(void *)); 170 | /* Added by 3.5.0 */ 171 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); 172 | int (*blob_bytes)(sqlite3_blob*); 173 | int (*blob_close)(sqlite3_blob*); 174 | int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, 175 | int,sqlite3_blob**); 176 | int (*blob_read)(sqlite3_blob*,void*,int,int); 177 | int (*blob_write)(sqlite3_blob*,const void*,int,int); 178 | int (*create_collation_v2)(sqlite3*,const char*,int,void*, 179 | int(*)(void*,int,const void*,int,const void*), 180 | void(*)(void*)); 181 | int (*file_control)(sqlite3*,const char*,int,void*); 182 | sqlite3_int64 (*memory_highwater)(int); 183 | sqlite3_int64 (*memory_used)(void); 184 | sqlite3_mutex *(*mutex_alloc)(int); 185 | void (*mutex_enter)(sqlite3_mutex*); 186 | void (*mutex_free)(sqlite3_mutex*); 187 | void (*mutex_leave)(sqlite3_mutex*); 188 | int (*mutex_try)(sqlite3_mutex*); 189 | int (*open_v2)(const char*,sqlite3**,int,const char*); 190 | int (*release_memory)(int); 191 | void (*result_error_nomem)(sqlite3_context*); 192 | void (*result_error_toobig)(sqlite3_context*); 193 | int (*sleep)(int); 194 | void (*soft_heap_limit)(int); 195 | sqlite3_vfs *(*vfs_find)(const char*); 196 | int (*vfs_register)(sqlite3_vfs*,int); 197 | int (*vfs_unregister)(sqlite3_vfs*); 198 | int (*xthreadsafe)(void); 199 | void (*result_zeroblob)(sqlite3_context*,int); 200 | void (*result_error_code)(sqlite3_context*,int); 201 | int (*test_control)(int, ...); 202 | void (*randomness)(int,void*); 203 | sqlite3 *(*context_db_handle)(sqlite3_context*); 204 | int (*extended_result_codes)(sqlite3*,int); 205 | int (*limit)(sqlite3*,int,int); 206 | sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); 207 | const char *(*sql)(sqlite3_stmt*); 208 | int (*status)(int,int*,int*,int); 209 | int (*backup_finish)(sqlite3_backup*); 210 | sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); 211 | int (*backup_pagecount)(sqlite3_backup*); 212 | int (*backup_remaining)(sqlite3_backup*); 213 | int (*backup_step)(sqlite3_backup*,int); 214 | const char *(*compileoption_get)(int); 215 | int (*compileoption_used)(const char*); 216 | int (*create_function_v2)(sqlite3*,const char*,int,int,void*, 217 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), 218 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), 219 | void (*xFinal)(sqlite3_context*), 220 | void(*xDestroy)(void*)); 221 | int (*db_config)(sqlite3*,int,...); 222 | sqlite3_mutex *(*db_mutex)(sqlite3*); 223 | int (*db_status)(sqlite3*,int,int*,int*,int); 224 | int (*extended_errcode)(sqlite3*); 225 | void (*log)(int,const char*,...); 226 | sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); 227 | const char *(*sourceid)(void); 228 | int (*stmt_status)(sqlite3_stmt*,int,int); 229 | int (*strnicmp)(const char*,const char*,int); 230 | int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); 231 | int (*wal_autocheckpoint)(sqlite3*,int); 232 | int (*wal_checkpoint)(sqlite3*,const char*); 233 | void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); 234 | int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); 235 | int (*vtab_config)(sqlite3*,int op,...); 236 | int (*vtab_on_conflict)(sqlite3*); 237 | /* Version 3.7.16 and later */ 238 | int (*close_v2)(sqlite3*); 239 | const char *(*db_filename)(sqlite3*,const char*); 240 | int (*db_readonly)(sqlite3*,const char*); 241 | int (*db_release_memory)(sqlite3*); 242 | const char *(*errstr)(int); 243 | int (*stmt_busy)(sqlite3_stmt*); 244 | int (*stmt_readonly)(sqlite3_stmt*); 245 | int (*stricmp)(const char*,const char*); 246 | int (*uri_boolean)(const char*,const char*,int); 247 | sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); 248 | const char *(*uri_parameter)(const char*,const char*); 249 | char *(*vsnprintf)(int,char*,const char*,va_list); 250 | int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); 251 | /* Version 3.8.7 and later */ 252 | int (*auto_extension)(void(*)(void)); 253 | int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, 254 | void(*)(void*)); 255 | int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, 256 | void(*)(void*),unsigned char); 257 | int (*cancel_auto_extension)(void(*)(void)); 258 | int (*load_extension)(sqlite3*,const char*,const char*,char**); 259 | void *(*malloc64)(sqlite3_uint64); 260 | sqlite3_uint64 (*msize)(void*); 261 | void *(*realloc64)(void*,sqlite3_uint64); 262 | void (*reset_auto_extension)(void); 263 | void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, 264 | void(*)(void*)); 265 | void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, 266 | void(*)(void*), unsigned char); 267 | int (*strglob)(const char*,const char*); 268 | /* Version 3.8.11 and later */ 269 | sqlite3_value *(*value_dup)(const sqlite3_value*); 270 | void (*value_free)(sqlite3_value*); 271 | int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64); 272 | int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64); 273 | /* Version 3.9.0 and later */ 274 | unsigned int (*value_subtype)(sqlite3_value*); 275 | void (*result_subtype)(sqlite3_context*,unsigned int); 276 | /* Version 3.10.0 and later */ 277 | int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int); 278 | int (*strlike)(const char*,const char*,unsigned int); 279 | int (*db_cacheflush)(sqlite3*); 280 | /* Version 3.12.0 and later */ 281 | int (*system_errno)(sqlite3*); 282 | /* Version 3.14.0 and later */ 283 | int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); 284 | char *(*expanded_sql)(sqlite3_stmt*); 285 | /* Version 3.18.0 and later */ 286 | void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64); 287 | }; 288 | 289 | /* 290 | ** This is the function signature used for all extension entry points. It 291 | ** is also defined in the file "loadext.c". 292 | */ 293 | typedef int (*sqlite3_loadext_entry)( 294 | sqlite3 *db, /* Handle to the database. */ 295 | char **pzErrMsg, /* Used to set error string on failure. */ 296 | const sqlite3_api_routines *pThunk /* Extension API function pointers. */ 297 | ); 298 | 299 | /* 300 | ** The following macros redefine the API routines so that they are 301 | ** redirected through the global sqlite3_api structure. 302 | ** 303 | ** This header file is also used by the loadext.c source file 304 | ** (part of the main SQLite library - not an extension) so that 305 | ** it can get access to the sqlite3_api_routines structure 306 | ** definition. But the main library does not want to redefine 307 | ** the API. So the redefinition macros are only valid if the 308 | ** SQLITE_CORE macros is undefined. 309 | */ 310 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 311 | #define sqlite3_aggregate_context sqlite3_api->aggregate_context 312 | #ifndef SQLITE_OMIT_DEPRECATED 313 | #define sqlite3_aggregate_count sqlite3_api->aggregate_count 314 | #endif 315 | #define sqlite3_bind_blob sqlite3_api->bind_blob 316 | #define sqlite3_bind_double sqlite3_api->bind_double 317 | #define sqlite3_bind_int sqlite3_api->bind_int 318 | #define sqlite3_bind_int64 sqlite3_api->bind_int64 319 | #define sqlite3_bind_null sqlite3_api->bind_null 320 | #define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count 321 | #define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index 322 | #define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name 323 | #define sqlite3_bind_text sqlite3_api->bind_text 324 | #define sqlite3_bind_text16 sqlite3_api->bind_text16 325 | #define sqlite3_bind_value sqlite3_api->bind_value 326 | #define sqlite3_busy_handler sqlite3_api->busy_handler 327 | #define sqlite3_busy_timeout sqlite3_api->busy_timeout 328 | #define sqlite3_changes sqlite3_api->changes 329 | #define sqlite3_close sqlite3_api->close 330 | #define sqlite3_collation_needed sqlite3_api->collation_needed 331 | #define sqlite3_collation_needed16 sqlite3_api->collation_needed16 332 | #define sqlite3_column_blob sqlite3_api->column_blob 333 | #define sqlite3_column_bytes sqlite3_api->column_bytes 334 | #define sqlite3_column_bytes16 sqlite3_api->column_bytes16 335 | #define sqlite3_column_count sqlite3_api->column_count 336 | #define sqlite3_column_database_name sqlite3_api->column_database_name 337 | #define sqlite3_column_database_name16 sqlite3_api->column_database_name16 338 | #define sqlite3_column_decltype sqlite3_api->column_decltype 339 | #define sqlite3_column_decltype16 sqlite3_api->column_decltype16 340 | #define sqlite3_column_double sqlite3_api->column_double 341 | #define sqlite3_column_int sqlite3_api->column_int 342 | #define sqlite3_column_int64 sqlite3_api->column_int64 343 | #define sqlite3_column_name sqlite3_api->column_name 344 | #define sqlite3_column_name16 sqlite3_api->column_name16 345 | #define sqlite3_column_origin_name sqlite3_api->column_origin_name 346 | #define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 347 | #define sqlite3_column_table_name sqlite3_api->column_table_name 348 | #define sqlite3_column_table_name16 sqlite3_api->column_table_name16 349 | #define sqlite3_column_text sqlite3_api->column_text 350 | #define sqlite3_column_text16 sqlite3_api->column_text16 351 | #define sqlite3_column_type sqlite3_api->column_type 352 | #define sqlite3_column_value sqlite3_api->column_value 353 | #define sqlite3_commit_hook sqlite3_api->commit_hook 354 | #define sqlite3_complete sqlite3_api->complete 355 | #define sqlite3_complete16 sqlite3_api->complete16 356 | #define sqlite3_create_collation sqlite3_api->create_collation 357 | #define sqlite3_create_collation16 sqlite3_api->create_collation16 358 | #define sqlite3_create_function sqlite3_api->create_function 359 | #define sqlite3_create_function16 sqlite3_api->create_function16 360 | #define sqlite3_create_module sqlite3_api->create_module 361 | #define sqlite3_create_module_v2 sqlite3_api->create_module_v2 362 | #define sqlite3_data_count sqlite3_api->data_count 363 | #define sqlite3_db_handle sqlite3_api->db_handle 364 | #define sqlite3_declare_vtab sqlite3_api->declare_vtab 365 | #define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache 366 | #define sqlite3_errcode sqlite3_api->errcode 367 | #define sqlite3_errmsg sqlite3_api->errmsg 368 | #define sqlite3_errmsg16 sqlite3_api->errmsg16 369 | #define sqlite3_exec sqlite3_api->exec 370 | #ifndef SQLITE_OMIT_DEPRECATED 371 | #define sqlite3_expired sqlite3_api->expired 372 | #endif 373 | #define sqlite3_finalize sqlite3_api->finalize 374 | #define sqlite3_free sqlite3_api->free 375 | #define sqlite3_free_table sqlite3_api->free_table 376 | #define sqlite3_get_autocommit sqlite3_api->get_autocommit 377 | #define sqlite3_get_auxdata sqlite3_api->get_auxdata 378 | #define sqlite3_get_table sqlite3_api->get_table 379 | #ifndef SQLITE_OMIT_DEPRECATED 380 | #define sqlite3_global_recover sqlite3_api->global_recover 381 | #endif 382 | #define sqlite3_interrupt sqlite3_api->interruptx 383 | #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid 384 | #define sqlite3_libversion sqlite3_api->libversion 385 | #define sqlite3_libversion_number sqlite3_api->libversion_number 386 | #define sqlite3_malloc sqlite3_api->malloc 387 | #define sqlite3_mprintf sqlite3_api->mprintf 388 | #define sqlite3_open sqlite3_api->open 389 | #define sqlite3_open16 sqlite3_api->open16 390 | #define sqlite3_prepare sqlite3_api->prepare 391 | #define sqlite3_prepare16 sqlite3_api->prepare16 392 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 393 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 394 | #define sqlite3_profile sqlite3_api->profile 395 | #define sqlite3_progress_handler sqlite3_api->progress_handler 396 | #define sqlite3_realloc sqlite3_api->realloc 397 | #define sqlite3_reset sqlite3_api->reset 398 | #define sqlite3_result_blob sqlite3_api->result_blob 399 | #define sqlite3_result_double sqlite3_api->result_double 400 | #define sqlite3_result_error sqlite3_api->result_error 401 | #define sqlite3_result_error16 sqlite3_api->result_error16 402 | #define sqlite3_result_int sqlite3_api->result_int 403 | #define sqlite3_result_int64 sqlite3_api->result_int64 404 | #define sqlite3_result_null sqlite3_api->result_null 405 | #define sqlite3_result_text sqlite3_api->result_text 406 | #define sqlite3_result_text16 sqlite3_api->result_text16 407 | #define sqlite3_result_text16be sqlite3_api->result_text16be 408 | #define sqlite3_result_text16le sqlite3_api->result_text16le 409 | #define sqlite3_result_value sqlite3_api->result_value 410 | #define sqlite3_rollback_hook sqlite3_api->rollback_hook 411 | #define sqlite3_set_authorizer sqlite3_api->set_authorizer 412 | #define sqlite3_set_auxdata sqlite3_api->set_auxdata 413 | #define sqlite3_snprintf sqlite3_api->snprintf 414 | #define sqlite3_step sqlite3_api->step 415 | #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata 416 | #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup 417 | #define sqlite3_total_changes sqlite3_api->total_changes 418 | #define sqlite3_trace sqlite3_api->trace 419 | #ifndef SQLITE_OMIT_DEPRECATED 420 | #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings 421 | #endif 422 | #define sqlite3_update_hook sqlite3_api->update_hook 423 | #define sqlite3_user_data sqlite3_api->user_data 424 | #define sqlite3_value_blob sqlite3_api->value_blob 425 | #define sqlite3_value_bytes sqlite3_api->value_bytes 426 | #define sqlite3_value_bytes16 sqlite3_api->value_bytes16 427 | #define sqlite3_value_double sqlite3_api->value_double 428 | #define sqlite3_value_int sqlite3_api->value_int 429 | #define sqlite3_value_int64 sqlite3_api->value_int64 430 | #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type 431 | #define sqlite3_value_text sqlite3_api->value_text 432 | #define sqlite3_value_text16 sqlite3_api->value_text16 433 | #define sqlite3_value_text16be sqlite3_api->value_text16be 434 | #define sqlite3_value_text16le sqlite3_api->value_text16le 435 | #define sqlite3_value_type sqlite3_api->value_type 436 | #define sqlite3_vmprintf sqlite3_api->vmprintf 437 | #define sqlite3_vsnprintf sqlite3_api->vsnprintf 438 | #define sqlite3_overload_function sqlite3_api->overload_function 439 | #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 440 | #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 441 | #define sqlite3_clear_bindings sqlite3_api->clear_bindings 442 | #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob 443 | #define sqlite3_blob_bytes sqlite3_api->blob_bytes 444 | #define sqlite3_blob_close sqlite3_api->blob_close 445 | #define sqlite3_blob_open sqlite3_api->blob_open 446 | #define sqlite3_blob_read sqlite3_api->blob_read 447 | #define sqlite3_blob_write sqlite3_api->blob_write 448 | #define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 449 | #define sqlite3_file_control sqlite3_api->file_control 450 | #define sqlite3_memory_highwater sqlite3_api->memory_highwater 451 | #define sqlite3_memory_used sqlite3_api->memory_used 452 | #define sqlite3_mutex_alloc sqlite3_api->mutex_alloc 453 | #define sqlite3_mutex_enter sqlite3_api->mutex_enter 454 | #define sqlite3_mutex_free sqlite3_api->mutex_free 455 | #define sqlite3_mutex_leave sqlite3_api->mutex_leave 456 | #define sqlite3_mutex_try sqlite3_api->mutex_try 457 | #define sqlite3_open_v2 sqlite3_api->open_v2 458 | #define sqlite3_release_memory sqlite3_api->release_memory 459 | #define sqlite3_result_error_nomem sqlite3_api->result_error_nomem 460 | #define sqlite3_result_error_toobig sqlite3_api->result_error_toobig 461 | #define sqlite3_sleep sqlite3_api->sleep 462 | #define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit 463 | #define sqlite3_vfs_find sqlite3_api->vfs_find 464 | #define sqlite3_vfs_register sqlite3_api->vfs_register 465 | #define sqlite3_vfs_unregister sqlite3_api->vfs_unregister 466 | #define sqlite3_threadsafe sqlite3_api->xthreadsafe 467 | #define sqlite3_result_zeroblob sqlite3_api->result_zeroblob 468 | #define sqlite3_result_error_code sqlite3_api->result_error_code 469 | #define sqlite3_test_control sqlite3_api->test_control 470 | #define sqlite3_randomness sqlite3_api->randomness 471 | #define sqlite3_context_db_handle sqlite3_api->context_db_handle 472 | #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes 473 | #define sqlite3_limit sqlite3_api->limit 474 | #define sqlite3_next_stmt sqlite3_api->next_stmt 475 | #define sqlite3_sql sqlite3_api->sql 476 | #define sqlite3_status sqlite3_api->status 477 | #define sqlite3_backup_finish sqlite3_api->backup_finish 478 | #define sqlite3_backup_init sqlite3_api->backup_init 479 | #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount 480 | #define sqlite3_backup_remaining sqlite3_api->backup_remaining 481 | #define sqlite3_backup_step sqlite3_api->backup_step 482 | #define sqlite3_compileoption_get sqlite3_api->compileoption_get 483 | #define sqlite3_compileoption_used sqlite3_api->compileoption_used 484 | #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 485 | #define sqlite3_db_config sqlite3_api->db_config 486 | #define sqlite3_db_mutex sqlite3_api->db_mutex 487 | #define sqlite3_db_status sqlite3_api->db_status 488 | #define sqlite3_extended_errcode sqlite3_api->extended_errcode 489 | #define sqlite3_log sqlite3_api->log 490 | #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 491 | #define sqlite3_sourceid sqlite3_api->sourceid 492 | #define sqlite3_stmt_status sqlite3_api->stmt_status 493 | #define sqlite3_strnicmp sqlite3_api->strnicmp 494 | #define sqlite3_unlock_notify sqlite3_api->unlock_notify 495 | #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint 496 | #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint 497 | #define sqlite3_wal_hook sqlite3_api->wal_hook 498 | #define sqlite3_blob_reopen sqlite3_api->blob_reopen 499 | #define sqlite3_vtab_config sqlite3_api->vtab_config 500 | #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict 501 | /* Version 3.7.16 and later */ 502 | #define sqlite3_close_v2 sqlite3_api->close_v2 503 | #define sqlite3_db_filename sqlite3_api->db_filename 504 | #define sqlite3_db_readonly sqlite3_api->db_readonly 505 | #define sqlite3_db_release_memory sqlite3_api->db_release_memory 506 | #define sqlite3_errstr sqlite3_api->errstr 507 | #define sqlite3_stmt_busy sqlite3_api->stmt_busy 508 | #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly 509 | #define sqlite3_stricmp sqlite3_api->stricmp 510 | #define sqlite3_uri_boolean sqlite3_api->uri_boolean 511 | #define sqlite3_uri_int64 sqlite3_api->uri_int64 512 | #define sqlite3_uri_parameter sqlite3_api->uri_parameter 513 | #define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf 514 | #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 515 | /* Version 3.8.7 and later */ 516 | #define sqlite3_auto_extension sqlite3_api->auto_extension 517 | #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 518 | #define sqlite3_bind_text64 sqlite3_api->bind_text64 519 | #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension 520 | #define sqlite3_load_extension sqlite3_api->load_extension 521 | #define sqlite3_malloc64 sqlite3_api->malloc64 522 | #define sqlite3_msize sqlite3_api->msize 523 | #define sqlite3_realloc64 sqlite3_api->realloc64 524 | #define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension 525 | #define sqlite3_result_blob64 sqlite3_api->result_blob64 526 | #define sqlite3_result_text64 sqlite3_api->result_text64 527 | #define sqlite3_strglob sqlite3_api->strglob 528 | /* Version 3.8.11 and later */ 529 | #define sqlite3_value_dup sqlite3_api->value_dup 530 | #define sqlite3_value_free sqlite3_api->value_free 531 | #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 532 | #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 533 | /* Version 3.9.0 and later */ 534 | #define sqlite3_value_subtype sqlite3_api->value_subtype 535 | #define sqlite3_result_subtype sqlite3_api->result_subtype 536 | /* Version 3.10.0 and later */ 537 | #define sqlite3_status64 sqlite3_api->status64 538 | #define sqlite3_strlike sqlite3_api->strlike 539 | #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush 540 | /* Version 3.12.0 and later */ 541 | #define sqlite3_system_errno sqlite3_api->system_errno 542 | /* Version 3.14.0 and later */ 543 | #define sqlite3_trace_v2 sqlite3_api->trace_v2 544 | #define sqlite3_expanded_sql sqlite3_api->expanded_sql 545 | /* Version 3.18.0 and later */ 546 | #define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid 547 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ 548 | 549 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 550 | /* This case when the file really is being compiled as a loadable 551 | ** extension */ 552 | # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; 553 | # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; 554 | # define SQLITE_EXTENSION_INIT3 \ 555 | extern const sqlite3_api_routines *sqlite3_api; 556 | #else 557 | /* This case when the file is being statically linked into the 558 | ** application */ 559 | # define SQLITE_EXTENSION_INIT1 /*no-op*/ 560 | # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ 561 | # define SQLITE_EXTENSION_INIT3 /*no-op*/ 562 | #endif 563 | 564 | #endif /* SQLITE3EXT_H */ 565 | -------------------------------------------------------------------------------- /tools/cmake/FindOBS.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | if(NOT OBS_SRC) 4 | MESSAGE(FATAL_ERROR "Please set obs src path: -DOBS_SRC=...") 5 | return() 6 | endif(NOT OBS_SRC) 7 | 8 | if(NOT OBS_APP) 9 | if(WIN32) 10 | set(OBS_APP "C:/Program Files (x86)/obs-studio") 11 | elseif(APPLE) 12 | set(OBS_APP "/Applications/OBS.app") 13 | else() 14 | MESSAGE(FATAL_ERROR "Please set obs app root path: -DOBS_APP=...") 15 | return() 16 | endif() 17 | endif(NOT OBS_APP) 18 | 19 | find_path(OBS_INCLUDES_LIBOBS 20 | NAMES obs.h obs-module.h 21 | PATHS "${OBS_SRC}/libobs") 22 | 23 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 24 | set(_lib_suffix 64) 25 | else() 26 | set(_lib_suffix 32) 27 | endif() 28 | 29 | if(WIN32) 30 | set(LIBOBS_PLUGIN_DESTINATION 31 | "${OBS_APP}/obs-plugins/${_lib_suffix}bit") 32 | set(LIBOBS_PLUGIN_DATA_DESTINATION 33 | "${OBS_APP}/data/obs-plugins") 34 | 35 | set(OBS_APP_BIN "${OBS_APP}/bin/${_lib_suffix}bit") 36 | set(OBS_LIB_LIBOBS "${OBS_APP_BIN}/obs.dll") 37 | # win lib 38 | set(OBS_APP_LIB "${CMAKE_CURRENT_SOURCE_DIR}/build/lib${_lib_suffix}") 39 | set(OBS_LIB_LIBOBS_LIB "${OBS_APP_LIB}/obs.lib") 40 | elseif(APPLE) 41 | set(LIBOBS_PLUGIN_DESTINATION 42 | "${OBS_APP}/Contents/Resources/obs-plugins/") 43 | set(LIBOBS_PLUGIN_DATA_DESTINATION 44 | "${OBS_APP}/Contents/Resources/data/obs-plugins") 45 | 46 | set(OBS_APP_BIN "${OBS_APP}/Contents/Resources/bin") 47 | set(OBS_LIB_LIBOBS "${OBS_APP_BIN}/libobs.0.dylib") 48 | else() 49 | # linux? 50 | set(LIBOBS_PLUGIN_DESTINATION 51 | "${OBS_APP}/obs-plugins") 52 | set(LIBOBS_PLUGIN_DATA_DESTINATION 53 | "${OBS_APP}/data/obs-plugins") 54 | 55 | set(OBS_APP_BIN "${OBS_APP}/lib") 56 | set(OBS_LIB_LIBOBS "${OBS_APP_BIN}/libobs.0.so") 57 | endif() 58 | -------------------------------------------------------------------------------- /tools/osx/MAC_INSTALL.md: -------------------------------------------------------------------------------- 1 | インストール方法 2 | ---------------- 3 | 4 | 始めに、OBS Studio を https://github.com/jp9000/obs-studio/releases からダウンロードして、インストールして下さい。アプリケーションにある「OBS」を右クリックして「パッケージの内容を表示」で中身を開いて下さい。「Contents」ディレクトリをそのままドラッグ&ドロップして下さい。認証画面でパスワードを入力し、ディレクトリの「結合」を選んで下さい。 5 | -------------------------------------------------------------------------------- /tools/osx/convert_readme.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | if __FILE__ == $0 4 | readme_md = ARGV[0] 5 | install_md = ARGV[1] 6 | readme_txt = ARGV[2] 7 | 8 | readme_md_all = IO.read(readme_md, mode: "rt", encoding: "UTF-8") 9 | install_md_all = IO.read(install_md, mode: "rt", encoding: "UTF-8") 10 | text = "" 11 | flag = 0 12 | readme_md_all.each_line do |line| 13 | case flag 14 | when 0 15 | if line =~ /^インストール方法$/ 16 | text << install_md_all 17 | text << "\n" 18 | flag = 1 19 | else 20 | text << line 21 | end 22 | when 1 23 | if line =~ /^使い方$/ 24 | flag = 2 25 | text << line 26 | end 27 | else 28 | text << line 29 | end 30 | end 31 | open(readme_txt, "wt:UTF-8") do |io| 32 | io.write text 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /tools/osx/fix_rpath.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # coding: utf-8 3 | 4 | class FixRpath 5 | FIX_RPATH_LIST = ['@loader_path/', '@executable_path/'] 6 | 7 | def initialize(path) 8 | @path = path 9 | @load_list = [] 10 | @rpath_list = [] 11 | read_otool 12 | end 13 | 14 | def read_otool 15 | otool_str = `otool -l #{@path}` 16 | cmd_type = nil 17 | otool_str.each_line do |line| 18 | case line 19 | when /^\s+cmd\s+(\S+)/ 20 | cmd_type = $1 21 | when /^\s+name\s+(\S+)/ 22 | name = $1 23 | if cmd_type == "LC_LOAD_DYLIB" 24 | @load_list << name 25 | end 26 | when /^\s+path\s+(\S+)/ 27 | path = $1 28 | if cmd_type == "LC_RPATH" 29 | @rpath_list << path 30 | end 31 | end 32 | end 33 | # p @load_list 34 | # p @rpath_list 35 | end 36 | 37 | def fix_load 38 | @load_list.each do |e| 39 | if e =~ %r{^/usr/(?!local/)|^/System/|^/Library/} 40 | next 41 | end 42 | name = File.basename(e) 43 | system("install_name_tool -change #{e} @rpath/#{name} #{@path}") 44 | end 45 | end 46 | 47 | def fix_rpath 48 | @rpath_list.each do |e| 49 | system("install_name_tool -delete_rpath #{e} #{@path}") 50 | end 51 | FixRpath::FIX_RPATH_LIST.each do |e| 52 | system("install_name_tool -add_rpath #{e} #{@path}") 53 | end 54 | end 55 | 56 | def fix_all 57 | fix_load 58 | fix_rpath 59 | end 60 | end 61 | 62 | if __FILE__ == $0 63 | if ARGV.size < 0 64 | puts "Usage: #{$0} path" 65 | exit 1 66 | end 67 | path = ARGV[0] 68 | if File.file?(path) 69 | FixRpath.new(path).fix_all 70 | else 71 | fail "Not found path" 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /tools/osx/make_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make package tool for obs-rtmp-nicolive 4 | 5 | OBS_RTMP_NICOLIVE_VERSION=$(cat ../VERSION) 6 | PACAKGE_NAME=obs-rtmp-nicolive_${OBS_RTMP_NICOLIVE_VERSION}-osx 7 | 8 | DATA_DIR=../data 9 | PLUGIN_NAME=rtmp-nicolive 10 | PLUGIN_SO=${PACAKGE_NAME}/Contents/Resources/obs-plugins/${PLUGIN_NAME}.so 11 | 12 | RUBY_EXE=/usr/bin/ruby 13 | 14 | README_MD=../README.md 15 | MAC_INSTALL_MD=../tools/osx/MAC_INSTALL.md 16 | CONVERT_README_RB=../tools/osx/convert_readme.rb 17 | FIX_RPATH_RB=../tools/osx/fix_rpath.rb 18 | 19 | echo creating package... 20 | if [ -e "${PACAKGE_NAME}" ]; then 21 | rm -rf "${PACAKGE_NAME}" 22 | fi 23 | if [ -e "${PACAKGE_NAME}.dmg" ]; then 24 | rm "${PACAKGE_NAME}.dmg" 25 | fi 26 | 27 | mkdir "${PACAKGE_NAME}" 28 | mkdir "${PACAKGE_NAME}/Contents" 29 | mkdir "${PACAKGE_NAME}/Contents/Resources" 30 | mkdir "${PACAKGE_NAME}/Contents/Resources/obs-plugins" 31 | mkdir "${PACAKGE_NAME}/Contents/Resources/data" 32 | mkdir "${PACAKGE_NAME}/Contents/Resources/data/obs-plugins" 33 | 34 | 35 | cp "${PLUGIN_NAME}.so" \ 36 | "${PACAKGE_NAME}/Contents/Resources/obs-plugins/${PLUGIN_NAME}.so" 37 | cp "../LICENSE" "${PACAKGE_NAME}/LICENSE" 38 | cp -R "../data" \ 39 | "${PACAKGE_NAME}/Contents/Resources/data/obs-plugins/rtmp-nicolive" 40 | "${RUBY_EXE}" "${CONVERT_README_RB}" "${README_MD}" "${MAC_INSTALL_MD}" \ 41 | "${PACAKGE_NAME}/README.txt" 42 | 43 | hdiutil create -ov -srcfolder "${PACAKGE_NAME}" -fs HFS+ -format UDBZ \ 44 | -volname "obs-studio rtmp-nicolive" "${PACAKGE_NAME}.dmg" 45 | 46 | echo "##### Succeeded to create package. #####" 47 | echo Read ${PACAKGE_NAME}/README.txt if you want to install this plugin. 48 | echo And you can deploy the ${PACAKGE_NAME}.dmg file! 49 | -------------------------------------------------------------------------------- /tools/ubuntu/make_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make package tool for obs-rtmp-nicolive 4 | 5 | OBS_RTMP_NICOLIVE_VERSION=$(cat ../VERSION) 6 | PACAKGE_NAME=obs-rtmp-nicolive 7 | 8 | DOC_DIR=doc-pak 9 | 10 | mkdir ${DOC_DIR} 11 | cp ../README.md ${DOC_DIR}/README.md 12 | cp ../LICENSE ${DOC_DIR}/LICENSE 13 | cp -R ../doc ${DOC_DIR}/doc 14 | 15 | echo "RTMP NicoLive Plugin for OBS MultiPlatform" > description-pak 16 | 17 | sudo checkinstall -D \ 18 | --install=no \ 19 | -y \ 20 | --pkgname=${PACAKGE_NAME} \ 21 | --pkgversion=${OBS_RTMP_NICOLIVE_VERSION} \ 22 | --pkgrelease=ubuntu \ 23 | --pkglicense=MIT \ 24 | --pkggroup=obs-studio \ 25 | --maintainer=open@raccy.org \ 26 | --requires=obs-studio \ 27 | --deldoc \ 28 | --deldesc 29 | -------------------------------------------------------------------------------- /tools/win/WIN_INSTALL.md: -------------------------------------------------------------------------------- 1 | インストール方法 2 | ---------------- 3 | 4 | 始めに、OBS Studio を https://github.com/jp9000/obs-studio/releases からダウンロードして、インストールして下さい。インストール先フォルダに 「obs-plugins」と「data」をそのままドラッグ&ドロップしてフォルダを結合して下さい。 5 | -------------------------------------------------------------------------------- /tools/win/convert_readme.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | if __FILE__ == $0 4 | readme_md = ARGV[0] 5 | install_md = ARGV[1] 6 | readme_txt = ARGV[2] 7 | 8 | readme_md_all = IO.read(readme_md, mode: "rt", encoding: "UTF-8") 9 | install_md_all = IO.read(install_md, mode: "rt", encoding: "UTF-8") 10 | text = "" 11 | flag = 0 12 | readme_md_all.each_line do |line| 13 | case flag 14 | when 0 15 | if line =~ /^インストール方法$/ 16 | text << install_md_all 17 | text << "\n" 18 | flag = 1 19 | else 20 | text << line 21 | end 22 | when 1 23 | if line =~ /^使い方$/ 24 | flag = 2 25 | text << line 26 | end 27 | else 28 | text << line 29 | end 30 | end 31 | open(readme_txt, "wb:UTF-8") do |io| 32 | io.write "\uFEFF" # BOM 33 | io.write text.gsub(/\r?\n/, "\r\n") 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /tools/win/make_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # make package tool for obs-rtmp-nicolive 4 | 5 | OBS_RTMP_NICOLIVE_VERSION=$(cat ../VERSION) 6 | PACAKGE_NAME=obs-rtmp-nicolive_${OBS_RTMP_NICOLIVE_VERSION}-win 7 | 8 | DATA_DIR=../data 9 | PLUGIN_NAME=rtmp-nicolive 10 | PLUGIN_32_DLL=obs-plugins/32bit/${PLUGIN_NAME}.dll 11 | PLUGIN_64_DLL=obs-plugins/64bit/${PLUGIN_NAME}.dll 12 | BUILD_32_DLL=../build32/${PLUGIN_NAME}.dll 13 | BUILD_64_DLL=../build64/${PLUGIN_NAME}.dll 14 | 15 | RUBY_EXE=ruby 16 | 17 | README_MD=../README.md 18 | INSTALL_MD=../tools/win/WIN_INSTALL.md 19 | CONVERT_README_RB=../tools/win/convert_readme.rb 20 | 21 | echo creating package... 22 | if [ -e "${PACAKGE_NAME}" ]; then 23 | rm -rf "${PACAKGE_NAME}" 24 | fi 25 | if [ -e "${PACAKGE_NAME}.zip" ]; then 26 | rm "${PACAKGE_NAME}.zip" 27 | fi 28 | 29 | mkdir "${PACAKGE_NAME}" 30 | mkdir "${PACAKGE_NAME}/obs-plugins" 31 | mkdir "${PACAKGE_NAME}/obs-plugins/32bit" 32 | mkdir "${PACAKGE_NAME}/obs-plugins/64bit" 33 | mkdir "${PACAKGE_NAME}/data" 34 | mkdir "${PACAKGE_NAME}/data/obs-plugins" 35 | 36 | cp "${BUILD_32_DLL}" "${PACAKGE_NAME}/${PLUGIN_32_DLL}" 37 | cp "${BUILD_64_DLL}" "${PACAKGE_NAME}/${PLUGIN_64_DLL}" 38 | cp "../LICENSE" "${PACAKGE_NAME}/LICENSE" 39 | cp -R "../data" \ 40 | "${PACAKGE_NAME}/data/obs-plugins/rtmp-nicolive" 41 | "${RUBY_EXE}" "${CONVERT_README_RB}" "${README_MD}" "${INSTALL_MD}" \ 42 | "${PACAKGE_NAME}/README.txt" 43 | 44 | zip -r "${PACAKGE_NAME}.zip" "${PACAKGE_NAME}" 45 | 46 | echo "##### Succeeded to create package. #####" 47 | echo Read ${PACAKGE_NAME}/README.txt if you want to install this plugin. 48 | echo And you can deploy the ${PACAKGE_NAME}.zip file! 49 | --------------------------------------------------------------------------------