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