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 103 : AccountArchive::deserialize(std::string_view dat, const std::vector<uint8_t>& salt)
30 : {
31 412 : JAMI_DEBUG("Loading account archive ({:d} bytes)", dat.size());
32 :
33 103 : password_salt = salt;
34 :
35 : // Decode string
36 103 : auto* char_data = dat.data(); // reinterpret_cast<const char*>(&dat[0]);
37 103 : std::string err;
38 103 : Json::Value value;
39 103 : Json::CharReaderBuilder rbuilder;
40 103 : Json::CharReaderBuilder::strictMode(&rbuilder.settings_);
41 103 : auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
42 103 : 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 2682 : for (Json::ValueIterator itr = value.begin(); itr != value.end(); itr++) {
50 : try {
51 2580 : const auto key = itr.key().asString();
52 2580 : if (key.empty())
53 0 : continue;
54 2580 : if (key.compare(libjami::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) {
55 2580 : } else if (key.compare(libjami::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) == 0) {
56 2580 : } else if (key.compare(libjami::Account::ConfProperties::TLS::CERTIFICATE_FILE) == 0) {
57 2580 : } else if (key.compare(libjami::Account::ConfProperties::DHT_PROXY_LIST_URL) == 0) {
58 2580 : } else if (key.compare(libjami::Account::ConfProperties::AUTOANSWER) == 0) {
59 2580 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_ENABLED) == 0) {
60 2580 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_SERVER) == 0) {
61 2580 : } else if (key.compare(libjami::Account::ConfProperties::PROXY_PUSH_TOKEN) == 0) {
62 2580 : } else if (key.compare(Conf::RING_CA_KEY) == 0) {
63 102 : ca_key = std::make_shared<dht::crypto::PrivateKey>(base64::decode(itr->asString()));
64 2478 : } else if (key.compare(Conf::RING_ACCOUNT_KEY) == 0) {
65 102 : id.first = std::make_shared<dht::crypto::PrivateKey>(base64::decode(itr->asString()));
66 2376 : } else if (key.compare(Conf::RING_ACCOUNT_CERT) == 0) {
67 102 : id.second = std::make_shared<dht::crypto::Certificate>(base64::decode(itr->asString()));
68 2274 : } 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 2271 : } else if (key.compare(Conf::CONVERSATIONS_KEY) == 0) {
75 21 : for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) {
76 12 : auto ci = ConvInfo(*citr);
77 12 : conversations[ci.id] = std::move(ci);
78 12 : }
79 2262 : } 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 2261 : } else if (key.compare(Conf::ETH_KEY) == 0) {
84 102 : eth_key = base64::decode(itr->asString());
85 2159 : } 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 2159 : config[key] = itr->asString();
89 : }
90 2580 : } 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 102 : if (not id.first) {
99 0 : throw std::runtime_error("Archive doesn't include account private key");
100 : }
101 106 : }
102 :
103 : std::string
104 844 : AccountArchive::serialize() const
105 : {
106 844 : Json::Value root;
107 :
108 5608 : for (const auto& it : config)
109 4764 : root[it.first] = it.second;
110 :
111 844 : if (ca_key and *ca_key)
112 844 : root[Conf::RING_CA_KEY] = base64::encode(ca_key->serialize());
113 :
114 844 : root[Conf::RING_ACCOUNT_KEY] = base64::encode(id.first->serialize());
115 844 : root[Conf::RING_ACCOUNT_CERT] = base64::encode(id.second->getPacked());
116 844 : root[Conf::ETH_KEY] = base64::encode(eth_key);
117 :
118 844 : if (revoked)
119 2 : root[Conf::RING_ACCOUNT_CRL] = base64::encode(revoked->getPacked());
120 :
121 844 : 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 844 : if (not conversations.empty()) {
128 18 : Json::Value& jsonConversations = root[Conf::CONVERSATIONS_KEY];
129 42 : for (const auto& [key, c] : conversations) {
130 24 : jsonConversations[key] = c.toJson();
131 : }
132 : }
133 :
134 844 : 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 1688 : return json::toString(root);
142 844 : }
143 :
144 : } // namespace jami
|