/* * Copyright (C) 2014-2016 Savoir-faire Linux Inc. * Author(s) : Adrien Béraud * Simon Désaulniers * * 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; either version 3 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "value.h" #include "default_types.h" #include "securedht.h" // print certificate ID namespace dht { const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."}; std::ostream& operator<< (std::ostream& s, const Value& v) { s << "Value[id:" << std::hex << v.id << std::dec << " "; if (v.isEncrypted()) s << "encrypted "; else if (v.isSigned()) { s << "signed (v" << v.seq << ") "; if (v.recipient != InfoHash()) s << "decrypted "; } if (not v.isEncrypted()) { if (v.type == IpServiceAnnouncement::TYPE.id) { s << IpServiceAnnouncement(v.data); } else if (v.type == CERTIFICATE_TYPE.id) { s << "Certificate"; try { InfoHash h = crypto::Certificate(v.data).getPublicKey().getId(); s << " with ID " << h; } catch (const std::exception& e) { s << " (invalid)"; } } else { s << "Data (type: " << v.type << " ): "; s << std::hex; for (size_t i=0; i& v, InfoHash, const sockaddr*, socklen_t) { return v->data.size() <= MAX_VALUE_SIZE; } msgpack::object* findMapValue(const msgpack::object& map, const std::string& key) { if (map.type != msgpack::type::MAP) throw msgpack::type_error(); for (unsigned i = 0; i < map.via.map.size; i++) { auto& o = map.via.map.ptr[i]; if(o.key.type != msgpack::type::STR) continue; if (o.key.as() == key) { return &o.val; } } return nullptr; } size_t Value::size() const { return cypher.size() + data.size() + signature.size() + user_type.size(); } void Value::msgpack_unpack(msgpack::object o) { if (o.type != msgpack::type::MAP) throw msgpack::type_error(); if (o.via.map.size < 2) throw msgpack::type_error(); if (auto rid = findMapValue(o, "id")) { id = rid->as(); } else throw msgpack::type_error(); if (auto rdat = findMapValue(o, "dat")) { msgpack_unpack_body(*rdat); } else throw msgpack::type_error(); } void Value::msgpack_unpack_body(const msgpack::object& o) { owner = {}; recipient = {}; cypher.clear(); signature.clear(); data.clear(); type = 0; if (o.type == msgpack::type::BIN) { auto dat = o.as>(); cypher = {dat.begin(), dat.end()}; } else { if (o.type != msgpack::type::MAP) throw msgpack::type_error(); auto rbody = findMapValue(o, "body"); if (not rbody) throw msgpack::type_error(); if (auto rdata = findMapValue(*rbody, "data")) { data = unpackBlob(*rdata); } else throw msgpack::type_error(); if (auto rtype = findMapValue(*rbody, "type")) { type = rtype->as(); } else throw msgpack::type_error(); if (auto rutype = findMapValue(*rbody, "utype")) { user_type = rutype->as(); } if (auto rowner = findMapValue(*rbody, "owner")) { if (auto rseq = findMapValue(*rbody, "seq")) seq = rseq->as(); else throw msgpack::type_error(); crypto::PublicKey new_owner; new_owner.msgpack_unpack(*rowner); owner = std::make_shared(std::move(new_owner)); if (auto rrecipient = findMapValue(*rbody, "to")) { recipient = rrecipient->as(); } if (auto rsig = findMapValue(o, "sig")) { signature = unpackBlob(*rsig); } else throw msgpack::type_error(); } } } bool FieldValue::operator==(const FieldValue& vfd) const { if (field != vfd.field) return false; switch (field) { case Value::Field::Id: case Value::Field::ValueType: return intValue == vfd.intValue; case Value::Field::OwnerPk: return hashValue == vfd.hashValue; case Value::Field::UserType: return blobValue == vfd.blobValue; case Value::Field::None: return true; default: return false; } } Value::Filter FieldValue::getLocalFilter() const { switch (field) { case Value::Field::Id: return Value::IdFilter(intValue); case Value::Field::ValueType: return Value::TypeFilter(intValue); case Value::Field::OwnerPk: return Value::ownerFilter(hashValue); case Value::Field::UserType: return Value::userTypeFilter(std::string {blobValue.begin(), blobValue.end()}); default: return Value::AllFilter(); } } FieldValueIndex::FieldValueIndex(const Value& v, Select s) { auto selection = s.getSelection(); if (not selection.empty()) { std::transform(selection.begin(), selection.end(), std::inserter(index, index.end()), [](const std::set::value_type& f) { return std::make_pair(f, FieldValue {}); }); } else { index.clear(); for (size_t f = 1 ; f < 5 ; ++f) index[static_cast(f)] = {}; } for (const auto& fvp : index) { const auto& f = fvp.first; switch (f) { case Value::Field::Id: index[f] = {f, v.id}; break; case Value::Field::ValueType: index[f] = {f, v.type}; break; case Value::Field::OwnerPk: index[f] = {f, v.owner ? v.owner->getId() : InfoHash() }; break; case Value::Field::UserType: index[f] = {f, Blob {v.user_type.begin(), v.user_type.end()}}; break; default: break; } } } bool FieldValueIndex::containedIn(const FieldValueIndex& other) const { if (index.size() > other.index.size()) return false; for (const auto& field : index) { auto other_field = other.index.find(field.first); if (other_field == other.index.end()) return false; } return true; } std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi) { os << "Index["; for (auto v = fvi.index.begin(); v != fvi.index.end(); ++v) { switch (v->first) { case Value::Field::Id: os << "Id:" << std::hex << v->second.getInt(); break; case Value::Field::ValueType: os << "ValueType:" << v->second.getInt(); break; case Value::Field::OwnerPk: os << "Owner:" << v->second.getHash().toString(); break; case Value::Field::UserType: { auto ut = v->second.getBlob(); os << "UserType:" << std::string(ut.begin(), ut.end()); break; } default: break; } os << (std::next(v) != fvi.index.end() ? "," : ""); } return os << "]"; } void FieldValueIndex::msgpack_unpack_fields(const std::set& fields, const msgpack::object& o, unsigned offset) { index.clear(); unsigned j = 0; for (const auto& field : fields) { auto& field_value = o.via.array.ptr[offset+(j++)]; switch (field) { case Value::Field::Id: case Value::Field::ValueType: index[field] = FieldValue(field, field_value.as()); break; case Value::Field::OwnerPk: index[field] = FieldValue(field, field_value.as()); break; case Value::Field::UserType: index[field] = FieldValue(field, field_value.as()); break; default: throw msgpack::type_error(); } } } void trim_str(std::string& str) { auto first = std::min(str.size(), str.find_first_not_of(" ")); auto last = std::min(str.size(), str.find_last_not_of(" ")); str = str.substr(first, last - first + 1); } Select::Select(const std::string& q_str) { std::istringstream q_iss {q_str}; std::string token {}; q_iss >> token; if (token == "SELECT" or token == "select") { q_iss >> token; std::istringstream fields {token}; while (std::getline(fields, token, ',')) { trim_str(token); if (token == "id") field(Value::Field::Id); else if (token == "value_type") field(Value::Field::ValueType); else if (token == "owner_pk") field(Value::Field::OwnerPk); else if (token == "user_type") field(Value::Field::UserType); } } } Where::Where(const std::string& q_str) { std::istringstream q_iss {q_str}; std::string token {}; q_iss >> token; if (token == "WHERE" or token == "where") { std::getline(q_iss, token); std::istringstream restrictions {token}; while (std::getline(restrictions, token, ',')) { trim_str(token); std::istringstream eq_ss {token}; std::string field_str, value_str; std::getline(eq_ss, field_str, '='); trim_str(field_str); std::getline(eq_ss, value_str, '='); trim_str(value_str); if (not value_str.empty()) { uint64_t v = 0; std::string s {}; std::istringstream convert {value_str}; convert >> v; if (convert.failbit and value_str.size() > 1 and value_str[0] == '\"' and value_str[value_str.size()-1] == '\"') s = value_str.substr(1, value_str.size()-2); else s = value_str; if (field_str == "id") id(v); else if (field_str == "value_type") valueType(v); else if (field_str == "owner_pk") owner(InfoHash(s)); else if (field_str == "user_type") userType(s); else throw std::invalid_argument(Query::QUERY_PARSE_ERROR + " (WHERE) wrong token near: " + field_str); } } } } void Query::msgpack_unpack(const msgpack::object& o) { if (o.type != msgpack::type::MAP) throw msgpack::type_error(); auto rfilters = findMapValue(o, "w"); /* unpacking filters */ if (rfilters) where.msgpack_unpack(*rfilters); else throw msgpack::type_error(); auto rfield_selector = findMapValue(o, "s"); /* unpacking field selectors */ if (rfield_selector) select.msgpack_unpack(*rfield_selector); else throw msgpack::type_error(); } template bool subset(std::vector fds, std::vector qfds) { for (auto& fd : fds) { auto correspondance = std::find_if(qfds.begin(), qfds.end(), [&fd](T& _vfd) { return fd == _vfd; }); if (correspondance == qfds.end()) return false; } return true; } bool Select::isSatisfiedBy(const Select& os) const { /* empty, means all values are selected. */ if (fieldSelection_.empty() and not os.fieldSelection_.empty()) return false; else return subset(fieldSelection_, os.fieldSelection_); } bool Where::isSatisfiedBy(const Where& ow) const { return subset(ow.filters_, filters_); } bool Query::isSatisfiedBy(const Query& q) const { return where.isSatisfiedBy(q.where) and select.isSatisfiedBy(q.select); } std::ostream& operator<<(std::ostream& s, const dht::Select& select) { s << "SELECT " << (select.fieldSelection_.empty() ? "*" : ""); for (auto fs = select.fieldSelection_.begin() ; fs != select.fieldSelection_.end() ; ++fs) { switch (fs->getField()) { case Value::Field::Id: s << "id"; break; case Value::Field::ValueType: s << "value_type"; break; case Value::Field::UserType: s << "user_type"; break; case Value::Field::OwnerPk: s << "owner_public_key"; break; default: break; } s << (std::next(fs) != select.fieldSelection_.end() ? "," : ""); } return s; } std::ostream& operator<<(std::ostream& s, const dht::Where& where) { if (not where.filters_.empty()) { s << "WHERE "; for (auto f = where.filters_.begin() ; f != where.filters_.end() ; ++f) { switch (f->getField()) { case Value::Field::Id: s << "id=" << f->getInt(); break; case Value::Field::ValueType: s << "value_type=" << f->getInt(); break; case Value::Field::OwnerPk: s << "owner_pk_hash=" << f->getHash().toString(); break; case Value::Field::UserType: { auto b = f->getBlob(); s << "user_type=" << std::string {b.begin(), b.end()}; break; } default: break; } s << (std::next(f) != where.filters_.end() ? "," : ""); } } return s; } }