1 | #include <base/system.h> |
2 | #include <engine/favorites.h> |
3 | #include <engine/shared/config.h> |
4 | #include <engine/shared/protocol.h> |
5 | |
6 | #include <algorithm> |
7 | #include <unordered_map> |
8 | #include <vector> |
9 | |
10 | class CFavorites : public IFavorites |
11 | { |
12 | protected: |
13 | void OnConfigSave(IConfigManager *pConfigManager) override; |
14 | |
15 | public: |
16 | TRISTATE IsFavorite(const NETADDR *pAddrs, int NumAddrs) const override; |
17 | TRISTATE IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const override; |
18 | void Add(const NETADDR *pAddrs, int NumAddrs) override; |
19 | void AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) override; |
20 | void Remove(const NETADDR *pAddrs, int NumAddrs) override; |
21 | void AllEntries(const CEntry **ppEntries, int *pNumEntries) override; |
22 | |
23 | private: |
24 | std::vector<CEntry> m_vEntries; |
25 | std::unordered_map<NETADDR, int> m_ByAddr; |
26 | |
27 | CEntry *Entry(const NETADDR &Addr); |
28 | const CEntry *Entry(const NETADDR &Addr) const; |
29 | // `pEntry` must come from the `m_vEntries` vector. |
30 | void RemoveEntry(CEntry *pEntry); |
31 | }; |
32 | |
33 | void CFavorites::OnConfigSave(IConfigManager *pConfigManager) |
34 | { |
35 | for(const auto &Entry : m_vEntries) |
36 | { |
37 | if(Entry.m_NumAddrs > 1) |
38 | { |
39 | pConfigManager->WriteLine(pLine: "begin_favorite_group" ); |
40 | } |
41 | for(int i = 0; i < Entry.m_NumAddrs; i++) |
42 | { |
43 | char aAddr[NETADDR_MAXSTRSIZE]; |
44 | char aBuffer[128]; |
45 | net_addr_str(addr: &Entry.m_aAddrs[i], string: aAddr, max_length: sizeof(aAddr), add_port: true); |
46 | if(!Entry.m_AllowPing) |
47 | { |
48 | str_format(buffer: aBuffer, buffer_size: sizeof(aBuffer), format: "add_favorite %s" , aAddr); |
49 | } |
50 | else |
51 | { |
52 | // Add quotes to the first parameter for backward |
53 | // compatibility with versions that took a `r` console |
54 | // parameter. |
55 | str_format(buffer: aBuffer, buffer_size: sizeof(aBuffer), format: "add_favorite \"%s\" allow_ping" , aAddr); |
56 | } |
57 | pConfigManager->WriteLine(pLine: aBuffer); |
58 | } |
59 | if(Entry.m_NumAddrs > 1) |
60 | { |
61 | pConfigManager->WriteLine(pLine: "end_favorite_group" ); |
62 | } |
63 | } |
64 | } |
65 | |
66 | TRISTATE CFavorites::IsFavorite(const NETADDR *pAddrs, int NumAddrs) const |
67 | { |
68 | bool All = true; |
69 | bool None = true; |
70 | for(int i = 0; i < NumAddrs && (All || None); i++) |
71 | { |
72 | const CEntry *pEntry = Entry(Addr: pAddrs[i]); |
73 | if(pEntry) |
74 | { |
75 | None = false; |
76 | } |
77 | else |
78 | { |
79 | All = false; |
80 | } |
81 | } |
82 | // Return ALL if no addresses were passed. |
83 | if(All) |
84 | { |
85 | return TRISTATE::ALL; |
86 | } |
87 | else if(None) |
88 | { |
89 | return TRISTATE::NONE; |
90 | } |
91 | else |
92 | { |
93 | return TRISTATE::SOME; |
94 | } |
95 | } |
96 | |
97 | TRISTATE CFavorites::IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const |
98 | { |
99 | bool All = true; |
100 | bool None = true; |
101 | for(int i = 0; i < NumAddrs && (All || None); i++) |
102 | { |
103 | const CEntry *pEntry = Entry(Addr: pAddrs[i]); |
104 | if(pEntry == nullptr) |
105 | { |
106 | continue; |
107 | } |
108 | if(pEntry->m_AllowPing) |
109 | { |
110 | None = false; |
111 | } |
112 | else |
113 | { |
114 | All = false; |
115 | } |
116 | } |
117 | // Return ALL if no addresses were passed. |
118 | if(All) |
119 | { |
120 | return TRISTATE::ALL; |
121 | } |
122 | else if(None) |
123 | { |
124 | return TRISTATE::NONE; |
125 | } |
126 | else |
127 | { |
128 | return TRISTATE::SOME; |
129 | } |
130 | } |
131 | |
132 | void CFavorites::Add(const NETADDR *pAddrs, int NumAddrs) |
133 | { |
134 | // First make sure that all the addresses are not registered for some |
135 | // other favorite. |
136 | for(int i = 0; i < NumAddrs; i++) |
137 | { |
138 | CEntry *pEntry = Entry(Addr: pAddrs[i]); |
139 | if(pEntry == nullptr) |
140 | { |
141 | continue; |
142 | } |
143 | for(int j = 0; j < pEntry->m_NumAddrs; j++) |
144 | { |
145 | if(pEntry->m_aAddrs[j] == pAddrs[i]) |
146 | { |
147 | pEntry->m_aAddrs[j] = pEntry->m_aAddrs[pEntry->m_NumAddrs - 1]; |
148 | pEntry->m_NumAddrs -= 1; |
149 | break; |
150 | } |
151 | } |
152 | // If the entry has become empty due to the cleaning, remove it |
153 | // completely. |
154 | if(pEntry->m_NumAddrs == 0) |
155 | { |
156 | RemoveEntry(pEntry); |
157 | } |
158 | } |
159 | // Add the new entry. |
160 | CEntry NewEntry; |
161 | mem_zero(block: &NewEntry, size: sizeof(NewEntry)); |
162 | NewEntry.m_NumAddrs = std::min(a: NumAddrs, b: (int)std::size(NewEntry.m_aAddrs)); |
163 | for(int i = 0; i < NewEntry.m_NumAddrs; i++) |
164 | { |
165 | NewEntry.m_aAddrs[i] = pAddrs[i]; |
166 | m_ByAddr[pAddrs[i]] = m_vEntries.size(); |
167 | } |
168 | NewEntry.m_AllowPing = false; |
169 | m_vEntries.push_back(x: NewEntry); |
170 | } |
171 | |
172 | void CFavorites::AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) |
173 | { |
174 | for(int i = 0; i < NumAddrs; i++) |
175 | { |
176 | CEntry *pEntry = Entry(Addr: pAddrs[i]); |
177 | if(pEntry == nullptr) |
178 | { |
179 | continue; |
180 | } |
181 | pEntry->m_AllowPing = AllowPing; |
182 | } |
183 | } |
184 | |
185 | void CFavorites::Remove(const NETADDR *pAddrs, int NumAddrs) |
186 | { |
187 | for(int i = 0; i < NumAddrs; i++) |
188 | { |
189 | CEntry *pEntry = Entry(Addr: pAddrs[i]); |
190 | if(pEntry == nullptr) |
191 | { |
192 | continue; |
193 | } |
194 | for(int j = 0; j < pEntry->m_NumAddrs; j++) |
195 | { |
196 | m_ByAddr.erase(x: pEntry->m_aAddrs[j]); |
197 | } |
198 | RemoveEntry(pEntry); |
199 | } |
200 | } |
201 | |
202 | void CFavorites::AllEntries(const CEntry **ppEntries, int *pNumEntries) |
203 | { |
204 | *ppEntries = m_vEntries.data(); |
205 | *pNumEntries = m_vEntries.size(); |
206 | } |
207 | |
208 | CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) |
209 | { |
210 | auto Entry = m_ByAddr.find(x: Addr); |
211 | if(Entry == m_ByAddr.end()) |
212 | { |
213 | return nullptr; |
214 | } |
215 | return &m_vEntries[Entry->second]; |
216 | } |
217 | |
218 | const CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const |
219 | { |
220 | auto Entry = m_ByAddr.find(x: Addr); |
221 | if(Entry == m_ByAddr.end()) |
222 | { |
223 | return nullptr; |
224 | } |
225 | return &m_vEntries[Entry->second]; |
226 | } |
227 | |
228 | void CFavorites::RemoveEntry(CEntry *pEntry) |
229 | { |
230 | // Replace the entry |
231 | int Index = pEntry - m_vEntries.data(); |
232 | *pEntry = m_vEntries[m_vEntries.size() - 1]; |
233 | m_vEntries.pop_back(); |
234 | if(Index != (int)m_vEntries.size()) |
235 | { |
236 | for(int i = 0; i < pEntry->m_NumAddrs; i++) |
237 | { |
238 | m_ByAddr.at(k: pEntry->m_aAddrs[i]) = Index; |
239 | } |
240 | } |
241 | } |
242 | |
243 | void IFavorites::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData) |
244 | { |
245 | ((IFavorites *)pUserData)->OnConfigSave(pConfigManager); |
246 | } |
247 | |
248 | std::unique_ptr<IFavorites> CreateFavorites() |
249 | { |
250 | return std::make_unique<CFavorites>(); |
251 | } |
252 | |