Initial commit
This commit is contained in:
committed by
Brendan LE GLAUNEC
parent
2af5f4475e
commit
201d7e31c6
@@ -0,0 +1,123 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cachemanager.h" // for cache_manager
|
||||
#include <dlfcn.h> // for dlerror, dlclose, dlopen, dlsym, etc
|
||||
#include <logger.h> // for LOG_ERR_
|
||||
#include <stdbool.h> // for bool, false, true
|
||||
#include <algorithm> // for move
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace cameradar {
|
||||
|
||||
#ifdef __APPLE__
|
||||
const std::string cache_manager::PLUGIN_EXT = ".dylib";
|
||||
#elif __linux__
|
||||
const std::string cache_manager::PLUGIN_EXT = ".so";
|
||||
#endif
|
||||
|
||||
const std::string cache_manager::default_symbol = "cache_manager_instance_new";
|
||||
|
||||
cache_manager::cache_manager(const std::string& path,
|
||||
const std::string& name,
|
||||
const std::string& symbol)
|
||||
: name(name), path(path), symbol(symbol), handle(nullptr), ptr(nullptr) {}
|
||||
|
||||
cache_manager::cache_manager(cache_manager&& old)
|
||||
: path(std::move(old.path)), symbol(std::move(old.symbol)) {
|
||||
this->handle = old.handle;
|
||||
old.handle = nullptr;
|
||||
this->ptr = old.ptr;
|
||||
old.ptr = nullptr;
|
||||
}
|
||||
|
||||
cache_manager::~cache_manager() {
|
||||
if (this->ptr) {
|
||||
delete this->ptr;
|
||||
this->ptr = nullptr;
|
||||
}
|
||||
if (this->handle) { dlclose(handle); }
|
||||
}
|
||||
|
||||
bool
|
||||
cache_manager::make_instance() {
|
||||
cache_manager_iface* (*new_fn)() = nullptr;
|
||||
|
||||
// Gets the path to the dynamic library
|
||||
auto real_path = this->make_full_path();
|
||||
|
||||
// Opens it to get the handle
|
||||
this->handle = dlopen(real_path.c_str(), RTLD_LAZY);
|
||||
if (this->handle == nullptr) {
|
||||
std::cout << "error: " << dlerror() << std::endl;
|
||||
LOG_ERR_("Failed to load cache manager: " + this->name + ", invalid path",
|
||||
"cache manager loader");
|
||||
return false;
|
||||
} else {
|
||||
// Gets the symbol and checks if the library is valid
|
||||
*(void**)(&new_fn) = dlsym(this->handle, symbol.c_str());
|
||||
if (dlerror() != nullptr) {
|
||||
LOG_ERR_("Invalid cache manager package: " + this->name, "cache manager loader");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a string containing the most recent dl* error
|
||||
dlerror();
|
||||
|
||||
// Instantiates the cache manager
|
||||
this->ptr = (*new_fn)();
|
||||
if (this->ptr == nullptr) {
|
||||
LOG_ERR_("Invalid cache manager format: " + this->name, "cache manager loader");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generates a path as such : /libdumb_cache_manager.so
|
||||
std::string
|
||||
cache_manager::make_full_path() {
|
||||
std::string full_path = this->path;
|
||||
full_path += "/lib";
|
||||
full_path += this->name;
|
||||
full_path += "_cache_manager";
|
||||
full_path += PLUGIN_EXT;
|
||||
|
||||
return full_path;
|
||||
}
|
||||
|
||||
cache_manager_iface* cache_manager::operator->() { return this->ptr; }
|
||||
|
||||
const cache_manager_iface* cache_manager::operator->() const { return this->ptr; }
|
||||
|
||||
bool operator==(std::nullptr_t nullp, const cache_manager& p) { return p.ptr == nullp; }
|
||||
|
||||
bool operator==(const cache_manager& p, std::nullptr_t nullp) { return p.ptr == nullp; }
|
||||
|
||||
bool operator!=(std::nullptr_t nullp, const cache_manager& p) { return p.ptr != nullp; }
|
||||
|
||||
bool operator!=(const cache_manager& p, std::nullptr_t nullp) { return p.ptr != nullp; }
|
||||
|
||||
cache_manager_base&
|
||||
cache_manager_base::get_instance() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // cameradar
|
||||
|
||||
} // etix
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <fstream> // std::ifstream
|
||||
#include <unistd.h> // access, F_OK
|
||||
#include <configuration.h> // configuration
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace cameradar {
|
||||
|
||||
const std::string configuration::name_ = "configuration";
|
||||
|
||||
// read a file at the path "path"
|
||||
// if the file is available we return the whole content as an std::string inside
|
||||
// a pair
|
||||
// otherwise return false and an empty string inside a pair
|
||||
std::pair<bool, std::string>
|
||||
read_file(const std::string& path) {
|
||||
auto line = std::string{};
|
||||
auto content = std::string{};
|
||||
auto file = std::ifstream{ path };
|
||||
|
||||
if (file.is_open()) {
|
||||
while (getline(file, line)) { content += line + "\n"; }
|
||||
file.close();
|
||||
} else {
|
||||
return std::make_pair(false, std::string{});
|
||||
}
|
||||
|
||||
return std::make_pair(true, content);
|
||||
}
|
||||
|
||||
// Loads the IDS dictionary
|
||||
bool
|
||||
configuration::load_ids() {
|
||||
std::string content;
|
||||
|
||||
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration");
|
||||
if (this->rtsp_ids_file.size()) {
|
||||
content = read_file(this->rtsp_ids_file.c_str()).second;
|
||||
} else {
|
||||
LOG_WARN_(
|
||||
"No ids file detected in your configuration, Cameradar will use "
|
||||
"the default one "
|
||||
"instead.",
|
||||
"configuration");
|
||||
content = read_file(default_ids_file_path_).second;
|
||||
}
|
||||
if (content.size()) {
|
||||
auto root = Json::Value();
|
||||
auto reader = Json::Reader();
|
||||
reader.parse(content, root);
|
||||
|
||||
for (unsigned int i = 0; i < root["username"].size(); i++) {
|
||||
if (not root["username"][i].isString()) {
|
||||
LOG_ERR_("\"username\" should be of type string", "configuration");
|
||||
return false;
|
||||
}
|
||||
this->usernames.push_back(root["username"][i].asString());
|
||||
}
|
||||
for (unsigned int i = 0; i < root["password"].size(); i++) {
|
||||
if (not root["password"][i].isString()) {
|
||||
LOG_ERR_("\"password\" should be of type string", "configuration");
|
||||
return false;
|
||||
}
|
||||
this->passwords.push_back(root["password"][i].asString());
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR_(
|
||||
"Could not load ids file. Make sure you provided a valid path in your "
|
||||
"configuration file.",
|
||||
"configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Loads the URL dictionary
|
||||
bool
|
||||
configuration::load_url() {
|
||||
std::string content;
|
||||
|
||||
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration");
|
||||
if (this->rtsp_url_file.size()) {
|
||||
content = read_file(this->rtsp_url_file.c_str()).second;
|
||||
} else {
|
||||
LOG_WARN_(
|
||||
"No ids file detected in your configuration, Cameradar will use "
|
||||
"the default one "
|
||||
"instead.",
|
||||
"configuration");
|
||||
content = read_file(default_urls_file_path_).second;
|
||||
}
|
||||
if (content.size()) {
|
||||
auto root = Json::Value();
|
||||
auto reader = Json::Reader();
|
||||
reader.parse(content, root);
|
||||
// auto result = tool::json::check_fields(
|
||||
// {{"urls", Json::arrayValue, root["urls"]}}, "general
|
||||
// configuration");
|
||||
|
||||
// if (not result.first) {
|
||||
// LOG_ERR_(result.second, "general configuration");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
for (unsigned int i = 0; i < root["urls"].size(); i++) {
|
||||
if (not root["urls"][i].isString()) {
|
||||
LOG_ERR_("\"urls\" should be of type string", "configuration");
|
||||
return false;
|
||||
}
|
||||
this->paths.push_back(root["urls"][i].asString());
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR_(
|
||||
"Could not load ids file. Make sure you provided a valid path in your "
|
||||
"configuration file.",
|
||||
"configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, configuration>
|
||||
serialize(const Json::Value& root) {
|
||||
std::pair<bool, configuration> ret;
|
||||
|
||||
try {
|
||||
ret.second.ports = root["ports"].asString();
|
||||
ret.second.subnets = root["subnets"].asString();
|
||||
ret.second.rtsp_ids_file = root["rtsp_ids_file"].asString();
|
||||
ret.second.rtsp_url_file = root["rtsp_url_file"].asString();
|
||||
ret.second.thumbnail_storage_path = root["thumbnail_storage_path"].asString();
|
||||
ret.second.cache_manager_path = root["cache_manager_path"].asString();
|
||||
ret.second.cache_manager_name = root["cache_manager_name"].asString();
|
||||
ret.first = true;
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERR_("Configuration failed : " + std::string(e.what()), "configuration");
|
||||
ret.first = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
configuration::get_raw() const {
|
||||
return this->raw_conf;
|
||||
}
|
||||
|
||||
// Loads the configuration from a path
|
||||
// Returns a pair containing a boolean value & the configuration.
|
||||
// Will return true & valid configuration if success
|
||||
// Otherwise false & empty configuration
|
||||
std::pair<bool, configuration>
|
||||
load(const std::string& path) {
|
||||
// Check if the file exists at the given path
|
||||
if (access(path.c_str(), F_OK) == -1) {
|
||||
LOG_ERR_("Can't access: " + path, "configuration");
|
||||
return std::make_pair(false, configuration{});
|
||||
}
|
||||
|
||||
// Get the content of the file
|
||||
auto content = read_file(path);
|
||||
if (not content.first) {
|
||||
LOG_ERR_(
|
||||
"Can't open configuration file, you should check your rights to "
|
||||
"access the file",
|
||||
"configuration");
|
||||
return std::make_pair(false, configuration{});
|
||||
}
|
||||
|
||||
// Parse & validate the json
|
||||
auto root = Json::Value();
|
||||
|
||||
auto reader = Json::Reader();
|
||||
auto parse_succes = reader.parse(content.second, root);
|
||||
if (not parse_succes) {
|
||||
LOG_ERR_("Can't load configuration, invalid json format:\n" +
|
||||
reader.getFormattedErrorMessages(),
|
||||
"configuration");
|
||||
return std::make_pair(false, configuration{});
|
||||
}
|
||||
// Deserialize the json to a configuration struct
|
||||
// and return
|
||||
// REPLACE THIS WITH JSONCPP
|
||||
std::pair<bool, configuration> conf = serialize(root);
|
||||
conf.second.raw_conf = root;
|
||||
conf.first &= conf.second.load_url();
|
||||
conf.first &= conf.second.load_ids();
|
||||
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <describe.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Sends a request to the camera using the OPTION method,
|
||||
//! then a DESCRIBE to check for valid IDs
|
||||
//! then another DESCIBE with IDs if an authentication is needed
|
||||
bool
|
||||
curl_describe(const std::string& path, bool logs) {
|
||||
CURL* csession;
|
||||
CURLcode res;
|
||||
struct curl_slist* custom_msg = NULL;
|
||||
char URL[256];
|
||||
long rc;
|
||||
FILE* protofile = NULL;
|
||||
protofile = fopen("/dev/null", "wb");
|
||||
csession = curl_easy_init();
|
||||
if (csession == NULL) return -1;
|
||||
sprintf(URL, "%s", path.c_str());
|
||||
// These are the options for all following cURL requests
|
||||
// Activate verbose if debug is needed
|
||||
curl_easy_setopt(csession, CURLOPT_TIMEOUT, 1);
|
||||
curl_easy_setopt(csession, CURLOPT_NOBODY, 1);
|
||||
curl_easy_setopt(csession, CURLOPT_URL, URL);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_STREAM_URI, URL);
|
||||
curl_easy_setopt(csession, CURLOPT_FOLLOWLOCATION, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_HEADER, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_INTERLEAVEDATA, protofile);
|
||||
curl_easy_setopt(csession, CURLOPT_VERBOSE, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// This request will handshake the stream's server, it should always return 200 OK
|
||||
curl_easy_perform(csession);
|
||||
curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
custom_msg = curl_slist_append(
|
||||
custom_msg, "Accept: application/x-rtsp-mh, application/rtsl, application/sdp");
|
||||
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// This request will check if the given path is right without the need of encrypted ids
|
||||
curl_easy_perform(
|
||||
csession); // will return 404 if no ids and bad route, 401 if ids, 200 is all ok
|
||||
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
unsigned long pos = path.find("@");
|
||||
if (pos != std::string::npos) {
|
||||
std::string encoded = etix::tool::encode::encode64(path.substr(7, pos - 7));
|
||||
custom_msg =
|
||||
curl_slist_append(custom_msg, std::string("Authorization: Basic " + encoded).c_str());
|
||||
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// This request will check if the given ids are good
|
||||
curl_easy_perform(csession); // will return 404 if good ids, 401 if bad ids
|
||||
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
}
|
||||
curl_easy_cleanup(csession);
|
||||
fclose(protofile);
|
||||
curl_global_cleanup();
|
||||
if (logs) {
|
||||
if (rc != 401 && pos == std::string::npos)
|
||||
LOG_INFO_("Unprotected camera discovered.", "brutelogs");
|
||||
return ((res == CURLE_OK) && rc != 401);
|
||||
}
|
||||
return ((res == CURLE_OK) && rc != 404);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dispatcher.h"
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
// The main loop of the binary
|
||||
void
|
||||
dispatcher::run() {
|
||||
std::thread worker(&dispatcher::do_stuff, this);
|
||||
using namespace std::chrono_literals;
|
||||
// catch CTRL+C signal
|
||||
signal_handler::instance();
|
||||
|
||||
// wait for event or end
|
||||
while (signal_handler::instance().should_stop() not_eq stop_priority::stop &&
|
||||
current != task::finished) {
|
||||
std::this_thread::sleep_for(30ms);
|
||||
}
|
||||
|
||||
if (doing_stuff()) {
|
||||
LOG_INFO_("Waiting for a task to terminate", "dispatcher");
|
||||
LOG_INFO_("Press CTRL+C again to force stop", "dispatcher");
|
||||
}
|
||||
|
||||
// waiting for task to cleanup / force stop command
|
||||
while ((signal_handler::instance().should_stop() not_eq stop_priority::force_stop) and
|
||||
doing_stuff()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||
}
|
||||
worker.join();
|
||||
}
|
||||
|
||||
//! This loop is used to add all the tasks specified in the command line
|
||||
//! And then run them successively
|
||||
void
|
||||
dispatcher::do_stuff() {
|
||||
if (opts.second.exist("-d")) {
|
||||
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
|
||||
}
|
||||
if (opts.second.exist("-b")) {
|
||||
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
|
||||
}
|
||||
if (opts.second.exist("-t")) {
|
||||
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
|
||||
}
|
||||
if (opts.second.exist("-g")) {
|
||||
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
|
||||
}
|
||||
if (!opts.second.exist("-d") && !opts.second.exist("-b") && !opts.second.exist("-t") &&
|
||||
!opts.second.exist("-g")) {
|
||||
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
|
||||
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
|
||||
}
|
||||
queue.push_back(new etix::cameradar::print(cache, conf, nmap_output));
|
||||
while (queue.size() > 0 && signal_handler::instance().should_stop() == stop_priority::running) {
|
||||
if (queue.front()->run())
|
||||
queue.pop_front();
|
||||
else {
|
||||
LOG_ERR_("An error occured in one of the tasks, Cameradar will now stop.", "dispatcher");
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->current = task::finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "encode.h"
|
||||
#include <ctype.h>
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace tool {
|
||||
|
||||
namespace encode {
|
||||
|
||||
std::string
|
||||
encode64(const std::string& str_to_encode) {
|
||||
return base64_encode(reinterpret_cast<const unsigned char*>(str_to_encode.c_str()),
|
||||
str_to_encode.length());
|
||||
}
|
||||
|
||||
std::string
|
||||
decode64(const std::string& str_to_decode) {
|
||||
return base64_decode(str_to_decode);
|
||||
}
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static inline bool
|
||||
is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
/* from external source */
|
||||
std::string
|
||||
base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
|
||||
|
||||
while ((i++ < 3)) ret += '=';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* from external source */
|
||||
std::string
|
||||
base64_decode(std::string const& encoded_string) {
|
||||
int in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
|
||||
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_];
|
||||
in_++;
|
||||
if (i == 4) {
|
||||
for (i = 0; i < 4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++) ret += char_array_3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) char_array_4[j] = 0;
|
||||
|
||||
for (j = 0; j < 4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
#include <vector> // for std::vector
|
||||
#include <sstream> // for std::stringstream
|
||||
#include <pwd.h> // for getpwuid, passwd
|
||||
#include <stddef.h> // for size_t
|
||||
#include <sys/stat.h> // for stat, mkdir, S_ISDIR
|
||||
#include <unistd.h> // for getuid
|
||||
#include <fstream> // for std::ifstream
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace tool {
|
||||
|
||||
std::vector<std::string>
|
||||
split(const std::string& s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(s);
|
||||
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) elems.push_back(item);
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
namespace fs {
|
||||
|
||||
fs_error
|
||||
is_folder(const std::string& folder) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(folder.c_str(), &sb) == 0) {
|
||||
if (S_ISDIR(sb.st_mode))
|
||||
return fs_error::is_dir;
|
||||
else
|
||||
return fs_error::is_not_dir;
|
||||
}
|
||||
return fs_error::dont_exist;
|
||||
}
|
||||
|
||||
bool
|
||||
get_or_create_folder(const std::string& folder) {
|
||||
bool status = false;
|
||||
|
||||
switch (is_folder(folder)) {
|
||||
case fs_error::is_dir: status = true; break;
|
||||
case fs_error::is_not_dir: status = false; break;
|
||||
case fs_error::dont_exist: status = create_recursive_folder(folder); break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool
|
||||
create_folder(const std::string& folder) {
|
||||
if (mkdir(folder.c_str(), 0755) == 0) { return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
create_recursive_folder(const std::string& folder) {
|
||||
auto path_elems = split(folder, '/');
|
||||
std::string current_path = folder[0] == '/' ? "/" : "";
|
||||
|
||||
for (const auto& elem : path_elems) {
|
||||
current_path += elem;
|
||||
|
||||
if (is_folder(current_path) == fs_error::dont_exist) create_folder(current_path);
|
||||
|
||||
current_path += '/';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_file_folder(std::string full_file_path) {
|
||||
//! remove ending slash
|
||||
if (full_file_path.back() == '/') full_file_path.pop_back();
|
||||
|
||||
size_t last_slash_position = full_file_path.find_last_of('/');
|
||||
|
||||
//! it there is no slash, there is no folder to return
|
||||
if (last_slash_position == std::string::npos) return "";
|
||||
|
||||
return std::string(full_file_path, 0, last_slash_position);
|
||||
}
|
||||
|
||||
std::string
|
||||
home(void) {
|
||||
struct passwd* passwdEnt = getpwuid(getuid());
|
||||
return { passwdEnt->pw_dir };
|
||||
}
|
||||
|
||||
bool
|
||||
copy(const std::string& src, const std::string& dst) {
|
||||
std::ifstream src_stream(src, std::ios::binary);
|
||||
std::ofstream dst_stream(dst, std::ios::binary);
|
||||
|
||||
if (not src_stream.is_open()) return false;
|
||||
|
||||
dst_stream << src_stream.rdbuf();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // fs
|
||||
|
||||
} // tool
|
||||
|
||||
} // etix
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <launch_command.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Launches a command and checks for the return value
|
||||
bool
|
||||
launch_command(const std::string& cmd) {
|
||||
int status = system(cmd.c_str());
|
||||
if (status < 0) {
|
||||
LOG_ERR_("Error: " + std::string(strerror(errno)) + "", "dispatcher");
|
||||
return false;
|
||||
} else {
|
||||
if (WIFEXITED(status)) {
|
||||
LOG_DEBUG_("Program returned normally, exit code " +
|
||||
std::to_string(WEXITSTATUS(status)),
|
||||
"dispatcher");
|
||||
return true;
|
||||
} else
|
||||
LOG_WARN_("Program exited abnormaly.", "dispatcher");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <fs.h> // fs::home
|
||||
#include <opt_parse.h> // parsing opt
|
||||
#include <dispatcher.h> // program loop
|
||||
|
||||
namespace cmrdr = etix::cameradar;
|
||||
|
||||
// Command line parsing
|
||||
std::pair<bool, etix::tool::opt_parse>
|
||||
parse_cmdline(int argc, char* argv[]) {
|
||||
auto opt_parse = etix::tool::opt_parse{ argc, argv };
|
||||
|
||||
opt_parse.optional("-c", "Path to the configuration file (-c /path/to/conf)", true);
|
||||
opt_parse.optional("-l", "Set log level (-l 4 will only show warnings and errors)", true);
|
||||
opt_parse.optional("-d", "Launch the discovery tool on the given subnet", false);
|
||||
opt_parse.optional("-b", "Launch the bruteforce tool on all discovered devices", false);
|
||||
opt_parse.optional("-t", "Generate thumbnails from detected cameras", false);
|
||||
opt_parse.optional("-g", "Check if the stream can be opened with GStreamer", false);
|
||||
opt_parse.optional("-v", "Display Cameradar's version", false);
|
||||
opt_parse.optional("-h", "Display this help", false);
|
||||
opt_parse.execute();
|
||||
|
||||
if (opt_parse.exist("-h")) {
|
||||
opt_parse.print_help();
|
||||
return std::make_pair(false, opt_parse);
|
||||
} else if (opt_parse.exist("-v")) {
|
||||
std::cout << "Cameradar 0.1" << std::endl;
|
||||
return std::make_pair(false, opt_parse);
|
||||
} else if (opt_parse.has_error()) {
|
||||
std::cout << "Usage: ./cameradar [option]\n\toptions:\n" << std::endl;
|
||||
opt_parse.print_help();
|
||||
return std::make_pair(false, opt_parse);
|
||||
}
|
||||
|
||||
return std::make_pair(true, opt_parse);
|
||||
}
|
||||
|
||||
// Check if a folder exists, is readable and writable
|
||||
bool
|
||||
check_folder(const std::string& path) {
|
||||
struct stat sb;
|
||||
|
||||
if ((stat(path.c_str(), &sb) == 0) && (S_ISDIR(sb.st_mode)) && (sb.st_mode & S_IRUSR) &&
|
||||
(sb.st_mode & S_IWUSR)) {
|
||||
LOG_INFO_("Folder " + path + " is available and has sufficient rights", "main");
|
||||
return true;
|
||||
}
|
||||
LOG_ERR_("Folder " + path + " has insufficient rights, please check your configuration",
|
||||
"main");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the storage path is available
|
||||
bool
|
||||
check_storage_path(const std::string& thumbnail_storage_path) {
|
||||
LOG_INFO_("Checking if storage path exists and are usable", "main");
|
||||
return (check_folder(thumbnail_storage_path));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[]) {
|
||||
etix::tool::logger::get_instance("cameradar");
|
||||
auto args = parse_cmdline(argc, argv);
|
||||
if (not args.first) return EXIT_FAILURE;
|
||||
|
||||
// configure file configuration path
|
||||
auto conf_path = std::string{};
|
||||
if (not args.second.exist("-c")) {
|
||||
conf_path = etix::cameradar::default_configuration_path;
|
||||
LOG_WARN_("No custom path set, trying to use default path: " + conf_path, "main");
|
||||
} else {
|
||||
conf_path = args.second["-c"];
|
||||
}
|
||||
|
||||
if (not args.second.exist("-l")) {
|
||||
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::INFO);
|
||||
LOG_INFO_("No log level set, using log level 2 (ignoring DEBUG)", "main");
|
||||
} else {
|
||||
try {
|
||||
int level = std::stoi(args.second["-l"]);
|
||||
etix::tool::logger::get_instance("cameradar")
|
||||
.set_level(static_cast<etix::tool::loglevel>(level));
|
||||
} catch (...) {
|
||||
LOG_ERR_("Invalid log level format, log level should be 1, 2, 4, 5 or 6", "main");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load the configuration
|
||||
auto conf = cmrdr::load(conf_path);
|
||||
if (not conf.first) { return EXIT_FAILURE; }
|
||||
|
||||
LOG_INFO_("Configuration successfully loaded", "main");
|
||||
|
||||
// If one of the path is invalid, exit
|
||||
auto paths_ok = check_storage_path(conf.second.thumbnail_storage_path);
|
||||
if (not paths_ok) { return EXIT_FAILURE; }
|
||||
|
||||
// Here we should get the cache manager but for now we will juste
|
||||
// make a dumb cache manager
|
||||
auto plug = std::make_shared<etix::cameradar::cache_manager>(conf.second.cache_manager_path,
|
||||
conf.second.cache_manager_name);
|
||||
|
||||
if (not plug->make_instance()) {
|
||||
LOG_ERR_(std::string("Invalid cache manager "), "cameradar");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO_("Launching Cameradar, press CTRL+C to gracefully stop", "main");
|
||||
|
||||
etix::cameradar::dispatcher disp(conf.second, plug, args);
|
||||
|
||||
disp.run();
|
||||
|
||||
LOG_WARN_("See ya !", "cameradar");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "opt_parse.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace tool {
|
||||
|
||||
opt_parse::opt_parse(int argc, char* argv[]) : argc(argc), argv(argv) {}
|
||||
|
||||
opt_parse::~opt_parse() {}
|
||||
|
||||
void
|
||||
opt_parse::required(const std::string& name, const std::string& desc, bool need_arg) {
|
||||
this->params.emplace(name, opt_param(true, need_arg, name, desc));
|
||||
}
|
||||
|
||||
void
|
||||
opt_parse::optional(const std::string& name, const std::string& desc, bool need_arg) {
|
||||
this->params.emplace(name, opt_param(false, need_arg, name, desc));
|
||||
}
|
||||
|
||||
bool
|
||||
opt_parse::execute() {
|
||||
int i = 1;
|
||||
|
||||
// if params are invalid
|
||||
if (this->argc < 1 || not this->argv) { return false; }
|
||||
|
||||
while (i != this->argc) {
|
||||
// there is less argument than argc
|
||||
if (not this->argv[i]) { return false; }
|
||||
auto params = this->params.find(std::string(this->argv[i]));
|
||||
if (params != this->params.end()) {
|
||||
this->params_cnt += 1;
|
||||
(*params).second.is_passed = true;
|
||||
if ((*params).second.need_arg == true && (i + 1) != this->argc) {
|
||||
(*params).second.argument = this->argv[i + 1];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
opt_parse::iterator
|
||||
opt_parse::begin() const {
|
||||
std::vector<std::pair<std::string, std::string>> p;
|
||||
|
||||
for (auto entry : this->params) {
|
||||
p.push_back(std::make_pair(entry.second.name, entry.second.argument));
|
||||
}
|
||||
return iterator(p, 0);
|
||||
}
|
||||
|
||||
opt_parse::iterator
|
||||
opt_parse::end() const {
|
||||
return iterator(std::vector<std::pair<std::string, std::string>>(), this->params_cnt);
|
||||
}
|
||||
|
||||
void
|
||||
opt_parse::print_usage() const {
|
||||
std::cout << "Usage: " << this->argv[0];
|
||||
|
||||
for (auto entry : this->params) {
|
||||
if (entry.second.required == true) {
|
||||
if (entry.second.need_arg == true) { std::cout << " <arg>"; }
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
opt_parse::print_help() const {
|
||||
std::cout << "help: " << this->argv[0] << std::endl;
|
||||
|
||||
for (auto entry : this->params) {
|
||||
std::cout << entry.second.name << " " << entry.second.desc << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
opt_parse::has_error() const {
|
||||
for (auto entry : this->params) {
|
||||
// is the parameter required ?
|
||||
// the parameter need arguement ?
|
||||
if ((entry.second.required == true && entry.second.is_passed == false) ||
|
||||
(entry.second.is_passed == true && entry.second.need_arg == true &&
|
||||
entry.second.argument == "")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
opt_parse::exist(const std::string& opt) const {
|
||||
auto params = this->params.find(opt);
|
||||
|
||||
if (params == this->params.end()) { return false; }
|
||||
|
||||
return (*params).second.is_passed;
|
||||
}
|
||||
|
||||
std::string opt_parse::operator[](const std::string& opt) const {
|
||||
std::string param("");
|
||||
|
||||
auto opt_param = this->params.find(opt);
|
||||
if (opt_param != this->params.end()) { param = (*opt_param).second.argument; }
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
} // tool
|
||||
|
||||
} // etix
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <rtsp_path.h>
|
||||
#include <logger.h>
|
||||
|
||||
namespace etix {
|
||||
|
||||
namespace cameradar {
|
||||
|
||||
const std::string
|
||||
make_path(const stream_model& model) {
|
||||
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
|
||||
model.address + ":" + std::to_string(model.port) + model.route);
|
||||
LOG_DEBUG_(ret, "debug");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <signal_handler.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
event_handler signal_handler::handler;
|
||||
|
||||
signal_handler::signal_handler() {}
|
||||
|
||||
void
|
||||
signal_handler::call_handler(int signum) {
|
||||
handler.handle_signal(signum);
|
||||
}
|
||||
|
||||
signal_handler&
|
||||
signal_handler::instance(void) {
|
||||
static signal_handler singleton;
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = call_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
return singleton;
|
||||
}
|
||||
|
||||
etix::cameradar::stop_priority
|
||||
signal_handler::should_stop(void) const {
|
||||
return handler.should_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stream_model.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
Json::Value
|
||||
deserialize(const stream_model& model) {
|
||||
Json::Value ret;
|
||||
|
||||
ret["address"] = model.address;
|
||||
ret["port"] = model.port;
|
||||
ret["username"] = model.username;
|
||||
ret["password"] = model.password;
|
||||
ret["route"] = model.route;
|
||||
ret["service_name"] = model.service_name;
|
||||
ret["product"] = model.product;
|
||||
ret["protocol"] = model.protocol;
|
||||
ret["state"] = model.state;
|
||||
ret["path_found"] = model.path_found;
|
||||
ret["ids_found"] = model.ids_found;
|
||||
ret["thumbnail_path"] = model.thumbnail_path;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/brutelogs.h>
|
||||
#include <cachemanager.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
static const std::string no_ids_warning_ =
|
||||
"The ids.json files' default paths didn't match with the discovered "
|
||||
"cameras. Either "
|
||||
"they have custom ids, or your ids.json file does not contain enough "
|
||||
"default routes. "
|
||||
"Path bruteforce is impossible without the IDs.";
|
||||
|
||||
//! Tries to match the detected combination of Username / Password
|
||||
//! with the camera stream. Creates a resource in the DB upon
|
||||
//! valid discovery
|
||||
bool
|
||||
brutelogs::test_ids(const etix::cameradar::stream_model& stream,
|
||||
const std::string& password,
|
||||
const std::string& username) const {
|
||||
bool found = false;
|
||||
std::string path = stream.service_name + "://";
|
||||
if (username != "" || password != "") { path += username + ":" + password + "@"; }
|
||||
path += stream.address + ":" + std::to_string(stream.port);
|
||||
LOG_DEBUG_("Testing ids : " + path, "bruteforce");
|
||||
try {
|
||||
if (curl_describe(path, true)) {
|
||||
LOG_DEBUG_("[FOUND IDS] : " + path, "bruteforce");
|
||||
found = true;
|
||||
stream_model newstream{
|
||||
stream.address, stream.port, username, password,
|
||||
stream.route, stream.service_name, stream.product, stream.protocol,
|
||||
stream.state, stream.path_found, true, stream.thumbnail_path
|
||||
};
|
||||
(*cache)->update_stream(newstream);
|
||||
} else {
|
||||
stream_model newstream{ stream.address, stream.port, username,
|
||||
password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
stream.path_found, false, stream.thumbnail_path };
|
||||
(*cache)->update_stream(newstream);
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_DEBUG_("Ids already tested : " + std::string(e.what()), "bruteforce");
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool
|
||||
ids_already_found(std::vector<stream_model> streams, stream_model stream) {
|
||||
for (const auto& it : streams) {
|
||||
if ((stream.address == it.address) && (stream.port == it.port) && it.ids_found) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Tries to discover the right IDs on all RTSP streams in DB
|
||||
//! Uses the ids.json file to try different combinations
|
||||
bool
|
||||
brutelogs::run() const {
|
||||
LOG_INFO_(
|
||||
"Beginning bruteforce of the usernames and passwords task, it may "
|
||||
"take a while.",
|
||||
"bruteforce");
|
||||
std::vector<etix::cameradar::stream_model> streams = (*cache)->get_streams();
|
||||
bool doubleskip;
|
||||
size_t found = 0;
|
||||
for (const auto& stream : streams) {
|
||||
doubleskip = false;
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if ((found < streams.size()) && ids_already_found(streams, stream)) {
|
||||
LOG_INFO_(stream.address +
|
||||
" : This camera's ids were already discovered in "
|
||||
"the database. Skipping to "
|
||||
"the next camera.",
|
||||
"bruteforce");
|
||||
++found;
|
||||
} else {
|
||||
for (const auto& username : conf.usernames) {
|
||||
if (doubleskip ||
|
||||
signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
for (const auto& password : conf.passwords) {
|
||||
if (doubleskip ||
|
||||
signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if (test_ids(stream, password, username)) {
|
||||
++found;
|
||||
doubleskip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
LOG_WARN_(no_ids_warning_, "bruteforce");
|
||||
return false;
|
||||
} else
|
||||
LOG_INFO_("Found " + std::to_string(found) + " ids for " + std::to_string(streams.size()) +
|
||||
" cameras",
|
||||
"bruteforce");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2016 Etix Labs - All Rights Reserved.
|
||||
// All information contained herein is, and remains the property of Etix Labs
|
||||
// and its suppliers,
|
||||
// if any. The intellectual and technical concepts contained herein are
|
||||
// proprietary to Etix Labs
|
||||
// Dissemination of this information or reproduction of this material is
|
||||
// strictly forbidden unless
|
||||
// prior written permission is obtained from Etix Labs.
|
||||
|
||||
#include <tasks/brutepath.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
static const std::string no_route_found_ =
|
||||
"The url.json files' default paths didn't match with the discovered "
|
||||
"cameras. Either "
|
||||
"they have a custom path, or your url.json file does not contain enough "
|
||||
"default "
|
||||
"routes. Thumbnail generation is impossible without the path.";
|
||||
|
||||
//! Tries to match the detected combination of Username / Password
|
||||
//! with a route for the camera stream. Creates a resource in the DB upon
|
||||
//! valid discovery
|
||||
bool
|
||||
brutepath::test_path(const stream_model& stream, const std::string& route) const {
|
||||
bool found = false;
|
||||
std::string path = stream.service_name + "://" + stream.username + ":" + stream.password + "@" +
|
||||
stream.address + ":" + std::to_string(stream.port);
|
||||
if (route.front() != '/') { path += "/"; }
|
||||
path += route;
|
||||
LOG_DEBUG_("Testing path : " + path, "brutepath");
|
||||
try {
|
||||
if (curl_describe(path, false)) {
|
||||
// insert in DB and go to the next port, print a cool message
|
||||
found = true;
|
||||
LOG_INFO_("Discovered a valid path : [" + path + "]", "brutepath");
|
||||
stream_model newstream{
|
||||
stream.address, stream.port, stream.username, stream.password, route,
|
||||
stream.service_name, stream.product, stream.protocol, stream.state, true,
|
||||
stream.ids_found, stream.thumbnail_path
|
||||
};
|
||||
(*cache)->update_stream(newstream);
|
||||
} else {
|
||||
stream_model newstream{
|
||||
stream.address, stream.port, stream.username, stream.password, route,
|
||||
stream.service_name, stream.product, stream.protocol, stream.state, false,
|
||||
stream.ids_found, stream.thumbnail_path
|
||||
};
|
||||
(*cache)->update_stream(newstream);
|
||||
}
|
||||
} catch (const std::runtime_error& e) { LOG_INFO_(e.what(), "brutepath"); }
|
||||
return found;
|
||||
}
|
||||
|
||||
bool
|
||||
path_already_found(std::vector<stream_model> streams, stream_model model) {
|
||||
for (const auto& stream : streams) {
|
||||
if ((model.address == stream.address) && (model.port == stream.port) && stream.path_found)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Tries to discover a route on all RTSP streams in DB
|
||||
//! Uses the url.json file to try different routes
|
||||
bool
|
||||
brutepath::run() const {
|
||||
LOG_INFO_("Beginning bruteforce of the camera paths task, it may take a while.", "bruteforce");
|
||||
std::vector<stream_model> streams = (*cache)->get_streams();
|
||||
int found = 0;
|
||||
for (const auto& stream : streams) {
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if (path_already_found(streams, stream)) {
|
||||
LOG_INFO_(stream.address +
|
||||
" : This camera's path was already discovered in the database."
|
||||
"Skipping to the next camera.",
|
||||
"brutepath");
|
||||
++found;
|
||||
} else {
|
||||
for (const auto& route : conf.paths) {
|
||||
if (signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if (test_path(stream, route)) {
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
LOG_WARN_(no_route_found_, "brutepath");
|
||||
|
||||
} else
|
||||
LOG_INFO_("Found " + std::to_string(found) + " routes for " +
|
||||
std::to_string(streams.size()) + " cameras",
|
||||
"brutepath");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/mapping.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! The first command checks if dpkg finds nmap in the system by cutting the
|
||||
//! result and grepping
|
||||
//! nmap from it.
|
||||
//!
|
||||
//! The second command checks the version of nmap, right now it needs to be the
|
||||
//! 6.47 but this could
|
||||
//! be changed to 6 or greater depending on the needs. In a docker container
|
||||
//! this should not be a
|
||||
//! problem.
|
||||
bool
|
||||
nmap_is_ok() {
|
||||
return (launch_command("test `dpkg -l | cut -c 5-9 | grep nmap` = nmap")
|
||||
// && launch_command("test `nmap --version | cut -c 14-18 | head -n2 | tail -n1` = 6.47")
|
||||
&& launch_command("mkdir -p scans")); // Creates the directory in which the scans will be stored
|
||||
}
|
||||
|
||||
//! Launches and checks the return of the nmap command
|
||||
//! Uses the subnets specified in the conf file to launch nmap
|
||||
bool
|
||||
mapping::run() const {
|
||||
if (nmap_is_ok()) {
|
||||
std::string subnets = this->conf.subnets;
|
||||
std::replace(subnets.begin(), subnets.end(), ',', ' ');
|
||||
LOG_INFO_("Nmap 6.0 or greater found", "mapping");
|
||||
LOG_INFO_("Beginning mapping task. This may take a while.", "mapping");
|
||||
std::string cmd =
|
||||
"nmap -T4 -A " + subnets + " -p " + this->conf.ports + " -oX " + nmap_output;
|
||||
bool ret = launch_command(cmd);
|
||||
if (ret)
|
||||
LOG_INFO_("Nmap XML output successfully generated in file: " + nmap_output, "mapping");
|
||||
else
|
||||
LOG_ERR_("Nmap command failed", "mapping");
|
||||
return ret;
|
||||
} else {
|
||||
LOG_ERR_("Nmap 6.0 or greater is required to launch Cameradar", "mapping");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/parsing.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
static const std::string no_hosts_found_ =
|
||||
"No hosts were discovered on your network. Please check your internet "
|
||||
"connexion "
|
||||
"and verify that the subnetworks you specified in your configuration file "
|
||||
"were "
|
||||
"accessible";
|
||||
|
||||
//! Avoids segfaults on unknown xml structure
|
||||
std::string
|
||||
xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
|
||||
if (elem == nullptr) return "Closed";
|
||||
if (elem->Attribute(attr.c_str()) != nullptr) return std::string(elem->Attribute(attr.c_str()));
|
||||
return "Closed";
|
||||
}
|
||||
|
||||
//! Parse a single host node (generally containing only one camera)
|
||||
//! Pushes it back to the data structure
|
||||
void
|
||||
parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) const {
|
||||
TiXmlElement* xml_streams = xml_host->FirstChild("ports")->ToElement();
|
||||
stream_model stream;
|
||||
for (TiXmlElement* xml_stream = xml_streams->FirstChild("port")->ToElement(); xml_stream;
|
||||
xml_stream = xml_stream->NextSiblingElement("port")) {
|
||||
stream.address = xml_safe_get(xml_host->FirstChild("address")->ToElement(), "addr");
|
||||
stream.protocol = xml_safe_get(xml_stream, "protocol");
|
||||
stream.port = static_cast<unsigned short>(std::stoi(xml_safe_get(xml_stream, "portid")));
|
||||
TiXmlElement* state = xml_stream->FirstChild("state")->ToElement();
|
||||
stream.state = xml_safe_get(state, "state");
|
||||
TiXmlElement* service;
|
||||
if (state->NextSibling("service") &&
|
||||
(service = state->NextSibling("service")->ToElement())) {
|
||||
stream.service_name = xml_safe_get(service, "name");
|
||||
stream.product = xml_safe_get(service, "product");
|
||||
} else {
|
||||
stream.service_name = "Closed";
|
||||
stream.product = "Closed";
|
||||
}
|
||||
data.push_back(stream);
|
||||
}
|
||||
}
|
||||
|
||||
//! Prints all detected cameras into the data structure and stops the program if
|
||||
//! no open RTSP streams were found
|
||||
bool
|
||||
parsing::print_detected_cameras(const std::vector<stream_model>& data) const {
|
||||
int added = 0;
|
||||
for (const auto& stream : data) {
|
||||
if (!stream.service_name.compare("rtsp") && !stream.state.compare("open")) {
|
||||
try {
|
||||
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(),
|
||||
"print");
|
||||
added++;
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_WARN_("Port already scanned : " + std::string(e.what()), "parsing");
|
||||
added++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
LOG_WARN_(
|
||||
"Mapping unsuccessful, no rtsp streams were discovered. You "
|
||||
"should try other "
|
||||
"subnetworks",
|
||||
"parsing");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO_("Mapping successfuly ended, " + std::to_string(added) +
|
||||
" RTSP streams were discovered.",
|
||||
"parsing");
|
||||
(*cache)->set_streams(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Opens the nmap output file, parses the data of each discovered port
|
||||
//! Adds the RTSP ports only into the DB
|
||||
bool
|
||||
parsing::run() const {
|
||||
std::vector<stream_model> data;
|
||||
try {
|
||||
TiXmlDocument doc(nmap_output.c_str());
|
||||
doc.LoadFile();
|
||||
TiXmlHandle docHandle(&doc);
|
||||
|
||||
TiXmlElement* nmaprun = docHandle.FirstChild("nmaprun").ToElement();
|
||||
TiXmlNode* xml_node = nmaprun->FirstChild("host");
|
||||
if (xml_node == NULL) return false;
|
||||
TiXmlElement* xml_host;
|
||||
if ((xml_host = xml_node->ToElement()) && xml_host->Attribute("endtime"))
|
||||
for (xml_host = xml_node->ToElement(); xml_host;
|
||||
xml_host = xml_host->NextSiblingElement("host")) {
|
||||
parse_camera(xml_host, data);
|
||||
}
|
||||
else
|
||||
LOG_WARN_(no_hosts_found_, "parsing");
|
||||
if (data.size() == 0) { LOG_WARN_("No cameras were discovered", "parsing"); }
|
||||
return print_detected_cameras(data);
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERR_("Error during parsing. brutepath aborted : " + std::string(e.what()), "parsing");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/print.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Launches and checks the return of the nmap command
|
||||
//! Uses the subnets specified in the conf file to launch nmap
|
||||
bool
|
||||
print::run() const {
|
||||
std::vector<stream_model> results = (*cache)->get_valid_streams();
|
||||
std::ofstream file;
|
||||
bool first = true;
|
||||
file.open("result.json");
|
||||
file << "[\n";
|
||||
for (const auto& stream : results) {
|
||||
LOG_INFO_("Found a valid RTSP Stream and generated a thumbnail at : " +
|
||||
stream.thumbnail_path,
|
||||
"print");
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
file << ",";
|
||||
file << deserialize(stream).toStyledString();
|
||||
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(), "print");
|
||||
}
|
||||
file << "\n]";
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/stream_check.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Gets all the discovered streams with good routes and logs
|
||||
//! And launches an ffmpeg command to generate a thumbnail
|
||||
//! In order to check for the stream validity
|
||||
bool
|
||||
stream_check::run() const {
|
||||
GstElement* pipeline;
|
||||
GstElement* elem;
|
||||
|
||||
gst_init(nullptr, nullptr);
|
||||
|
||||
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
||||
|
||||
for (const auto& stream : streams) {
|
||||
GError* error = NULL;
|
||||
|
||||
pipeline =
|
||||
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
|
||||
|
||||
if (pipeline == NULL) {
|
||||
LOG_ERR_("[" + stream.address + "] Can't configure pipeline", "stream_check");
|
||||
return false;
|
||||
} else {
|
||||
elem = gst_bin_get_by_name(GST_BIN(pipeline), "source");
|
||||
g_object_set(G_OBJECT(elem), "location", stream.address, "latency", 20, NULL);
|
||||
|
||||
if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
|
||||
LOG_ERR_(
|
||||
"This stream is unaccessible with GStreamer, there must be a "
|
||||
"configuration issue",
|
||||
"stream_check");
|
||||
gst_object_unref(pipeline);
|
||||
stream_model invalidstream{
|
||||
stream.address, stream.port, stream.username, stream.password,
|
||||
stream.route, stream.service_name, stream.product, stream.protocol,
|
||||
"invalid stream", stream.path_found, stream.ids_found, stream.thumbnail_path
|
||||
};
|
||||
(*cache)->update_stream(invalidstream);
|
||||
return false;
|
||||
}
|
||||
LOG_INFO_("[" + stream.address + "] Set pipeline to playing", "stream_check");
|
||||
}
|
||||
}
|
||||
LOG_INFO_("All streams could be accessed with GStreamer", "stream_check");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2016 Etix Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <tasks/thumbnail.h>
|
||||
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
std::string
|
||||
remove_trailing_backslash(std::string s) {
|
||||
while (s.back() == '/') { s.pop_back(); }
|
||||
return s;
|
||||
}
|
||||
|
||||
// Tranforms the path into a path for the thumbnail
|
||||
// Example :
|
||||
// rtsp://username:password@172.16.100.13/live.sdp
|
||||
// will become /storage/path/172.16.100.13/1345425533.jpg
|
||||
std::string
|
||||
thumbnail::build_output_file_path(const std::string& path) const {
|
||||
auto ss = std::stringstream{};
|
||||
|
||||
ss << remove_trailing_backslash(this->conf.thumbnail_storage_path);
|
||||
ss << "/";
|
||||
ss << path;
|
||||
ss << "/";
|
||||
ss << std::to_string(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
ss << ".jpg";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Gets all the discovered streams with good routes and logs
|
||||
//! And launches an ffmpeg command to generate a thumbnail
|
||||
//! In order to check for the stream validity
|
||||
bool
|
||||
thumbnail::run() const {
|
||||
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
||||
LOG_INFO_("Started thumbnail generation, it may take a while", "thumbnail");
|
||||
for (const auto& stream : streams) {
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
std::string ffmpeg_cmd =
|
||||
"mkdir -p %s ; "
|
||||
"ffmpeg "
|
||||
"-y "
|
||||
"-nostdin "
|
||||
"-loglevel quiet "
|
||||
"-i '%s' "
|
||||
"-vcodec mjpeg "
|
||||
"-vframes 1 "
|
||||
"-an "
|
||||
"-f image2 "
|
||||
"-s 320x240 "
|
||||
"'%s'";
|
||||
std::string fullpath = make_path(stream);
|
||||
std::string output = build_output_file_path(stream.address);
|
||||
ffmpeg_cmd = tool::fmt(ffmpeg_cmd.c_str(),
|
||||
output.substr(0, output.find_last_of("/")).c_str(),
|
||||
fullpath.c_str(),
|
||||
output.c_str());
|
||||
if (!launch_command(ffmpeg_cmd)) {
|
||||
LOG_WARN_("The following command [" + ffmpeg_cmd +
|
||||
"] didn't work. That can either mean that the stream is "
|
||||
"not valid or "
|
||||
"that there is a problem with the camera.",
|
||||
"thumbnail_generation");
|
||||
} else {
|
||||
LOG_DEBUG_("Generated thumbnail : " + ffmpeg_cmd, "thumbnail_generation");
|
||||
try {
|
||||
stream_model result{ stream.address, stream.port, stream.username,
|
||||
stream.password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
stream.path_found, stream.ids_found, output };
|
||||
(*cache)->update_stream(result);
|
||||
|
||||
} catch (std::exception& e) { LOG_DEBUG_(e.what(), "thumbnail_generation"); }
|
||||
}
|
||||
}
|
||||
LOG_INFO_("All thumbnails have been successfully generated in " +
|
||||
this->conf.thumbnail_storage_path,
|
||||
"thumbnail_generation");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#include "tinystr.h"
|
||||
|
||||
// Error value for find primitive
|
||||
const TiXmlString::size_type TiXmlString::npos = static_cast<TiXmlString::size_type>(-1);
|
||||
|
||||
// Null rep.
|
||||
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
|
||||
|
||||
void
|
||||
TiXmlString::reserve(size_type cap) {
|
||||
if (cap > capacity()) {
|
||||
TiXmlString tmp;
|
||||
tmp.init(length(), cap);
|
||||
memcpy(tmp.start(), data(), length());
|
||||
swap(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
TiXmlString&
|
||||
TiXmlString::assign(const char* str, size_type len) {
|
||||
size_type cap = capacity();
|
||||
if (len > cap || cap > 3 * (len + 8)) {
|
||||
TiXmlString tmp;
|
||||
tmp.init(len);
|
||||
memcpy(tmp.start(), str, len);
|
||||
swap(tmp);
|
||||
} else {
|
||||
memmove(start(), str, len);
|
||||
set_size(len);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TiXmlString&
|
||||
TiXmlString::append(const char* str, size_type len) {
|
||||
size_type newsize = length() + len;
|
||||
if (newsize > capacity()) { reserve(newsize + capacity()); }
|
||||
memmove(finish(), str, len);
|
||||
set_size(newsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TiXmlString operator+(const TiXmlString& a, const TiXmlString& b) {
|
||||
TiXmlString tmp;
|
||||
tmp.reserve(a.length() + b.length());
|
||||
tmp += a;
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator+(const TiXmlString& a, const char* b) {
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
|
||||
tmp.reserve(a.length() + b_len);
|
||||
tmp += a;
|
||||
tmp.append(b, b_len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator+(const char* a, const TiXmlString& b) {
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
|
||||
tmp.reserve(a_len + b.length());
|
||||
tmp.append(a, a_len);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#endif // TIXML_USE_STL
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include "tinyxml.h"
|
||||
|
||||
// The goal of the seperate error file is to make the first
|
||||
// step towards localization. tinyxml (currently) only supports
|
||||
// english error messages, but the could now be translated.
|
||||
//
|
||||
// It also cleans up the code a bit.
|
||||
//
|
||||
|
||||
const char* TiXmlBase::errorString[TiXmlBase::TIXML_ERROR_STRING_COUNT] = {
|
||||
"No error",
|
||||
"Error",
|
||||
"Failed to open file",
|
||||
"Error parsing Element.",
|
||||
"Failed to read Element name",
|
||||
"Error reading Element value.",
|
||||
"Error reading Attributes.",
|
||||
"Error: empty tag.",
|
||||
"Error reading end tag.",
|
||||
"Error parsing Unknown.",
|
||||
"Error parsing Comment.",
|
||||
"Error parsing Declaration.",
|
||||
"Error document empty.",
|
||||
"Error null (0) or unexpected EOF found in input stream.",
|
||||
"Error parsing CDATA.",
|
||||
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user