louloulibs is directly included, instead of being a submodule
Because this is a nightmare to manage
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
#include <utils/encoding.hpp>
|
||||
|
||||
#include <utils/scopeguard.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <iconv.h>
|
||||
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
|
||||
/**
|
||||
* The UTF-8-encoded character used as a place holder when a character conversion fails.
|
||||
* This is U+FFFD � "replacement character"
|
||||
*/
|
||||
static const char* invalid_char = "\xef\xbf\xbd";
|
||||
static const size_t invalid_char_len = 3;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
/**
|
||||
* Based on http://en.wikipedia.org/wiki/UTF-8#Description
|
||||
*/
|
||||
bool is_valid_utf8(const char* s)
|
||||
{
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
const unsigned char* str = reinterpret_cast<const unsigned char*>(s);
|
||||
|
||||
while (*str)
|
||||
{
|
||||
// 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
if ((str[0] & 0b11111000) == 0b11110000)
|
||||
{
|
||||
if (!str[1] || !str[2] || !str[3]
|
||||
|| ((str[1] & 0b11000000) != 0b10000000)
|
||||
|| ((str[2] & 0b11000000) != 0b10000000)
|
||||
|| ((str[3] & 0b11000000) != 0b10000000))
|
||||
return false;
|
||||
str += 4;
|
||||
}
|
||||
// 3 bytes: 1110xxx 10xxxxxx 10xxxxxx
|
||||
else if ((str[0] & 0b11110000) == 0b11100000)
|
||||
{
|
||||
if (!str[1] || !str[2]
|
||||
|| ((str[1] & 0b11000000) != 0b10000000)
|
||||
|| ((str[2] & 0b11000000) != 0b10000000))
|
||||
return false;
|
||||
str += 3;
|
||||
}
|
||||
// 2 bytes: 110xxxxx 10xxxxxx
|
||||
else if (((str[0]) & 0b11100000) == 0b11000000)
|
||||
{
|
||||
if (!str[1] ||
|
||||
((str[1] & 0b11000000) != 0b10000000))
|
||||
return false;
|
||||
str += 2;
|
||||
}
|
||||
// 1 byte: 0xxxxxxx
|
||||
else if ((str[0] & 0b10000000) != 0)
|
||||
return false;
|
||||
else
|
||||
str++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string remove_invalid_xml_chars(const std::string& original)
|
||||
{
|
||||
// The given string MUST be a valid utf-8 string
|
||||
unsigned char* res = new unsigned char[original.size()];
|
||||
ScopeGuard sg([&res]() { delete[] res;});
|
||||
|
||||
// pointer where we write valid chars
|
||||
unsigned char* r = res;
|
||||
|
||||
const unsigned char* str = reinterpret_cast<const unsigned char*>(original.c_str());
|
||||
std::bitset<20> codepoint;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
// 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
if ((str[0] & 0b11111000) == 0b11110000)
|
||||
{
|
||||
codepoint = ((str[0] & 0b00000111) << 18);
|
||||
codepoint |= ((str[1] & 0b00111111) << 12);
|
||||
codepoint |= ((str[2] & 0b00111111) << 6 );
|
||||
codepoint |= ((str[3] & 0b00111111) << 0 );
|
||||
if (codepoint.to_ulong() <= 0x10FFFF)
|
||||
{
|
||||
::memcpy(r, str, 4);
|
||||
r += 4;
|
||||
}
|
||||
str += 4;
|
||||
}
|
||||
// 3 bytes: 1110xxx 10xxxxxx 10xxxxxx
|
||||
else if ((str[0] & 0b11110000) == 0b11100000)
|
||||
{
|
||||
codepoint = ((str[0] & 0b00001111) << 12);
|
||||
codepoint |= ((str[1] & 0b00111111) << 6);
|
||||
codepoint |= ((str[2] & 0b00111111) << 0 );
|
||||
if (codepoint.to_ulong() <= 0xD7FF ||
|
||||
(codepoint.to_ulong() >= 0xE000 && codepoint.to_ulong() <= 0xFFFD))
|
||||
{
|
||||
::memcpy(r, str, 3);
|
||||
r += 3;
|
||||
}
|
||||
str += 3;
|
||||
}
|
||||
// 2 bytes: 110xxxxx 10xxxxxx
|
||||
else if (((str[0]) & 0b11100000) == 0b11000000)
|
||||
{
|
||||
// All 2 bytes char are valid, don't even bother calculating
|
||||
// the codepoint
|
||||
::memcpy(r, str, 2);
|
||||
r += 2;
|
||||
str += 2;
|
||||
}
|
||||
// 1 byte: 0xxxxxxx
|
||||
else if ((str[0] & 0b10000000) == 0)
|
||||
{
|
||||
codepoint = ((str[0] & 0b01111111));
|
||||
if (codepoint.to_ulong() == 0x09 ||
|
||||
codepoint.to_ulong() == 0x0A ||
|
||||
codepoint.to_ulong() == 0x0D ||
|
||||
codepoint.to_ulong() >= 0x20)
|
||||
{
|
||||
::memcpy(r, str, 1);
|
||||
r += 1;
|
||||
}
|
||||
str += 1;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Invalid UTF-8 passed to remove_invalid_xml_chars");
|
||||
}
|
||||
return std::string(reinterpret_cast<char*>(res), r-res);
|
||||
}
|
||||
|
||||
std::string convert_to_utf8(const std::string& str, const char* charset)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
const iconv_t cd = iconv_open("UTF-8", charset);
|
||||
if (cd == (iconv_t)-1)
|
||||
throw std::runtime_error("Cannot convert into UTF-8");
|
||||
|
||||
// Make sure cd is always closed when we leave this function
|
||||
ScopeGuard sg([&]{ iconv_close(cd); });
|
||||
|
||||
size_t inbytesleft = str.size();
|
||||
|
||||
// iconv will not attempt to modify this buffer, but some plateform
|
||||
// require a char** anyway
|
||||
#ifdef ICONV_SECOND_ARGUMENT_IS_CONST
|
||||
const char* inbuf_ptr = str.c_str();
|
||||
#else
|
||||
char* inbuf_ptr = const_cast<char*>(str.c_str());
|
||||
#endif
|
||||
|
||||
size_t outbytesleft = str.size() * 4;
|
||||
char* outbuf = new char[outbytesleft];
|
||||
char* outbuf_ptr = outbuf;
|
||||
|
||||
// Make sure outbuf is always deleted when we leave this function
|
||||
sg.add_callback([&]{ delete[] outbuf; });
|
||||
|
||||
bool done = false;
|
||||
while (done == false)
|
||||
{
|
||||
size_t error = iconv(cd, &inbuf_ptr, &inbytesleft, &outbuf_ptr, &outbytesleft);
|
||||
if ((size_t)-1 == error)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EILSEQ:
|
||||
// Invalid byte found. Insert a placeholder instead of the
|
||||
// converted character, jump one byte and continue
|
||||
memcpy(outbuf_ptr, invalid_char, invalid_char_len);
|
||||
outbuf_ptr += invalid_char_len;
|
||||
inbytesleft--;
|
||||
inbuf_ptr++;
|
||||
break;
|
||||
case EINVAL:
|
||||
// A multibyte sequence is not terminated, but we can't
|
||||
// provide any more data, so we just add a placeholder to
|
||||
// indicate that the character is not properly converted,
|
||||
// and we stop the conversion
|
||||
memcpy(outbuf_ptr, invalid_char, invalid_char_len);
|
||||
outbuf_ptr += invalid_char_len;
|
||||
outbuf_ptr++;
|
||||
done = true;
|
||||
break;
|
||||
case E2BIG:
|
||||
// This should never happen
|
||||
done = true;
|
||||
break;
|
||||
default:
|
||||
// This should happen even neverer
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The conversion finished without any error, stop converting
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
// Terminate the converted buffer, and copy that buffer it into the
|
||||
// string we return
|
||||
*outbuf_ptr = '\0';
|
||||
res = outbuf;
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace xep0106
|
||||
{
|
||||
static const std::map<const char, const std::string> encode_map = {
|
||||
{' ', "\\20"},
|
||||
{'"', "\\22"},
|
||||
{'&', "\\26"},
|
||||
{'\'',"\\27"},
|
||||
{'/', "\\2f"},
|
||||
{':', "\\3a"},
|
||||
{'<', "\\3c"},
|
||||
{'>', "\\3e"},
|
||||
{'@', "\\40"},
|
||||
};
|
||||
|
||||
void decode(std::string& s)
|
||||
{
|
||||
std::string::size_type pos;
|
||||
for (const auto& pair: encode_map)
|
||||
while ((pos = s.find(pair.second)) != std::string::npos)
|
||||
s.replace(pos, pair.second.size(),
|
||||
1, pair.first);
|
||||
}
|
||||
|
||||
void encode(std::string& s)
|
||||
{
|
||||
std::string::size_type pos;
|
||||
while ((pos = s.find_first_of(" \"&'/:<>@")) != std::string::npos)
|
||||
{
|
||||
auto it = encode_map.find(s[pos]);
|
||||
assert(it != encode_map.end());
|
||||
s.replace(pos, 1, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef ENCODING_INCLUDED
|
||||
# define ENCODING_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
/**
|
||||
* Returns true if the given null-terminated string is valid utf-8.
|
||||
*
|
||||
* Based on http://en.wikipedia.org/wiki/UTF-8#Description
|
||||
*/
|
||||
bool is_valid_utf8(const char* s);
|
||||
/**
|
||||
* Remove all invalid codepoints from the given utf-8-encoded string.
|
||||
* The value returned is a copy of the string, without the removed chars.
|
||||
*
|
||||
* See http://www.w3.org/TR/xml/#charsets for the list of valid characters
|
||||
* in XML.
|
||||
*/
|
||||
std::string remove_invalid_xml_chars(const std::string& original);
|
||||
/**
|
||||
* Convert the given string (encoded is "encoding") into valid utf-8.
|
||||
* If some decoding fails, insert an utf-8 placeholder character instead.
|
||||
*/
|
||||
std::string convert_to_utf8(const std::string& str, const char* encoding);
|
||||
}
|
||||
|
||||
namespace xep0106
|
||||
{
|
||||
/**
|
||||
* Decode and encode inplace.
|
||||
*/
|
||||
void decode(std::string&);
|
||||
void encode(std::string&);
|
||||
}
|
||||
|
||||
#endif // ENCODING_INCLUDED
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <config/config.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
void reload_process()
|
||||
{
|
||||
// Closing the config will just force it to be reopened the next time
|
||||
// a configuration option is needed
|
||||
Config::close();
|
||||
// Destroy the logger instance, to be recreated the next time a log
|
||||
// line needs to be written
|
||||
Logger::instance().reset();
|
||||
log_debug("Configuration and logger reloaded.");
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef RELOAD_HPP_INCLUDED
|
||||
#define RELOAD_HPP_INCLUDED
|
||||
|
||||
/**
|
||||
* Reload the server's configuration, and close the logger (so that it
|
||||
* closes its files etc, to take into account the new configuration)
|
||||
*/
|
||||
void reload_process();
|
||||
|
||||
#endif /* RELOAD_HPP_INCLUDED */
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <utils/revstr.hpp>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::string revstr(const std::string& original)
|
||||
{
|
||||
return {original.rbegin(), original.rend()};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef REVSTR_HPP_INCLUDED
|
||||
# define REVSTR_HPP_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::string revstr(const std::string& original);
|
||||
}
|
||||
|
||||
#endif // REVSTR_HPP_INCLUDED
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef SCOPEGUARD_HPP
|
||||
#define SCOPEGUARD_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* A class to be used to make sure some functions are called when the scope
|
||||
* is left, because they will be called in the ScopeGuard's destructor. It
|
||||
* can for example be used to delete some pointer whenever any exception is
|
||||
* called. Example:
|
||||
|
||||
* {
|
||||
* ScopeGuard scope;
|
||||
* int* number = new int(2);
|
||||
* scope.add_callback([number]() { delete number; });
|
||||
* // Do some other stuff with the number. But these stuff might throw an exception:
|
||||
* throw std::runtime_error("Some error not caught here, but in our caller");
|
||||
* return true;
|
||||
* }
|
||||
|
||||
* In this example, our pointer will always be deleted, even when the
|
||||
* exception is thrown. If we want the functions to be called only when the
|
||||
* scope is left because of an unexpected exception, we can use
|
||||
* ScopeGuard::disable();
|
||||
*/
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
class ScopeGuard
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The constructor can take a callback. But additional callbacks can be
|
||||
* added later with add_callback()
|
||||
*/
|
||||
explicit ScopeGuard(std::function<void()>&& func):
|
||||
enabled(true)
|
||||
{
|
||||
this->add_callback(std::move(func));
|
||||
}
|
||||
/**
|
||||
* default constructor, the scope guard is enabled but empty, use
|
||||
* add_callback()
|
||||
*/
|
||||
explicit ScopeGuard():
|
||||
enabled(true)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Call all callbacks in the desctructor, unless it has been disabled.
|
||||
*/
|
||||
~ScopeGuard()
|
||||
{
|
||||
if (this->enabled)
|
||||
for (auto& func: this->callbacks)
|
||||
func();
|
||||
}
|
||||
/**
|
||||
* Add a callback to be called in our destructor, one scope guard can be
|
||||
* used for more than one task, if needed.
|
||||
*/
|
||||
void add_callback(std::function<void()>&& func)
|
||||
{
|
||||
this->callbacks.emplace_back(std::move(func));
|
||||
}
|
||||
/**
|
||||
* Disable that scope guard, nothing will be done when the scope is
|
||||
* exited.
|
||||
*/
|
||||
void disable()
|
||||
{
|
||||
this->enabled = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool enabled;
|
||||
std::vector<std::function<void()>> callbacks;
|
||||
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(ScopeGuard&&) = delete;
|
||||
ScopeGuard(ScopeGuard&&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,154 @@
|
||||
/* This code is public-domain - it is based on libcrypt
|
||||
* placed in the public domain by Wei Dai and other contributors.
|
||||
*/
|
||||
|
||||
#include "sha1.hpp"
|
||||
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
const uint8_t sha1InitState[] = {
|
||||
0x01,0x23,0x45,0x67, // H0
|
||||
0x89,0xab,0xcd,0xef, // H1
|
||||
0xfe,0xdc,0xba,0x98, // H2
|
||||
0x76,0x54,0x32,0x10, // H3
|
||||
0xf0,0xe1,0xd2,0xc3 // H4
|
||||
};
|
||||
|
||||
void sha1_init(sha1nfo *s) {
|
||||
memcpy(s->state.b,sha1InitState,HASH_LENGTH);
|
||||
s->byteCount = 0;
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (number >> (32-bits)));
|
||||
}
|
||||
|
||||
void sha1_hashBlock(sha1nfo *s) {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=s->state.w[0];
|
||||
b=s->state.w[1];
|
||||
c=s->state.w[2];
|
||||
d=s->state.w[3];
|
||||
e=s->state.w[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = s->buffer.w[(i+13)&15] ^ s->buffer.w[(i+8)&15] ^ s->buffer.w[(i+2)&15] ^ s->buffer.w[i&15];
|
||||
s->buffer.w[i&15] = sha1_rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=sha1_rol32(a,5) + e + s->buffer.w[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=sha1_rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
s->state.w[0] += a;
|
||||
s->state.w[1] += b;
|
||||
s->state.w[2] += c;
|
||||
s->state.w[3] += d;
|
||||
s->state.w[4] += e;
|
||||
}
|
||||
|
||||
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
|
||||
s->buffer.b[s->bufferOffset ^ 3] = data;
|
||||
s->bufferOffset++;
|
||||
if (s->bufferOffset == BLOCK_LENGTH) {
|
||||
sha1_hashBlock(s);
|
||||
s->bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data) {
|
||||
++s->byteCount;
|
||||
sha1_addUncounted(s, data);
|
||||
}
|
||||
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len) {
|
||||
for (;len--;) sha1_writebyte(s, (uint8_t) *data++);
|
||||
}
|
||||
|
||||
void sha1_pad(sha1nfo *s) {
|
||||
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
sha1_addUncounted(s, 0x80);
|
||||
while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
|
||||
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
|
||||
sha1_addUncounted(s, 0); // So zero pad the top bits
|
||||
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
|
||||
sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
sha1_addUncounted(s, s->byteCount >> 13); // byte.
|
||||
sha1_addUncounted(s, s->byteCount >> 5);
|
||||
sha1_addUncounted(s, s->byteCount << 3);
|
||||
}
|
||||
|
||||
uint8_t* sha1_result(sha1nfo *s) {
|
||||
int i;
|
||||
// Pad to complete the last block
|
||||
sha1_pad(s);
|
||||
|
||||
// Swap byte order back
|
||||
for (i=0; i<5; i++) {
|
||||
uint32_t a,b;
|
||||
a=s->state.w[i];
|
||||
b=a<<24;
|
||||
b|=(a<<8) & 0x00ff0000;
|
||||
b|=(a>>8) & 0x0000ff00;
|
||||
b|=a>>24;
|
||||
s->state.w[i]=b;
|
||||
}
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return s->state.b;
|
||||
}
|
||||
|
||||
#define HMAC_IPAD 0x36
|
||||
#define HMAC_OPAD 0x5c
|
||||
|
||||
void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) {
|
||||
uint8_t i;
|
||||
memset(s->keyBuffer, 0, BLOCK_LENGTH);
|
||||
if (keyLength > BLOCK_LENGTH) {
|
||||
// Hash long keys
|
||||
sha1_init(s);
|
||||
for (;keyLength--;) sha1_writebyte(s, *key++);
|
||||
memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH);
|
||||
} else {
|
||||
// Block length keys are used as is
|
||||
memcpy(s->keyBuffer, key, keyLength);
|
||||
}
|
||||
// Start inner hash
|
||||
sha1_init(s);
|
||||
for (i=0; i<BLOCK_LENGTH; i++) {
|
||||
sha1_writebyte(s, s->keyBuffer[i] ^ HMAC_IPAD);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* sha1_resultHmac(sha1nfo *s) {
|
||||
uint8_t i;
|
||||
// Complete inner hash
|
||||
memcpy(s->innerHash,sha1_result(s),HASH_LENGTH);
|
||||
// Calculate outer hash
|
||||
sha1_init(s);
|
||||
for (i=0; i<BLOCK_LENGTH; i++) sha1_writebyte(s, s->keyBuffer[i] ^ HMAC_OPAD);
|
||||
for (i=0; i<HASH_LENGTH; i++) sha1_writebyte(s, s->innerHash[i]);
|
||||
return sha1_result(s);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* This code is public-domain - it is based on libcrypt
|
||||
* placed in the public domain by Wei Dai and other contributors.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
union _buffer {
|
||||
uint8_t b[BLOCK_LENGTH];
|
||||
uint32_t w[BLOCK_LENGTH/4];
|
||||
};
|
||||
|
||||
union _state {
|
||||
uint8_t b[HASH_LENGTH];
|
||||
uint32_t w[HASH_LENGTH/4];
|
||||
};
|
||||
|
||||
typedef struct sha1nfo {
|
||||
union _buffer buffer;
|
||||
uint8_t bufferOffset;
|
||||
union _state state;
|
||||
uint32_t byteCount;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
} sha1nfo;
|
||||
|
||||
void sha1_init(sha1nfo *s);
|
||||
void sha1_writebyte(sha1nfo *s, uint8_t data);
|
||||
void sha1_write(sha1nfo *s, const char *data, size_t len);
|
||||
uint8_t* sha1_result(sha1nfo *s);
|
||||
void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength);
|
||||
uint8_t* sha1_resultHmac(sha1nfo *s);
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <utils/split.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::vector<std::string> split(const std::string& s, const char delim, const bool allow_empty)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim))
|
||||
{
|
||||
if (item.empty() && !allow_empty)
|
||||
continue ;
|
||||
ret.emplace_back(std::move(item));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef SPLIT_INCLUDED
|
||||
# define SPLIT_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::vector<std::string> split(const std::string &s, const char delim, const bool allow_empty=true);
|
||||
}
|
||||
|
||||
#endif // SPLIT_INCLUDED
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <utils/timed_events.hpp>
|
||||
|
||||
TimedEvent::TimedEvent(std::chrono::steady_clock::time_point&& time_point,
|
||||
std::function<void()> callback, const std::string& name):
|
||||
time_point(std::move(time_point)),
|
||||
callback(callback),
|
||||
repeat(false),
|
||||
repeat_delay(0),
|
||||
name(name)
|
||||
{
|
||||
}
|
||||
|
||||
TimedEvent::TimedEvent(std::chrono::milliseconds&& duration,
|
||||
std::function<void()> callback, const std::string& name):
|
||||
time_point(std::chrono::steady_clock::now() + duration),
|
||||
callback(callback),
|
||||
repeat(true),
|
||||
repeat_delay(std::move(duration)),
|
||||
name(name)
|
||||
{
|
||||
}
|
||||
|
||||
TimedEvent::TimedEvent(TimedEvent&& other):
|
||||
time_point(std::move(other.time_point)),
|
||||
callback(std::move(other.callback)),
|
||||
repeat(other.repeat),
|
||||
repeat_delay(std::move(other.repeat_delay)),
|
||||
name(std::move(other.name))
|
||||
{
|
||||
}
|
||||
|
||||
TimedEvent::~TimedEvent()
|
||||
{
|
||||
}
|
||||
|
||||
bool TimedEvent::is_after(const TimedEvent& other) const
|
||||
{
|
||||
return this->is_after(other.time_point);
|
||||
}
|
||||
|
||||
bool TimedEvent::is_after(const std::chrono::steady_clock::time_point& time_point) const
|
||||
{
|
||||
return this->time_point >= time_point;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds TimedEvent::get_timeout() const
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now > this->time_point)
|
||||
return std::chrono::milliseconds(0);
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(this->time_point - now);
|
||||
}
|
||||
|
||||
void TimedEvent::execute()
|
||||
{
|
||||
this->callback();
|
||||
}
|
||||
|
||||
const std::string& TimedEvent::get_name() const
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef TIMED_EVENTS_HPP
|
||||
# define TIMED_EVENTS_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
namespace utils {
|
||||
static constexpr std::chrono::milliseconds no_timeout = std::chrono::milliseconds(-1);
|
||||
}
|
||||
|
||||
class TimedEventsManager;
|
||||
|
||||
/**
|
||||
* A callback with an associated date.
|
||||
*/
|
||||
|
||||
class TimedEvent
|
||||
{
|
||||
friend class TimedEventsManager;
|
||||
public:
|
||||
/**
|
||||
* An event the occurs only once, at the given time_point
|
||||
*/
|
||||
explicit TimedEvent(std::chrono::steady_clock::time_point&& time_point,
|
||||
std::function<void()> callback, const std::string& name="");
|
||||
explicit TimedEvent(std::chrono::milliseconds&& duration,
|
||||
std::function<void()> callback, const std::string& name="");
|
||||
|
||||
explicit TimedEvent(TimedEvent&&);
|
||||
~TimedEvent();
|
||||
/**
|
||||
* Whether or not this event happens after the other one.
|
||||
*/
|
||||
bool is_after(const TimedEvent& other) const;
|
||||
bool is_after(const std::chrono::steady_clock::time_point& time_point) const;
|
||||
/**
|
||||
* Return the duration difference between now and the event time point.
|
||||
* If the difference would be negative (i.e. the event is expired), the
|
||||
* returned value is 0 instead. The value cannot then be negative.
|
||||
*/
|
||||
std::chrono::milliseconds get_timeout() const;
|
||||
void execute();
|
||||
const std::string& get_name() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The next time point at which the event is executed.
|
||||
*/
|
||||
std::chrono::steady_clock::time_point time_point;
|
||||
/**
|
||||
* The function to execute.
|
||||
*/
|
||||
const std::function<void()> callback;
|
||||
/**
|
||||
* Whether or not this events repeats itself until it is destroyed.
|
||||
*/
|
||||
const bool repeat;
|
||||
/**
|
||||
* This value is added to the time_point each time the event is executed,
|
||||
* if repeat is true. Otherwise it is ignored.
|
||||
*/
|
||||
const std::chrono::milliseconds repeat_delay;
|
||||
/**
|
||||
* A name that is used to identify that event. If you want to find your
|
||||
* event (for example if you want to cancel it), the name should be
|
||||
* unique.
|
||||
*/
|
||||
const std::string name;
|
||||
|
||||
TimedEvent(const TimedEvent&) = delete;
|
||||
TimedEvent& operator=(const TimedEvent&) = delete;
|
||||
TimedEvent& operator=(TimedEvent&&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* A class managing a list of TimedEvents.
|
||||
* They are sorted, new events can be added, removed, fetch, etc.
|
||||
*/
|
||||
|
||||
class TimedEventsManager
|
||||
{
|
||||
public:
|
||||
~TimedEventsManager();
|
||||
/**
|
||||
* Return the unique instance of this class
|
||||
*/
|
||||
static TimedEventsManager& instance();
|
||||
/**
|
||||
* Add an event to the list of managed events. The list is sorted after
|
||||
* this call.
|
||||
*/
|
||||
void add_event(TimedEvent&& event);
|
||||
/**
|
||||
* Returns the duration, in milliseconds, between now and the next
|
||||
* available event. If the event is already expired (the duration is
|
||||
* negative), 0 is returned instead (as in “it's not too late, execute it
|
||||
* now”)
|
||||
* Returns a negative value if no event is available.
|
||||
*/
|
||||
std::chrono::milliseconds get_timeout() const;
|
||||
/**
|
||||
* Execute all the expired events (if their expiration time is exactly
|
||||
* now, or before now). The event is then removed from the list. If the
|
||||
* event does repeat, its expiration time is updated and it is reinserted
|
||||
* in the list at the correct position.
|
||||
* Returns the number of executed events.
|
||||
*/
|
||||
std::size_t execute_expired_events();
|
||||
/**
|
||||
* Remove (and thus cancel) all the timed events with the given name.
|
||||
* Returns the number of canceled events.
|
||||
*/
|
||||
std::size_t cancel(const std::string& name);
|
||||
/**
|
||||
* Return the number of managed events.
|
||||
*/
|
||||
std::size_t size() const;
|
||||
|
||||
private:
|
||||
explicit TimedEventsManager();
|
||||
std::list<TimedEvent> events;
|
||||
TimedEventsManager(const TimedEventsManager&) = delete;
|
||||
TimedEventsManager(TimedEventsManager&&) = delete;
|
||||
TimedEventsManager& operator=(const TimedEventsManager&) = delete;
|
||||
TimedEventsManager& operator=(TimedEventsManager&&) = delete;
|
||||
};
|
||||
|
||||
#endif // TIMED_EVENTS_HPP
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <utils/timed_events.hpp>
|
||||
|
||||
TimedEventsManager& TimedEventsManager::instance()
|
||||
{
|
||||
static TimedEventsManager inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
TimedEventsManager::TimedEventsManager()
|
||||
{
|
||||
}
|
||||
|
||||
TimedEventsManager::~TimedEventsManager()
|
||||
{
|
||||
}
|
||||
|
||||
void TimedEventsManager::add_event(TimedEvent&& event)
|
||||
{
|
||||
for (auto it = this->events.begin(); it != this->events.end(); ++it)
|
||||
{
|
||||
if (it->is_after(event))
|
||||
{
|
||||
this->events.emplace(it, std::move(event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->events.emplace_back(std::move(event));
|
||||
}
|
||||
|
||||
std::chrono::milliseconds TimedEventsManager::get_timeout() const
|
||||
{
|
||||
if (this->events.empty())
|
||||
return utils::no_timeout;
|
||||
return this->events.front().get_timeout() + std::chrono::milliseconds(1);
|
||||
}
|
||||
|
||||
std::size_t TimedEventsManager::execute_expired_events()
|
||||
{
|
||||
std::size_t count = 0;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
for (auto it = this->events.begin(); it != this->events.end();)
|
||||
{
|
||||
if (!it->is_after(now))
|
||||
{
|
||||
TimedEvent copy(std::move(*it));
|
||||
it = this->events.erase(it);
|
||||
++count;
|
||||
copy.execute();
|
||||
if (copy.repeat)
|
||||
{
|
||||
copy.time_point += copy.repeat_delay;
|
||||
this->add_event(std::move(copy));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t TimedEventsManager::cancel(const std::string& name)
|
||||
{
|
||||
std::size_t res = 0;
|
||||
for (auto it = this->events.begin(); it != this->events.end();)
|
||||
{
|
||||
if (it->get_name() == name)
|
||||
{
|
||||
it = this->events.erase(it);
|
||||
res++;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t TimedEventsManager::size() const
|
||||
{
|
||||
return this->events.size();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <utils/tolower.hpp>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::string tolower(const std::string& original)
|
||||
{
|
||||
std::string res;
|
||||
res.reserve(original.size());
|
||||
for (const char c: original)
|
||||
res += static_cast<char>(std::tolower(c));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef TOLOWER_INCLUDED
|
||||
# define TOLOWER_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::string tolower(const std::string& original);
|
||||
}
|
||||
|
||||
#endif // SPLIT_INCLUDED
|
||||
Reference in New Issue
Block a user