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 : #pragma once
18 :
19 : #include <atomic>
20 : #include <functional>
21 : #include <memory>
22 : #include <mutex>
23 : #include <string>
24 : #include <string_view>
25 : #include <git2/remote.h>
26 : #include <git2/sys/transport.h>
27 : #include <git2/errors.h>
28 : #include <git2.h>
29 :
30 : namespace jami {
31 : class Manager;
32 : } // namespace jami
33 :
34 : namespace dhtnet {
35 : class ChannelSocketInterface;
36 : } // namespace dhtnet
37 :
38 : // NOTE: THIS MUST BE IN THE ROOT NAMESPACE FOR LIBGIT2
39 :
40 : struct P2PStream
41 : {
42 : git_smart_subtransport_stream base;
43 : std::weak_ptr<dhtnet::ChannelSocketInterface> socket;
44 :
45 : std::string cmd {};
46 : std::string url {};
47 : unsigned sent_command : 1;
48 : };
49 :
50 : struct P2PSubTransport
51 : {
52 : git_smart_subtransport base;
53 : std::unique_ptr<P2PStream> stream;
54 : git_remote* remote;
55 : };
56 :
57 : using namespace std::string_view_literals;
58 : constexpr auto UPLOAD_PACK_CMD = "git-upload-pack"sv;
59 : constexpr auto HOST_TAG = "host="sv;
60 :
61 : #ifdef LIBJAMI_TEST
62 : using P2PStallHook = std::function<void(std::string_view url)>;
63 :
64 : /**
65 : * Holds a fetch inside the transport, the way a peer that stops talking without
66 : * hanging up does. Called with the stream url so a test only stalls its own
67 : * conversation. Null by default.
68 : *
69 : * Held by shared_ptr because the reader blocks inside the hook for as long as
70 : * the stall lasts: the transport thread loads a strong reference and keeps the
71 : * callable alive while it runs, so a test can install, replace, or clear the
72 : * hook at any time without racing it. The lock only guards the pointer copy,
73 : * never the call, so the test can always take it back.
74 : * @note std::atomic<std::shared_ptr> is not available on every libc++.
75 : */
76 : class P2PStallHookHolder
77 : {
78 : public:
79 6833 : std::shared_ptr<P2PStallHook> load() const
80 : {
81 6833 : std::lock_guard lk(mtx_);
82 13668 : return hook_;
83 6834 : }
84 2 : void store(std::shared_ptr<P2PStallHook> hook)
85 : {
86 2 : std::lock_guard lk(mtx_);
87 2 : hook_ = std::move(hook);
88 2 : }
89 :
90 : private:
91 : mutable std::mutex mtx_;
92 : std::shared_ptr<P2PStallHook> hook_;
93 : };
94 : extern P2PStallHookHolder P2P_STALL_HOOK;
95 : #endif
96 :
97 : /**
98 : * Send a git command on the linked socket
99 : * @param s Related stream
100 : * @return 0 on success
101 : */
102 : int sendCmd(P2PStream* s);
103 :
104 : /**
105 : * Read on a channel socket
106 : * @param stream Related stream
107 : * @param buffer Buffer to fill
108 : * @param buflen Maximum buffer size
109 : * @param read Number of bytes read
110 : * @return 0 on success
111 : */
112 : int P2PStreamRead(git_smart_subtransport_stream* stream, char* buffer, size_t buflen, size_t* read);
113 :
114 : int P2PStreamWrite(git_smart_subtransport_stream* stream, const char* buffer, size_t len);
115 :
116 : /**
117 : * Free resources used by the stream
118 : */
119 : void P2PStreamFree(git_smart_subtransport_stream* stream);
120 :
121 : /**
122 : * Handles git actions
123 : * @param out Subtransport's stream created or used by the action
124 : * @param transport Subtransport created or used by the action
125 : * @param url 'deviceId/conversationId'
126 : * @param action Action to perform
127 : * @return 0 on success
128 : */
129 : int P2PSubTransportAction(git_smart_subtransport_stream** out,
130 : git_smart_subtransport* transport,
131 : const char* url,
132 : git_smart_service_t action);
133 :
134 : /**
135 : * Close a subtransport
136 : * Because we use a channel socket, we need to do nothing here.
137 : * Will be shutdown by the rest of the code
138 : */
139 : int P2PSubTransportClose(git_smart_subtransport*);
140 :
141 : /**
142 : * Free resources used by a transport
143 : * @param transport Transport to free
144 : */
145 : void P2PSubTransportFree(git_smart_subtransport* transport);
146 :
147 : /**
148 : * Create a new subtransport
149 : * @param out The new subtransport
150 : * @param owner The transport owning this subtransport
151 : * @param payload The remote
152 : * @return 0 on success
153 : */
154 : int P2PSubTransportNew(P2PSubTransport** out, git_transport* owner, void* payload);
155 :
156 : /**
157 : * Setup the subtransport callback
158 : * @param out Subtransport created
159 : * @param owner Transport owning the sub transport
160 : * @param param The remote
161 : * @param 0 on success
162 : */
163 : int p2p_subtransport_cb(git_smart_subtransport** out, git_transport* owner, void* payload);
164 :
165 : /**
166 : * Setup the transport callback
167 : * @param out Transport created
168 : * @param owner Remote wanted
169 : * @param 0 on success
170 : */
171 : int p2p_transport_cb(git_transport** out, git_remote* owner, void* param);
|