Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 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 105 : AccountArchive::deserialize(std::string_view dat, const std::vector<uint8_t>& salt)
30 : {
31 420 : JAMI_DEBUG("Loading account archive ({:d} bytes)", dat.size());
32 :
33 105 : password_salt = salt;
34 :
35 : // Decode string
36 105 : auto* char_data = dat.data(); // reinterpret_cast<const char*>(&dat[0]);
37 105 : std::string err;
38 105 : Json::Value value;
39 105 : Json::CharReaderBuilder rbuilder;
40 105 : Json::CharReaderBuilder::strictMode(&rbuilder.settings_);
41 105 : auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
42 105 : if (!reader->parse(char_data, char_data + dat.size(), &value, &err)) {
43 4 : JAMI_ERROR("Archive JSON parsing error: {}", err);
44 1 : throw std::runtime_error("failed to parse JSON");
45 : }
46 :
47 : // Import content
48 : try {
49 2785 : for (Json::ValueIterator itr = value.begin(); itr != value.end(); itr++) {
50 : try {
51 2681 : const auto key = itr.key().asString();
52 2681 : if (key.empty())
53 0 : continue;
54 2681 : if (key.compare(libjami::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) {
55 2681 : } else if (key.compare(libjami::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
56 2639 : } else if (key.compare(libjami::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0) {
57 2597 : } else if (key.compare(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL) == 0) {
58 2597 : } else if (key.compare(libjami::Account::ConfProperties::AUTOANSWER) == 0) {
59 2597 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_ENABLED) == 0) {
60 2597 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_SERVER) == 0) {
61 2597 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_PUSH_TOKEN) == 0) {
62 2597 : } else if (key.compare(Conf::RING_CA_KEY) == 0) {
63 104 : ca_key = std::make_shared<dht::crypto::PrivateKey>(base64::decode(itr->asString()));
64 2493 : } else if (key.compare(Conf::RING_ACCOUNT_KEY) == 0) {
65 104 : id.first = std::make_shared<dht::crypto::PrivateKey>(base64::decode(itr->asString()));
66 2389 : } else if (key.compare(Conf::RING_ACCOUNT_CERT) == 0) {
67 104 : id.second = std::make_shared<dht::crypto::Certificate>(base64::decode(itr->asString()));
68 2285 : } else if (key.compare(Conf::RING_ACCOUNT_CONTACTS) == 0) {
69 6 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) {
70 3 : dht::InfoHash h {citr.key().asString()};
71 3 : if (h != dht::InfoHash {})
72 3 : contacts.emplace(h, Contact {*citr});
73 : }
74 2282 : } else if (key.compare(Conf::CONVERSATIONS_KEY) == 0) {
75 23 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) {
76 13 : auto ci = ConvInfo(*citr);
77 13 : conversations[ci.id] = std::move(ci);
78 13 : }
79 2272 : } else if (key.compare(Conf::CONVERSATIONS_REQUESTS_KEY) == 0) {
80 2 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) {
81 1 : conversationsRequests.emplace(citr.key().asString(), ConversationRequest(*citr));
82 : }
83 2271 : } else if (key.compare(Conf::ETH_KEY) == 0) {
84 104 : eth_key = base64::decode(itr->asString());
85 2167 : } else if (key.compare(Conf::RING_ACCOUNT_CRL) == 0) {
86 0 : revoked = std::make_shared<dht::crypto::RevocationList>(base64::decode(itr->asString()));
87 : } else {
88 2167 : config[key] = itr->asString();
89 : }
90 2681 : } catch (const std::exception& ex) {
91 0 : JAMI_ERROR("Unable to parse JSON entry with value of type {}: {}", (unsigned) itr->type(), ex.what());
92 0 : }
93 : }
94 0 : } catch (const std::exception& ex) {
95 0 : JAMI_ERROR("Unable to parse JSON: {}", ex.what());
96 0 : }
97 :
98 104 : if (not id.first) {
99 0 : throw std::runtime_error("Archive doesn't include account private key");
100 : }
101 108 : }
102 :
103 : std::string
104 825 : AccountArchive::serialize() const
105 : {
106 825 : Json::Value root;
107 :
108 5672 : for (const auto& it : config)
109 4847 : root[it.first] = it.second;
110 :
111 825 : if (ca_key and *ca_key)
112 825 : root[Conf::RING_CA_KEY] = base64::encode(ca_key->serialize());
113 :
114 825 : root[Conf::RING_ACCOUNT_KEY] = base64::encode(id.first->serialize());
115 825 : root[Conf::RING_ACCOUNT_CERT] = base64::encode(id.second->getPacked());
116 825 : root[Conf::ETH_KEY] = base64::encode(eth_key);
117 :
118 825 : if (revoked)
119 2 : root[Conf::RING_ACCOUNT_CRL] = base64::encode(revoked->getPacked());
120 :
121 825 : if (not contacts.empty()) {
122 5 : Json::Value& jsonContacts = root[Conf::RING_ACCOUNT_CONTACTS];
123 10 : for (const auto& c : contacts)
124 5 : jsonContacts[c.first.toString()] = c.second.toJson();
125 : }
126 :
127 825 : if (not conversations.empty()) {
128 20 : Json::Value& jsonConversations = root[Conf::CONVERSATIONS_KEY];
129 46 : for (const auto& [key, c] : conversations) {
130 26 : jsonConversations[key] = c.toJson();
131 : }
132 : }
133 :
134 825 : if (not conversationsRequests.empty()) {
135 2 : Json::Value& jsonConversationsReqs = root[Conf::CONVERSATIONS_REQUESTS_KEY];
136 4 : for (const auto& [key, value] : conversationsRequests) {
137 2 : jsonConversationsReqs[key] = value.toJson();
138 : }
139 : }
140 :
141 1650 : return json::toString(root);
142 825 : }
143 :
144 : } // namespace jami
|