Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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/json.h> 25 : 26 : namespace jami { 27 : 28 : void 29 95 : AccountArchive::deserialize(const std::vector<uint8_t>& dat, const std::vector<uint8_t>& salt) 30 : { 31 285 : JAMI_DEBUG("Loading account archive ({:d} bytes)", dat.size()); 32 : 33 95 : password_salt = salt; 34 : 35 : // Decode string 36 95 : auto* char_data = reinterpret_cast<const char*>(&dat[0]); 37 95 : std::string err; 38 95 : Json::Value value; 39 95 : Json::CharReaderBuilder rbuilder; 40 95 : Json::CharReaderBuilder::strictMode(&rbuilder.settings_); 41 95 : auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); 42 95 : 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 2546 : for (Json::ValueIterator itr = value.begin(); itr != value.end(); itr++) { 50 : try { 51 2452 : const auto key = itr.key().asString(); 52 2452 : if (key.empty()) 53 0 : continue; 54 2452 : if (key.compare(libjami::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) { 55 2452 : } else if (key.compare(libjami::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) { 56 2410 : } else if (key.compare(libjami::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0) { 57 2368 : } else if (key.compare(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL) == 0) { 58 2368 : } else if (key.compare(libjami::Account::ConfProperties::AUTOANSWER) == 0) { 59 2368 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_ENABLED) == 0) { 60 2368 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_SERVER) == 0) { 61 2368 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_PUSH_TOKEN) == 0) { 62 2368 : } else if (key.compare(Conf::RING_CA_KEY) == 0) { 63 188 : ca_key = std::make_shared<dht::crypto::PrivateKey>( 64 282 : base64::decode(itr->asString())); 65 2274 : } else if (key.compare(Conf::RING_ACCOUNT_KEY) == 0) { 66 188 : id.first = std::make_shared<dht::crypto::PrivateKey>( 67 282 : base64::decode(itr->asString())); 68 2180 : } else if (key.compare(Conf::RING_ACCOUNT_CERT) == 0) { 69 188 : id.second = std::make_shared<dht::crypto::Certificate>( 70 282 : base64::decode(itr->asString())); 71 2086 : } else if (key.compare(Conf::RING_ACCOUNT_CONTACTS) == 0) { 72 6 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 73 3 : dht::InfoHash h {citr.key().asString()}; 74 3 : if (h != dht::InfoHash {}) 75 3 : contacts.emplace(h, Contact {*citr}); 76 : } 77 2083 : } else if (key.compare(Conf::CONVERSATIONS_KEY) == 0) { 78 23 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 79 13 : auto ci = ConvInfo(*citr); 80 13 : conversations[ci.id] = std::move(ci); 81 13 : } 82 2073 : } else if (key.compare(Conf::CONVERSATIONS_REQUESTS_KEY) == 0) { 83 2 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { 84 1 : conversationsRequests.emplace(citr.key().asString(), 85 2 : ConversationRequest(*citr)); 86 : } 87 2072 : } else if (key.compare(Conf::ETH_KEY) == 0) { 88 94 : eth_key = base64::decode(itr->asString()); 89 1978 : } else if (key.compare(Conf::RING_ACCOUNT_CRL) == 0) { 90 0 : revoked = std::make_shared<dht::crypto::RevocationList>( 91 0 : base64::decode(itr->asString())); 92 : } else { 93 1978 : config[key] = itr->asString(); 94 : } 95 2452 : } catch (const std::exception& ex) { 96 0 : JAMI_ERR("Unable to parse JSON entry with value of type %d: %s", 97 : (unsigned) itr->type(), 98 : ex.what()); 99 0 : } 100 : } 101 0 : } catch (const std::exception& ex) { 102 0 : JAMI_ERR("Unable to parse JSON: %s", ex.what()); 103 0 : } 104 : 105 94 : if (not id.first) { 106 0 : throw std::runtime_error("Archive doesn't include account private key"); 107 : } 108 98 : } 109 : 110 : std::string 111 828 : AccountArchive::serialize() const 112 : { 113 828 : Json::Value root; 114 : 115 4732 : for (const auto& it : config) 116 3904 : root[it.first] = it.second; 117 : 118 828 : if (ca_key and *ca_key) 119 828 : root[Conf::RING_CA_KEY] = base64::encode(ca_key->serialize()); 120 : 121 828 : root[Conf::RING_ACCOUNT_KEY] = base64::encode(id.first->serialize()); 122 828 : root[Conf::RING_ACCOUNT_CERT] = base64::encode(id.second->getPacked()); 123 828 : root[Conf::ETH_KEY] = base64::encode(eth_key); 124 : 125 828 : if (revoked) 126 2 : root[Conf::RING_ACCOUNT_CRL] = base64::encode(revoked->getPacked()); 127 : 128 828 : if (not contacts.empty()) { 129 5 : Json::Value& jsonContacts = root[Conf::RING_ACCOUNT_CONTACTS]; 130 10 : for (const auto& c : contacts) 131 5 : jsonContacts[c.first.toString()] = c.second.toJson(); 132 : } 133 : 134 828 : if (not conversations.empty()) { 135 19 : Json::Value& jsonConversations = root[Conf::CONVERSATIONS_KEY]; 136 44 : for (const auto& [key, c] : conversations) { 137 25 : jsonConversations[key] = c.toJson(); 138 : } 139 : } 140 : 141 828 : if (not conversationsRequests.empty()) { 142 2 : Json::Value& jsonConversationsReqs = root[Conf::CONVERSATIONS_REQUESTS_KEY]; 143 4 : for (const auto& [key, value] : conversationsRequests) { 144 2 : jsonConversationsReqs[key] = value.toJson(); 145 : } 146 : } 147 : 148 828 : Json::StreamWriterBuilder wbuilder; 149 828 : wbuilder["commentStyle"] = "None"; 150 828 : wbuilder["indentation"] = ""; 151 1656 : return Json::writeString(wbuilder, root); 152 828 : } 153 : 154 : } // namespace jami