Line data Source code
1 : /* 2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include "accountarchive.h" 19 : #include "account_const.h" 20 : #include "configkeys.h" 21 : #include "base64.h" 22 : #include "logger.h" 23 : 24 : #include <json_utils.h> 25 : 26 : namespace jami { 27 : 28 : void 29 87 : AccountArchive::deserialize(std::string_view dat, const std::vector<uint8_t>& salt) 30 : { 31 261 : JAMI_DEBUG("Loading account archive ({:d} bytes)", dat.size()); 32 : 33 87 : password_salt = salt; 34 : 35 : // Decode string 36 87 : auto* char_data = dat.data(); // reinterpret_cast<const char*>(&dat[0]); 37 87 : std::string err; 38 87 : Json::Value value; 39 87 : Json::CharReaderBuilder rbuilder; 40 87 : Json::CharReaderBuilder::strictMode(&rbuilder.settings_); 41 87 : auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); 42 87 : if (!reader->parse(char_data, char_data + dat.size(), &value, &err)) { 43 1 : JAMI_ERR() << "Archive JSON parsing error: " << err; 44 1 : throw std::runtime_error("failed to parse JSON"); 45 : } 46 : 47 : // Import content 48 : try { 49 2205 : for (Json::ValueIterator itr = value.begin(); itr != value.end(); itr++) { 50 : try { 51 2119 : const auto key = itr.key().asString(); 52 2119 : if (key.empty()) 53 0 : continue; 54 2119 : if (key.compare(libjami::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) { 55 2119 : } else if (key.compare(libjami::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) 56 2119 : == 0) { 57 2085 : } else if (key.compare(libjami::Account::ConfProperties::TLS::CERTIFICATE_FILE) 58 2085 : == 0) { 59 2051 : } else if (key.compare(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL) == 0) { 60 2051 : } else if (key.compare(libjami::Account::ConfProperties::AUTOANSWER) == 0) { 61 2051 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_ENABLED) == 0) { 62 2051 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_SERVER) == 0) { 63 2051 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_PUSH_TOKEN) == 0) { 64 2051 : } else if (key.compare(Conf::RING_CA_KEY) == 0) { 65 172 : ca_key = std::make_shared<dht::crypto::PrivateKey>( 66 258 : base64::decode(itr->asString())); 67 1965 : } else if (key.compare(Conf::RING_ACCOUNT_KEY) == 0) { 68 172 : id.first = std::make_shared<dht::crypto::PrivateKey>( 69 258 : base64::decode(itr->asString())); 70 1879 : } else if (key.compare(Conf::RING_ACCOUNT_CERT) == 0) { 71 172 : id.second = std::make_shared<dht::crypto::Certificate>( 72 258 : base64::decode(itr->asString())); 73 1793 : } else if (key.compare(Conf::RING_ACCOUNT_CONTACTS) == 0) { 74 4 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 75 2 : dht::InfoHash h {citr.key().asString()}; 76 2 : if (h != dht::InfoHash {}) 77 2 : contacts.emplace(h, Contact {*citr}); 78 : } 79 1791 : } else if (key.compare(Conf::CONVERSATIONS_KEY) == 0) { 80 19 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 81 11 : auto ci = ConvInfo(*citr); 82 11 : conversations[ci.id] = std::move(ci); 83 11 : } 84 1783 : } else if (key.compare(Conf::CONVERSATIONS_REQUESTS_KEY) == 0) { 85 2 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 86 1 : conversationsRequests.emplace(citr.key().asString(), 87 2 : ConversationRequest(*citr)); 88 : } 89 1782 : } else if (key.compare(Conf::ETH_KEY) == 0) { 90 86 : eth_key = base64::decode(itr->asString()); 91 1696 : } else if (key.compare(Conf::RING_ACCOUNT_CRL) == 0) { 92 0 : revoked = std::make_shared<dht::crypto::RevocationList>( 93 0 : base64::decode(itr->asString())); 94 : } else { 95 1696 : config[key] = itr->asString(); 96 : } 97 2119 : } catch (const std::exception& ex) { 98 0 : JAMI_ERR("Unable to parse JSON entry with value of type %d: %s", 99 : (unsigned) itr->type(), 100 : ex.what()); 101 0 : } 102 : } 103 0 : } catch (const std::exception& ex) { 104 0 : JAMI_ERR("Unable to parse JSON: %s", ex.what()); 105 0 : } 106 : 107 86 : if (not id.first) { 108 0 : throw std::runtime_error("Archive doesn't include account private key"); 109 : } 110 90 : } 111 : 112 : std::string 113 672 : AccountArchive::serialize() const 114 : { 115 672 : Json::Value root; 116 : 117 4496 : for (const auto& it : config) 118 3824 : root[it.first] = it.second; 119 : 120 672 : if (ca_key and *ca_key) 121 672 : root[Conf::RING_CA_KEY] = base64::encode(ca_key->serialize()); 122 : 123 672 : root[Conf::RING_ACCOUNT_KEY] = base64::encode(id.first->serialize()); 124 672 : root[Conf::RING_ACCOUNT_CERT] = base64::encode(id.second->getPacked()); 125 672 : root[Conf::ETH_KEY] = base64::encode(eth_key); 126 : 127 672 : if (revoked) 128 2 : root[Conf::RING_ACCOUNT_CRL] = base64::encode(revoked->getPacked()); 129 : 130 672 : if (not contacts.empty()) { 131 4 : Json::Value& jsonContacts = root[Conf::RING_ACCOUNT_CONTACTS]; 132 8 : for (const auto& c : contacts) 133 4 : jsonContacts[c.first.toString()] = c.second.toJson(); 134 : } 135 : 136 672 : if (not conversations.empty()) { 137 16 : Json::Value& jsonConversations = root[Conf::CONVERSATIONS_KEY]; 138 38 : for (const auto& [key, c] : conversations) { 139 22 : jsonConversations[key] = c.toJson(); 140 : } 141 : } 142 : 143 672 : if (not conversationsRequests.empty()) { 144 2 : Json::Value& jsonConversationsReqs = root[Conf::CONVERSATIONS_REQUESTS_KEY]; 145 4 : for (const auto& [key, value] : conversationsRequests) { 146 2 : jsonConversationsReqs[key] = value.toJson(); 147 : } 148 : } 149 : 150 1344 : return json::toString(root); 151 672 : } 152 : 153 : } // namespace jami