/**
* Relational pipes
* Copyright © 2021 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <vector>
#include <sstream>
#include <iomanip>
#include <cmath>
namespace relpipe {
namespace in {
namespace asn1 {
namespace lib {
class ASN1ContentHandler {
public:
enum class CollectionType : uint64_t {
Constructed, // TODO: special event?
Sequence = 16,
Set = 17
};
enum class StringType : uint64_t {
OctetString,
UTF8String,
NumericString,
PrintableString,
T61String,
VideotexString,
IA5String,
GraphicString,
VisibleString,
GeneralString,
UniversalString,
CharacterString,
BMPString,
};
class Integer {
private:
// TODO: use std::string (of octets, not ASCII) instead of std::vector?
// TODO: use this class as BigInteger across Relational pipes?
std::vector<uint8_t> data;
public:
/**
* @param data integer octets as in BER encoding
*/
Integer(std::vector<uint8_t> data) : data(data) {
}
virtual ~Integer() {
}
size_t size() const {
return data.size();
}
const uint8_t& operator[](std::size_t index) const {
return data[index];
}
const std::string toHex() const {
std::stringstream hex;
hex << std::hex << std::setfill('0');
for (uint8_t b : data) hex << std::setw(2) << (int) b;
return hex.str();
}
const std::string toString() const {
try {
return std::to_string(toInt64());
} catch (...) {
// integer has more than 64 bits → only HEX form value will be available
// TODO: support longer values than 64 bits
// TODO: do not ignore zero-length error?
return "";
}
}
const int64_t toInt64() const {
int64_t value = 0;
if (data.size() > sizeof (value)) throw std::invalid_argument("Integer is too long");
else if (data.size() == 0) throw std::invalid_argument("Integer has zero length");
value = data[0];
bool negative = data[0] & 0x80;
for (size_t i = 1, limit = data.size(); i < limit; i++) value = (value << 8) | data[i];
if (negative) value -= std::pow(256, data.size());
return value;
}
};
class ObjectIdentifier {
private:
// TODO: use std::string (of octets, not ASCII) instead of std::vector?
// TODO: use this class across Relational pipes as one of basic types?
std::vector<uint8_t> data;
public:
/**
* @param data integer octets as in BER encoding
*/
ObjectIdentifier(std::vector<uint8_t> data) : data(data) {
// TODO: cache size and element values?
}
virtual ~ObjectIdentifier() {
}
/**
* @return number of elements, not octets
*/
size_t size() const {
return 0; // FIXME: correct OID size
}
/**
* @param index 0 = root element
* @return value of the element at given position
*/
const uint8_t& operator[](std::size_t index) const {
return data[index]; // FIXME: correct OID value
}
const std::string toString() const {
if (data.size() == 0) return "";
std::stringstream result;
result << (data[0] / 40) << "." << (data[0] % 40); // first two elements are encoded in the first octet
for (size_t i = 1, limit = data.size(), octet = 0, element = 0; i < limit; i++) {
octet = data[i];
element = element << 7 | (octet & 0xFF >> 1);
// TODO: throw exception if the element value overflows? (should not happen) or format even longer values
if ((octet & 1 << 7) == 0) {
result << "." << element;
element = 0;
}
}
return result.str();
}
};
virtual ~ASN1ContentHandler() = default;
// TODO: more metadata, support OID decoding and ASN.1 modules (schema), probably through a plug-in
virtual void writeStreamStart() = 0;
virtual void writeStreamEnd() = 0;
virtual void writeCollectionStart(CollectionType type) = 0;
virtual void writeCollectionEnd() = 0;
virtual void writeBoolean(bool value) = 0;
virtual void writeNull() = 0;
virtual void writeInteger(Integer value) = 0;
virtual void writeString(StringType type, std::string value) = 0;
virtual void writeOID(ObjectIdentifier value) = 0;
// Object descriptor
// virtual void writeReal(float value) = 0;
// Enumerated
// Embedded PVD
// Relative OID
// TIME
// UTC time
// Generalized time
// Date
// Time of day
// Date-time
// Duration
// OID-IRI
// Relative OID-IRI
};
class ASN1ContentHandlerProxy : public ASN1ContentHandler {
private:
std::vector<std::shared_ptr<ASN1ContentHandler>> handlers;
public:
void addHandler(std::shared_ptr<ASN1ContentHandler> handler) {
handlers.push_back(handler);
}
#define handler for (auto ___h : handlers) ___h
void writeStreamStart() override {
handler->writeStreamStart();
}
void writeStreamEnd() override {
handler->writeStreamEnd();
}
void writeCollectionStart(CollectionType type) override {
handler->writeCollectionStart(type);
}
void writeCollectionEnd() override {
handler->writeCollectionEnd();
}
void writeBoolean(bool value) override {
handler->writeBoolean(value);
}
void writeNull() override {
handler->writeNull();
}
void writeInteger(Integer value) override {
handler->writeInteger(value);
}
void writeString(StringType type, std::string value) override {
handler->writeString(type, value);
}
void writeOID(ObjectIdentifier value) override {
handler->writeOID(value);
}
#undef handler
};
}
}
}
}