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 <cppunit/TestAssert.h>
19 : #include <cppunit/TestFixture.h>
20 : #include <cppunit/extensions/HelperMacros.h>
21 :
22 : #include <yaml-cpp/yaml.h>
23 : #include <filesystem>
24 :
25 : #include "common.h"
26 :
27 : /* Jami */
28 : #include "account_const.h"
29 : #include "base64.h"
30 : #include "jami.h"
31 : #include "fileutils.h"
32 : #include "manager.h"
33 : #include "jamidht/conversation.h"
34 : #include "jamidht/conversationrepository.h"
35 : #include "conversation/conversationcommon.h"
36 :
37 : using namespace std::string_literals;
38 :
39 : /* Make GCC quiet about unused functions */
40 : #pragma GCC diagnostic push
41 : #pragma GCC diagnostic ignored "-Wunused-function"
42 :
43 : namespace jami {
44 :
45 : void
46 1 : addVote(std::shared_ptr<JamiAccount> account,
47 : const std::string& convId,
48 : const std::string& votedUri,
49 : const std::string& content)
50 : {
51 1 : ConversationRepository::DISABLE_RESET = true;
52 2 : auto repoPath = fileutils::get_data_dir() / account->getAccountID()
53 4 : / "conversations" / convId;
54 2 : auto voteDirectory = repoPath / "votes" / "members";
55 1 : auto voteFile = voteDirectory / votedUri;
56 1 : if (!dhtnet::fileutils::recursive_mkdir(voteDirectory, 0700)) {
57 0 : return;
58 : }
59 :
60 1 : std::ofstream file(voteFile);
61 1 : if (file.is_open()) {
62 1 : file << content;
63 1 : file.close();
64 : }
65 :
66 1 : Json::Value json;
67 1 : json["uri"] = votedUri;
68 1 : json["type"] = "vote";
69 1 : Json::StreamWriterBuilder wbuilder;
70 1 : wbuilder["commentStyle"] = "None";
71 1 : wbuilder["indentation"] = "";
72 1 : ConversationRepository cr(account, convId);
73 1 : cr.commitMessage(Json::writeString(wbuilder, json), false);
74 1 : }
75 :
76 : void
77 3 : simulateRemoval(std::shared_ptr<JamiAccount> account,
78 : const std::string& convId,
79 : const std::string& votedUri)
80 : {
81 3 : ConversationRepository::DISABLE_RESET = true;
82 6 : auto repoPath = fileutils::get_data_dir() / account->getAccountID()
83 12 : / "conversations" / convId;
84 6 : auto memberFile = repoPath / "members" / (votedUri + ".crt");
85 6 : auto bannedFile = repoPath / "banned" / "members"
86 9 : / (votedUri + ".crt");
87 3 : std::rename(memberFile.c_str(), bannedFile.c_str());
88 :
89 3 : git_repository* repo = nullptr;
90 3 : if (git_repository_open(&repo, repoPath.c_str()) != 0)
91 0 : return;
92 3 : GitRepository rep = {std::move(repo), git_repository_free};
93 :
94 : // git add -A
95 3 : git_index* index_ptr = nullptr;
96 3 : if (git_repository_index(&index_ptr, repo) < 0)
97 0 : return;
98 3 : GitIndex index {index_ptr, git_index_free};
99 3 : git_strarray array = {nullptr, 0};
100 3 : git_index_add_all(index.get(), &array, 0, nullptr, nullptr);
101 3 : git_index_write(index.get());
102 3 : git_strarray_dispose(&array);
103 :
104 3 : ConversationRepository cr(account, convId);
105 :
106 3 : Json::Value json;
107 3 : json["action"] = "ban";
108 3 : json["uri"] = votedUri;
109 3 : json["type"] = "member";
110 3 : Json::StreamWriterBuilder wbuilder;
111 3 : wbuilder["commentStyle"] = "None";
112 3 : wbuilder["indentation"] = "";
113 3 : cr.commitMessage(Json::writeString(wbuilder, json));
114 :
115 9 : libjami::sendMessage(account->getAccountID(),
116 : convId,
117 6 : "trigger the fake history to be pulled"s,
118 : "");
119 3 : }
120 :
121 : void
122 8 : addFile(std::shared_ptr<JamiAccount> account,
123 : const std::string& convId,
124 : const std::string& relativePath,
125 : const std::string& content)
126 : {
127 8 : ConversationRepository::DISABLE_RESET = true;
128 16 : auto repoPath = fileutils::get_data_dir() / account->getAccountID()
129 32 : / "conversations" / convId;
130 : // Add file
131 8 : auto p = std::filesystem::path(fileutils::getFullPath(repoPath, relativePath));
132 8 : dhtnet::fileutils::recursive_mkdir(p.parent_path());
133 8 : std::ofstream file(p);
134 8 : if (file.is_open()) {
135 8 : file << content;
136 8 : file.close();
137 : }
138 :
139 8 : git_repository* repo = nullptr;
140 8 : if (git_repository_open(&repo, repoPath.c_str()) != 0)
141 0 : return;
142 8 : GitRepository rep = {std::move(repo), git_repository_free};
143 :
144 : // git add -A
145 8 : git_index* index_ptr = nullptr;
146 8 : if (git_repository_index(&index_ptr, repo) < 0)
147 0 : return;
148 8 : GitIndex index {index_ptr, git_index_free};
149 8 : git_strarray array = {nullptr, 0};
150 8 : git_index_add_all(index.get(), &array, 0, nullptr, nullptr);
151 8 : git_index_write(index.get());
152 8 : git_strarray_dispose(&array);
153 8 : }
154 :
155 : void
156 7 : addAll(std::shared_ptr<JamiAccount> account, const std::string& convId)
157 : {
158 7 : ConversationRepository::DISABLE_RESET = true;
159 14 : auto repoPath = fileutils::get_data_dir() / account->getAccountID()
160 28 : / "conversations" / convId;
161 :
162 7 : git_repository* repo = nullptr;
163 7 : if (git_repository_open(&repo, repoPath.c_str()) != 0)
164 0 : return;
165 7 : GitRepository rep = {std::move(repo), git_repository_free};
166 :
167 : // git add -A
168 7 : git_index* index_ptr = nullptr;
169 7 : if (git_repository_index(&index_ptr, repo) < 0)
170 0 : return;
171 7 : GitIndex index {index_ptr, git_index_free};
172 7 : git_strarray array = {nullptr, 0};
173 7 : git_index_add_all(index.get(), &array, 0, nullptr, nullptr);
174 7 : git_index_write(index.get());
175 7 : git_strarray_dispose(&array);
176 7 : }
177 :
178 : void
179 5 : commit(std::shared_ptr<JamiAccount> account, const std::string& convId, Json::Value& message)
180 : {
181 5 : ConversationRepository::DISABLE_RESET = true;
182 5 : ConversationRepository cr(account, convId);
183 :
184 5 : Json::StreamWriterBuilder wbuilder;
185 5 : wbuilder["commentStyle"] = "None";
186 5 : wbuilder["indentation"] = "";
187 5 : cr.commitMessage(Json::writeString(wbuilder, message));
188 5 : }
189 :
190 : std::string
191 3 : commitInRepo(const std::string& path, std::shared_ptr<JamiAccount> account, const std::string& msg)
192 : {
193 3 : ConversationRepository::DISABLE_RESET = true;
194 3 : auto deviceId = std::string(account->currentDeviceId());
195 3 : auto name = account->getDisplayName();
196 3 : if (name.empty())
197 0 : name = deviceId;
198 :
199 3 : git_signature* sig_ptr = nullptr;
200 : // Sign commit's buffer
201 3 : if (git_signature_new(&sig_ptr, name.c_str(), deviceId.c_str(), std::time(nullptr), 0) < 0) {
202 0 : JAMI_ERROR("Unable to create a commit signature.");
203 0 : return {};
204 : }
205 3 : GitSignature sig {sig_ptr, git_signature_free};
206 :
207 : // Retrieve current index
208 3 : git_index* index_ptr = nullptr;
209 3 : git_repository* repo = nullptr;
210 : // TODO share this repo with GitServer
211 3 : if (git_repository_open(&repo, path.c_str()) != 0) {
212 0 : JAMI_ERROR("Unable to open repository");
213 0 : return {};
214 : }
215 :
216 3 : if (git_repository_index(&index_ptr, repo) < 0) {
217 0 : JAMI_ERROR("Unable to open repository index");
218 0 : return {};
219 : }
220 3 : GitIndex index {index_ptr, git_index_free};
221 :
222 : git_oid tree_id;
223 3 : if (git_index_write_tree(&tree_id, index.get()) < 0) {
224 0 : JAMI_ERROR("Unable to write initial tree from index");
225 0 : return {};
226 : }
227 :
228 3 : git_tree* tree_ptr = nullptr;
229 3 : if (git_tree_lookup(&tree_ptr, repo, &tree_id) < 0) {
230 0 : JAMI_ERROR("Unable to look up initial tree");
231 0 : return {};
232 : }
233 3 : GitTree tree = {tree_ptr, git_tree_free};
234 :
235 : git_oid commit_id;
236 3 : if (git_reference_name_to_id(&commit_id, repo, "HEAD") < 0) {
237 0 : JAMI_ERROR("Unable to get reference for HEAD");
238 0 : return {};
239 : }
240 :
241 3 : git_commit* head_ptr = nullptr;
242 3 : if (git_commit_lookup(&head_ptr, repo, &commit_id) < 0) {
243 0 : JAMI_ERROR("Unable to look up HEAD commit");
244 0 : return {};
245 : }
246 3 : GitCommit head_commit {head_ptr, git_commit_free};
247 :
248 3 : git_buf to_sign = {};
249 : #if( LIBGIT2_VER_MAJOR > 1 ) || ( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR >= 8 )
250 : // For libgit2 version 1.8.0 and above
251 3 : git_commit* const head_ref[1] = {head_commit.get()};
252 : #else
253 : // For libgit2 versions older than 1.8.0
254 : const git_commit* head_ref[1] = {head_commit.get()};
255 : #endif
256 6 : if (git_commit_create_buffer(
257 6 : &to_sign, repo, sig.get(), sig.get(), nullptr, msg.c_str(), tree.get(), 1, &head_ref[0])
258 3 : < 0) {
259 0 : JAMI_ERROR("Unable to create commit buffer");
260 0 : return {};
261 : }
262 :
263 : // git commit -S
264 3 : auto to_sign_vec = std::vector<uint8_t>(to_sign.ptr, to_sign.ptr + to_sign.size);
265 3 : auto signed_buf = account->identity().first->sign(to_sign_vec);
266 3 : std::string signed_str = base64::encode(signed_buf);
267 3 : if (git_commit_create_with_signature(&commit_id,
268 : repo,
269 3 : to_sign.ptr,
270 : signed_str.c_str(),
271 : "signature")
272 3 : < 0) {
273 0 : const git_error* err = giterr_last();
274 0 : if (err)
275 0 : JAMI_ERROR("Unable to sign commit: {}", err->message);
276 0 : return {};
277 : }
278 :
279 : // Move commit to main branch
280 3 : git_reference* ref_ptr = nullptr;
281 3 : if (git_reference_create(&ref_ptr, repo, "refs/heads/main", &commit_id, true, nullptr) < 0) {
282 0 : const git_error* err = giterr_last();
283 0 : if (err)
284 0 : JAMI_ERROR("Unable to move commit to main: {}", err->message);
285 0 : return {};
286 : }
287 3 : git_reference_free(ref_ptr);
288 3 : git_repository_free(repo);
289 :
290 3 : auto commit_str = git_oid_tostr_s(&commit_id);
291 3 : if (commit_str) {
292 9 : JAMI_LOG("New message added with id: {}", commit_str);
293 3 : return commit_str;
294 : }
295 :
296 0 : return {};
297 3 : }
298 :
299 : } // namespace jami
300 :
301 : #pragma GCC diagnostic pop
|