librats 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/binding.gyp +1 -0
  2. package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
  3. package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
  4. package/native-src/CMakeLists.txt +360 -0
  5. package/native-src/LICENSE +21 -0
  6. package/native-src/src/bencode.cpp +485 -0
  7. package/native-src/src/bencode.h +145 -0
  8. package/native-src/src/bittorrent.cpp +3682 -0
  9. package/native-src/src/bittorrent.h +731 -0
  10. package/native-src/src/dht.cpp +2342 -0
  11. package/native-src/src/dht.h +501 -0
  12. package/native-src/src/encrypted_socket.cpp +817 -0
  13. package/native-src/src/encrypted_socket.h +239 -0
  14. package/native-src/src/file_transfer.cpp +1808 -0
  15. package/native-src/src/file_transfer.h +567 -0
  16. package/native-src/src/fs.cpp +639 -0
  17. package/native-src/src/fs.h +108 -0
  18. package/native-src/src/gossipsub.cpp +1137 -0
  19. package/native-src/src/gossipsub.h +403 -0
  20. package/native-src/src/ice.cpp +1386 -0
  21. package/native-src/src/ice.h +328 -0
  22. package/native-src/src/json.hpp +25526 -0
  23. package/native-src/src/krpc.cpp +558 -0
  24. package/native-src/src/krpc.h +145 -0
  25. package/native-src/src/librats.cpp +2715 -0
  26. package/native-src/src/librats.h +1729 -0
  27. package/native-src/src/librats_bittorrent.cpp +167 -0
  28. package/native-src/src/librats_c.cpp +1317 -0
  29. package/native-src/src/librats_c.h +237 -0
  30. package/native-src/src/librats_encryption.cpp +123 -0
  31. package/native-src/src/librats_file_transfer.cpp +226 -0
  32. package/native-src/src/librats_gossipsub.cpp +293 -0
  33. package/native-src/src/librats_ice.cpp +515 -0
  34. package/native-src/src/librats_logging.cpp +158 -0
  35. package/native-src/src/librats_mdns.cpp +171 -0
  36. package/native-src/src/librats_nat.cpp +571 -0
  37. package/native-src/src/librats_persistence.cpp +815 -0
  38. package/native-src/src/logger.h +412 -0
  39. package/native-src/src/mdns.cpp +1178 -0
  40. package/native-src/src/mdns.h +253 -0
  41. package/native-src/src/network_utils.cpp +598 -0
  42. package/native-src/src/network_utils.h +162 -0
  43. package/native-src/src/noise.cpp +981 -0
  44. package/native-src/src/noise.h +227 -0
  45. package/native-src/src/os.cpp +371 -0
  46. package/native-src/src/os.h +40 -0
  47. package/native-src/src/rats_export.h +17 -0
  48. package/native-src/src/sha1.cpp +163 -0
  49. package/native-src/src/sha1.h +42 -0
  50. package/native-src/src/socket.cpp +1376 -0
  51. package/native-src/src/socket.h +309 -0
  52. package/native-src/src/stun.cpp +484 -0
  53. package/native-src/src/stun.h +349 -0
  54. package/native-src/src/threadmanager.cpp +105 -0
  55. package/native-src/src/threadmanager.h +53 -0
  56. package/native-src/src/tracker.cpp +1110 -0
  57. package/native-src/src/tracker.h +268 -0
  58. package/native-src/src/version.cpp +24 -0
  59. package/native-src/src/version.h.in +45 -0
  60. package/native-src/version.rc.in +31 -0
  61. package/package.json +2 -8
  62. package/scripts/build-librats.js +59 -12
  63. package/scripts/prepare-package.js +133 -37
@@ -0,0 +1,515 @@
1
+ #include "librats.h"
2
+ #include "ice.h"
3
+ #include <algorithm>
4
+ #include <random>
5
+
6
+ // Logging macros for ICE operations
7
+ #ifdef TESTING
8
+ #define LOG_ICE_DEBUG(message) LOG_DEBUG("ice", "[pointer: " << this << "] " << message)
9
+ #define LOG_ICE_INFO(message) LOG_INFO("ice", "[pointer: " << this << "] " << message)
10
+ #define LOG_ICE_WARN(message) LOG_WARN("ice", "[pointer: " << this << "] " << message)
11
+ #define LOG_ICE_ERROR(message) LOG_ERROR("ice", "[pointer: " << this << "] " << message)
12
+ #else
13
+ #define LOG_ICE_DEBUG(message) LOG_DEBUG("ice", message)
14
+ #define LOG_ICE_INFO(message) LOG_INFO("ice", message)
15
+ #define LOG_ICE_WARN(message) LOG_WARN("ice", message)
16
+ #define LOG_ICE_ERROR(message) LOG_ERROR("ice", message)
17
+ #endif
18
+
19
+ namespace librats {
20
+
21
+ //=============================================================================
22
+ // ICE Connection Attempts
23
+ //=============================================================================
24
+
25
+ bool RatsClient::attempt_ice_connection(const std::string& host, int port, ConnectionAttemptResult& result) {
26
+ LOG_ICE_DEBUG("Attempting ICE connection to " << host << ":" << port);
27
+
28
+ if (!ice_agent_ || !ice_agent_->is_running()) {
29
+ result.error_message = "ICE agent not available or not running";
30
+ return false;
31
+ }
32
+
33
+ try {
34
+ // For now, use direct connection and enhance with ICE coordination later
35
+ // Full ICE implementation would require signaling channel for candidate exchange
36
+ LOG_ICE_INFO("ICE coordination requires signaling channel - using enhanced direct connection");
37
+
38
+ bool success = attempt_direct_connection(host, port, result);
39
+ if (success) {
40
+ result.method = "ice_direct";
41
+ // In a full implementation, we would:
42
+ // 1. Gather local candidates
43
+ // 2. Exchange candidates via signaling
44
+ // 3. Perform connectivity checks
45
+ // 4. Establish connection on best candidate pair
46
+ }
47
+
48
+ return success;
49
+
50
+ } catch (const std::exception& e) {
51
+ result.error_message = "ICE connection failed: " + std::string(e.what());
52
+ return false;
53
+ }
54
+ }
55
+
56
+ //=============================================================================
57
+ // ICE Offer/Answer Processing
58
+ //=============================================================================
59
+
60
+ nlohmann::json RatsClient::create_ice_offer(const std::string& peer_id) {
61
+ LOG_ICE_INFO("Creating ICE offer for peer " << peer_id);
62
+
63
+ nlohmann::json offer;
64
+ offer["type"] = "ice_offer";
65
+ offer["peer_id"] = get_our_peer_id();
66
+ offer["target_peer_id"] = peer_id;
67
+
68
+ if (ice_agent_ && ice_agent_->is_running()) {
69
+ // Get local credentials
70
+ auto credentials = ice_agent_->get_local_credentials();
71
+ offer["ice_ufrag"] = credentials.first;
72
+ offer["ice_pwd"] = credentials.second;
73
+
74
+ // Get local candidates
75
+ auto candidates = ice_agent_->get_local_candidates();
76
+ nlohmann::json candidates_array = nlohmann::json::array();
77
+
78
+ for (const auto& candidate : candidates) {
79
+ nlohmann::json cand_json;
80
+ cand_json["ip"] = candidate.ip;
81
+ cand_json["port"] = candidate.port;
82
+ cand_json["type"] = static_cast<int>(candidate.type);
83
+ cand_json["priority"] = candidate.priority;
84
+ cand_json["foundation"] = candidate.foundation;
85
+ candidates_array.push_back(cand_json);
86
+ }
87
+
88
+ offer["candidates"] = candidates_array;
89
+ } else {
90
+ LOG_ICE_WARN("ICE agent not available for ICE offer");
91
+ offer["error"] = "ICE agent not available";
92
+ }
93
+
94
+ return offer;
95
+ }
96
+
97
+ bool RatsClient::connect_with_ice(const std::string& peer_id, const nlohmann::json& ice_offer) {
98
+ LOG_ICE_INFO("Connecting with ICE to peer " << peer_id);
99
+
100
+ if (!ice_agent_ || !ice_agent_->is_running()) {
101
+ LOG_ICE_ERROR("ICE agent not available for ICE connection");
102
+ return false;
103
+ }
104
+
105
+ try {
106
+ // Extract ICE credentials
107
+ std::string remote_ufrag = ice_offer.value("ice_ufrag", "");
108
+ std::string remote_pwd = ice_offer.value("ice_pwd", "");
109
+
110
+ if (remote_ufrag.empty() || remote_pwd.empty()) {
111
+ LOG_ICE_ERROR("Invalid ICE offer - missing credentials");
112
+ return false;
113
+ }
114
+
115
+ // Set remote credentials
116
+ ice_agent_->set_remote_credentials(remote_ufrag, remote_pwd);
117
+
118
+ // Add remote candidates
119
+ if (ice_offer.contains("candidates")) {
120
+ for (const auto& cand_json : ice_offer["candidates"]) {
121
+ IceCandidate candidate;
122
+ candidate.ip = cand_json.value("ip", "");
123
+ candidate.port = cand_json.value("port", 0);
124
+ candidate.type = static_cast<IceCandidateType>(cand_json.value("type", 0));
125
+ candidate.priority = cand_json.value("priority", 0);
126
+ candidate.foundation = cand_json.value("foundation", "");
127
+
128
+ if (!candidate.ip.empty() && candidate.port > 0) {
129
+ ice_agent_->add_remote_candidate(candidate);
130
+ LOG_ICE_DEBUG("Added remote ICE candidate: " << candidate.ip << ":" << candidate.port);
131
+ }
132
+ }
133
+ }
134
+
135
+ // Start connectivity checks
136
+ ice_agent_->start_connectivity_checks();
137
+
138
+ LOG_ICE_INFO("ICE connectivity checks started for peer " << peer_id);
139
+ return true;
140
+
141
+ } catch (const std::exception& e) {
142
+ LOG_ICE_ERROR("ICE connection failed: " << e.what());
143
+ return false;
144
+ }
145
+ }
146
+
147
+ bool RatsClient::handle_ice_answer(const std::string& peer_id, const nlohmann::json& ice_answer) {
148
+ LOG_ICE_INFO("Handling ICE answer from peer " << peer_id);
149
+
150
+ // ICE answer handling is similar to ICE offer processing
151
+ return connect_with_ice(peer_id, ice_answer);
152
+ }
153
+
154
+ //=============================================================================
155
+ // ICE Coordination and Initiation
156
+ //=============================================================================
157
+
158
+ void RatsClient::initiate_ice_with_peer(const std::string& peer_id, const std::string& host, int port) {
159
+ if (!ice_agent_ || !ice_agent_->is_running()) {
160
+ LOG_ICE_WARN("ICE agent not available for ICE initiation");
161
+ return;
162
+ }
163
+
164
+ LOG_ICE_INFO("Initiating ICE coordination with peer " << peer_id);
165
+
166
+ try {
167
+ // Gather local candidates if not already done
168
+ ice_agent_->gather_candidates();
169
+
170
+ // Create ICE offer
171
+ nlohmann::json ice_offer = create_ice_offer(peer_id);
172
+
173
+ // Send ICE offer via message system
174
+ nlohmann::json offer_message = create_rats_message("ice_offer", ice_offer, get_our_peer_id());
175
+
176
+ // Find peer socket to send offer
177
+ socket_t peer_socket = get_peer_socket_by_id(peer_id);
178
+
179
+ if (is_valid_socket(peer_socket)) {
180
+ if (send_json_to_peer(peer_socket, offer_message)) {
181
+ LOG_ICE_INFO("Sent ICE offer to peer " << peer_id);
182
+ } else {
183
+ LOG_ICE_ERROR("Failed to send ICE offer to peer " << peer_id);
184
+ }
185
+ } else {
186
+ LOG_ICE_ERROR("Cannot send ICE offer - peer socket not found for " << peer_id);
187
+ }
188
+
189
+ } catch (const std::exception& e) {
190
+ LOG_ICE_ERROR("Failed to initiate ICE with peer: " << e.what());
191
+ }
192
+ }
193
+
194
+ //=============================================================================
195
+ // ICE Message Handlers
196
+ //=============================================================================
197
+
198
+ void RatsClient::handle_ice_offer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
199
+ LOG_ICE_INFO("Received ICE offer from peer " << peer_hash_id);
200
+
201
+ if (!ice_agent_ || !ice_agent_->is_running()) {
202
+ LOG_ICE_WARN("ICE agent not available - cannot handle ICE offer");
203
+ return;
204
+ }
205
+
206
+ try {
207
+ // Extract offer details
208
+ std::string target_peer_id = payload.value("target_peer_id", "");
209
+ std::string offer_peer_id = payload.value("peer_id", "");
210
+
211
+ // Verify this offer is for us
212
+ if (target_peer_id != get_our_peer_id()) {
213
+ LOG_ICE_WARN("ICE offer not intended for us (target: " << target_peer_id << ", us: " << get_our_peer_id() << ")");
214
+ return;
215
+ }
216
+
217
+ // Update peer with ICE information
218
+ update_peer_ice_info(socket, payload);
219
+
220
+ // Process the ICE offer
221
+ connect_with_ice(offer_peer_id, payload);
222
+
223
+ // Create and send ICE answer
224
+ nlohmann::json ice_answer = create_ice_offer(offer_peer_id); // Create our offer as answer
225
+ ice_answer["type"] = "ice_answer";
226
+ ice_answer["target_peer_id"] = offer_peer_id;
227
+ ice_answer["peer_id"] = get_our_peer_id();
228
+
229
+ nlohmann::json answer_message = create_rats_message("ice_answer", ice_answer, get_our_peer_id());
230
+
231
+ if (send_json_to_peer(socket, answer_message)) {
232
+ LOG_ICE_INFO("Sent ICE answer to peer " << peer_hash_id);
233
+ } else {
234
+ LOG_ICE_ERROR("Failed to send ICE answer to peer " << peer_hash_id);
235
+ }
236
+
237
+ } catch (const std::exception& e) {
238
+ LOG_ICE_ERROR("Failed to handle ICE offer: " << e.what());
239
+ }
240
+ }
241
+
242
+ void RatsClient::handle_ice_answer_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
243
+ LOG_ICE_INFO("Received ICE answer from peer " << peer_hash_id);
244
+
245
+ if (!ice_agent_ || !ice_agent_->is_running()) {
246
+ LOG_ICE_WARN("ICE agent not available - cannot handle ICE answer");
247
+ return;
248
+ }
249
+
250
+ try {
251
+ std::string answer_peer_id = payload.value("peer_id", "");
252
+
253
+ // Update peer with ICE information
254
+ update_peer_ice_info(socket, payload);
255
+
256
+ // Set ICE state to checking
257
+ {
258
+ std::lock_guard<std::mutex> lock(peers_mutex_);
259
+ auto socket_it = socket_to_peer_id_.find(socket);
260
+ if (socket_it != socket_to_peer_id_.end()) {
261
+ auto peer_it = peers_.find(socket_it->second);
262
+ if (peer_it != peers_.end()) {
263
+ peer_it->second.ice_state = IceConnectionState::CHECKING;
264
+ }
265
+ }
266
+ }
267
+
268
+ // Process the ICE answer
269
+ handle_ice_answer(answer_peer_id, payload);
270
+
271
+ } catch (const std::exception& e) {
272
+ LOG_ICE_ERROR("Failed to handle ICE answer: " << e.what());
273
+ }
274
+ }
275
+
276
+ void RatsClient::handle_ice_candidate_message(socket_t socket, const std::string& peer_hash_id, const nlohmann::json& payload) {
277
+ LOG_ICE_DEBUG("Received ICE candidate from peer " << peer_hash_id);
278
+
279
+ if (!ice_agent_ || !ice_agent_->is_running()) {
280
+ LOG_ICE_WARN("ICE agent not available - cannot handle ICE candidate");
281
+ return;
282
+ }
283
+
284
+ try {
285
+ // Extract candidate information
286
+ IceCandidate candidate;
287
+ candidate.ip = payload.value("ip", "");
288
+ candidate.port = payload.value("port", 0);
289
+ candidate.type = static_cast<IceCandidateType>(payload.value("type", 0));
290
+ candidate.priority = payload.value("priority", 0);
291
+ candidate.foundation = payload.value("foundation", "");
292
+ candidate.component_id = payload.value("component_id", 1);
293
+
294
+ if (!candidate.ip.empty() && candidate.port > 0) {
295
+ // Add candidate to ICE agent
296
+ ice_agent_->add_remote_candidate(candidate);
297
+
298
+ // Update peer candidate list
299
+ add_candidate_to_peer(socket, candidate);
300
+
301
+ LOG_ICE_DEBUG("Added remote ICE candidate: " << candidate.ip << ":" << candidate.port
302
+ << " type=" << static_cast<int>(candidate.type));
303
+ } else {
304
+ LOG_ICE_WARN("Invalid ICE candidate received from peer " << peer_hash_id);
305
+ }
306
+
307
+ } catch (const std::exception& e) {
308
+ LOG_ICE_ERROR("Failed to handle ICE candidate: " << e.what());
309
+ }
310
+ }
311
+
312
+ //=============================================================================
313
+ // ICE Helper Functions
314
+ //=============================================================================
315
+
316
+ void RatsClient::update_peer_ice_info(socket_t socket, const nlohmann::json& payload) {
317
+ std::lock_guard<std::mutex> lock(peers_mutex_);
318
+ auto socket_it = socket_to_peer_id_.find(socket);
319
+ if (socket_it != socket_to_peer_id_.end()) {
320
+ auto peer_it = peers_.find(socket_it->second);
321
+ if (peer_it != peers_.end()) {
322
+ peer_it->second.ice_enabled = true;
323
+ peer_it->second.ice_ufrag = payload.value("ice_ufrag", "");
324
+ peer_it->second.ice_pwd = payload.value("ice_pwd", "");
325
+ }
326
+ }
327
+ }
328
+
329
+ void RatsClient::add_candidate_to_peer(socket_t socket, const IceCandidate& candidate) {
330
+ std::lock_guard<std::mutex> lock(peers_mutex_);
331
+ auto socket_it = socket_to_peer_id_.find(socket);
332
+ if (socket_it != socket_to_peer_id_.end()) {
333
+ auto peer_it = peers_.find(socket_it->second);
334
+ if (peer_it != peers_.end()) {
335
+ peer_it->second.ice_candidates.push_back(candidate);
336
+ }
337
+ }
338
+ }
339
+
340
+ //=============================================================================
341
+ // ICE Coordination Management
342
+ //=============================================================================
343
+
344
+ bool RatsClient::should_initiate_ice_coordination(const std::string& peer_id) {
345
+ std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
346
+ return ice_coordination_in_progress_.find(peer_id) == ice_coordination_in_progress_.end();
347
+ }
348
+
349
+ void RatsClient::mark_ice_coordination_in_progress(const std::string& peer_id) {
350
+ std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
351
+ ice_coordination_in_progress_.insert(peer_id);
352
+ }
353
+
354
+ void RatsClient::remove_ice_coordination_tracking(const std::string& peer_id) {
355
+ std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
356
+ ice_coordination_in_progress_.erase(peer_id);
357
+ }
358
+
359
+ void RatsClient::cleanup_ice_coordination_for_peer(const std::string& peer_id) {
360
+ try {
361
+ remove_ice_coordination_tracking(peer_id);
362
+ } catch (...) {
363
+ // Ignore cleanup errors to prevent recursive exceptions
364
+ LOG_ICE_DEBUG("Exception during ICE coordination cleanup for peer " << peer_id);
365
+ }
366
+ }
367
+
368
+ //=============================================================================
369
+ // ICE Callbacks and State Management
370
+ //=============================================================================
371
+
372
+ void RatsClient::setup_ice_callbacks() {
373
+ if (!ice_agent_) {
374
+ return;
375
+ }
376
+
377
+ // Set ICE candidate discovery callback
378
+ ice_agent_->set_candidate_callback([this](const IceCandidate& candidate) {
379
+ LOG_ICE_INFO("ICE candidate discovered: " << candidate.ip << ":" << candidate.port
380
+ << " type=" << static_cast<int>(candidate.type)
381
+ << " priority=" << candidate.priority);
382
+
383
+ if (ice_candidate_callback_) {
384
+ ice_candidate_callback_("", candidate); // Empty peer_id for local candidates
385
+ }
386
+ });
387
+
388
+ // Set ICE state change callback
389
+ ice_agent_->set_state_change_callback([this](IceConnectionState state) {
390
+ LOG_ICE_INFO("ICE connection state changed: " << static_cast<int>(state));
391
+
392
+ if (nat_progress_callback_) {
393
+ std::string status = "ICE state: " + std::to_string(static_cast<int>(state));
394
+ nat_progress_callback_("", status); // Empty peer_id for general ICE state
395
+ }
396
+ });
397
+ }
398
+
399
+ void RatsClient::initialize_ice_agent() {
400
+ if (!nat_config_.enable_ice) {
401
+ LOG_ICE_DEBUG("ICE is disabled in configuration");
402
+ return;
403
+ }
404
+
405
+ LOG_ICE_INFO("Initializing ICE agent");
406
+
407
+ IceConfig ice_config;
408
+ ice_config.stun_servers = nat_config_.stun_servers;
409
+ ice_config.turn_servers = nat_config_.turn_servers;
410
+ ice_config.turn_usernames = nat_config_.turn_usernames;
411
+ ice_config.turn_passwords = nat_config_.turn_passwords;
412
+ ice_config.stun_timeout_ms = 5000;
413
+ ice_config.turn_timeout_ms = nat_config_.turn_allocation_timeout_ms;
414
+ ice_config.connectivity_check_timeout_ms = nat_config_.ice_connectivity_timeout_ms;
415
+ ice_config.enable_host_candidates = true;
416
+ ice_config.enable_server_reflexive_candidates = true;
417
+ ice_config.enable_relay_candidates = nat_config_.enable_turn_relay;
418
+
419
+ ice_agent_ = std::make_unique<IceAgent>(IceRole::CONTROLLING, ice_config);
420
+
421
+ // Setup callbacks
422
+ setup_ice_callbacks();
423
+
424
+ LOG_ICE_INFO("ICE agent initialized successfully");
425
+ }
426
+
427
+ //=============================================================================
428
+ // ICE Statistics and Information
429
+ //=============================================================================
430
+
431
+ nlohmann::json RatsClient::get_ice_statistics() const {
432
+ nlohmann::json stats;
433
+
434
+ if (ice_agent_) {
435
+ stats["available"] = true;
436
+ stats["running"] = ice_agent_->is_running();
437
+ stats["state"] = static_cast<int>(ice_agent_->get_connection_state());
438
+
439
+ if (ice_agent_->is_running()) {
440
+ auto local_candidates = ice_agent_->get_local_candidates();
441
+ stats["local_candidates"] = local_candidates.size();
442
+
443
+ // Get candidate details
444
+ nlohmann::json candidates_array = nlohmann::json::array();
445
+ for (const auto& candidate : local_candidates) {
446
+ nlohmann::json cand_json;
447
+ cand_json["ip"] = candidate.ip;
448
+ cand_json["port"] = candidate.port;
449
+ cand_json["type"] = static_cast<int>(candidate.type);
450
+ cand_json["priority"] = candidate.priority;
451
+ candidates_array.push_back(cand_json);
452
+ }
453
+ stats["local_candidates_details"] = candidates_array;
454
+ }
455
+
456
+ // Get peer ICE information
457
+ nlohmann::json ice_peers = nlohmann::json::array();
458
+ {
459
+ std::lock_guard<std::mutex> lock(peers_mutex_);
460
+ for (const auto& pair : peers_) {
461
+ const RatsPeer& peer = pair.second;
462
+ if (peer.ice_enabled) {
463
+ nlohmann::json peer_ice;
464
+ peer_ice["peer_id"] = peer.peer_id;
465
+ peer_ice["ice_state"] = static_cast<int>(peer.ice_state);
466
+ peer_ice["ufrag"] = peer.ice_ufrag;
467
+ peer_ice["candidates_count"] = peer.ice_candidates.size();
468
+ ice_peers.push_back(peer_ice);
469
+ }
470
+ }
471
+ }
472
+ stats["ice_peers"] = ice_peers;
473
+
474
+ } else {
475
+ stats["available"] = false;
476
+ }
477
+
478
+ return stats;
479
+ }
480
+
481
+ bool RatsClient::is_ice_enabled() const {
482
+ return ice_agent_ && ice_agent_->is_running();
483
+ }
484
+
485
+ bool RatsClient::is_peer_ice_connected(const std::string& peer_id) const {
486
+ std::lock_guard<std::mutex> lock(peers_mutex_);
487
+ auto it = peers_.find(peer_id);
488
+ if (it != peers_.end()) {
489
+ return it->second.is_ice_connected();
490
+ }
491
+ return false;
492
+ }
493
+
494
+ //=============================================================================
495
+ // ICE Cleanup and Shutdown
496
+ //=============================================================================
497
+
498
+ void RatsClient::cleanup_ice_resources() {
499
+ LOG_ICE_DEBUG("Cleaning up ICE resources");
500
+
501
+ // Stop ICE agent
502
+ if (ice_agent_ && ice_agent_->is_running()) {
503
+ ice_agent_->stop();
504
+ }
505
+
506
+ // Clear ICE coordination tracking
507
+ {
508
+ std::lock_guard<std::mutex> ice_lock(ice_coordination_mutex_);
509
+ ice_coordination_in_progress_.clear();
510
+ }
511
+
512
+ LOG_ICE_DEBUG("ICE resources cleaned up");
513
+ }
514
+
515
+ } // namespace librats
@@ -0,0 +1,158 @@
1
+ #include "librats.h"
2
+ #include "logger.h"
3
+ #include <algorithm>
4
+
5
+ // Logging macros for RatsClient
6
+ #define LOG_CLIENT_DEBUG(message) LOG_DEBUG("client", message)
7
+ #define LOG_CLIENT_INFO(message) LOG_INFO("client", message)
8
+ #define LOG_CLIENT_WARN(message) LOG_WARN("client", message)
9
+ #define LOG_CLIENT_ERROR(message) LOG_ERROR("client", message)
10
+
11
+ namespace librats {
12
+
13
+ //=============================================================================
14
+ // Logging Control API Implementation
15
+ //=============================================================================
16
+
17
+ void RatsClient::set_logging_enabled(bool enabled) {
18
+ LOG_CLIENT_INFO("Setting file logging " << (enabled ? "enabled" : "disabled"));
19
+
20
+ Logger& logger = Logger::getInstance();
21
+
22
+ if (enabled) {
23
+ // Set default log file path if not already set
24
+ if (logger.get_log_file_path().empty()) {
25
+ logger.set_log_file_path("rats.log");
26
+ }
27
+ }
28
+
29
+ logger.set_file_logging_enabled(enabled);
30
+
31
+ if (enabled) {
32
+ LOG_CLIENT_INFO("File logging enabled - logs will be written to: " << logger.get_log_file_path());
33
+ } else {
34
+ LOG_CLIENT_INFO("File logging disabled");
35
+ }
36
+ }
37
+
38
+ bool RatsClient::is_logging_enabled() const {
39
+ return Logger::getInstance().is_file_logging_enabled();
40
+ }
41
+
42
+ void RatsClient::set_log_file_path(const std::string& file_path) {
43
+ Logger& logger = Logger::getInstance();
44
+ logger.set_log_file_path(file_path);
45
+ LOG_CLIENT_INFO("Log file path set to: " << file_path);
46
+ }
47
+
48
+ std::string RatsClient::get_log_file_path() const {
49
+ return Logger::getInstance().get_log_file_path();
50
+ }
51
+
52
+ void RatsClient::set_log_level(LogLevel level) {
53
+ Logger::getInstance().set_log_level(level);
54
+ LOG_CLIENT_INFO("Log level set to: " << static_cast<int>(level));
55
+ }
56
+
57
+ void RatsClient::set_log_level(const std::string& level_str) {
58
+ LogLevel level;
59
+
60
+ std::string upper_level = level_str;
61
+ std::transform(upper_level.begin(), upper_level.end(), upper_level.begin(), ::toupper);
62
+
63
+ if (upper_level == "DEBUG") {
64
+ level = LogLevel::DEBUG;
65
+ } else if (upper_level == "INFO") {
66
+ level = LogLevel::INFO;
67
+ } else if (upper_level == "WARN" || upper_level == "WARNING") {
68
+ level = LogLevel::WARN;
69
+ } else if (upper_level == "ERROR") {
70
+ level = LogLevel::ERROR;
71
+ } else {
72
+ LOG_CLIENT_WARN("Invalid log level string: " << level_str << " - using INFO as default");
73
+ level = LogLevel::INFO;
74
+ }
75
+
76
+ set_log_level(level);
77
+ }
78
+
79
+ LogLevel RatsClient::get_log_level() const {
80
+ // Note: Logger doesn't expose get_log_level method, so we'll use a workaround
81
+ // by checking which level actually outputs. This is a limitation of the current Logger design.
82
+ Logger& logger = Logger::getInstance();
83
+
84
+ // Try to determine current level by checking if debug messages would be shown
85
+ // This is a simple approximation - for a more accurate implementation,
86
+ // the Logger class would need a get_log_level() method
87
+
88
+ // For now, we'll return INFO as a reasonable default
89
+ // In a production system, the Logger class should be enhanced to track and return the current level
90
+ return LogLevel::INFO;
91
+ }
92
+
93
+ void RatsClient::set_log_colors_enabled(bool enabled) {
94
+ Logger::getInstance().set_colors_enabled(enabled);
95
+ LOG_CLIENT_INFO("Log colors " << (enabled ? "enabled" : "disabled"));
96
+ }
97
+
98
+ bool RatsClient::is_log_colors_enabled() const {
99
+ // Note: Logger doesn't expose a getter for colors_enabled
100
+ // This is another limitation of the current Logger design
101
+ // For now, return true as a reasonable default
102
+ return true;
103
+ }
104
+
105
+ void RatsClient::set_log_timestamps_enabled(bool enabled) {
106
+ Logger::getInstance().set_timestamps_enabled(enabled);
107
+ LOG_CLIENT_INFO("Log timestamps " << (enabled ? "enabled" : "disabled"));
108
+ }
109
+
110
+ bool RatsClient::is_log_timestamps_enabled() const {
111
+ // Note: Logger doesn't expose a getter for timestamps_enabled
112
+ // This is another limitation of the current Logger design
113
+ // For now, return true as a reasonable default
114
+ return true;
115
+ }
116
+
117
+ void RatsClient::set_log_rotation_size(size_t max_size_bytes) {
118
+ Logger::getInstance().set_log_rotation_size(max_size_bytes);
119
+ LOG_CLIENT_INFO("Log rotation size set to: " << max_size_bytes << " bytes");
120
+ }
121
+
122
+ void RatsClient::set_log_retention_count(int count) {
123
+ Logger::getInstance().set_log_retention_count(count);
124
+ LOG_CLIENT_INFO("Log retention count set to: " << count << " files");
125
+ }
126
+
127
+ void RatsClient::clear_log_file() {
128
+ Logger& logger = Logger::getInstance();
129
+ std::string log_path = logger.get_log_file_path();
130
+
131
+ if (log_path.empty()) {
132
+ LOG_CLIENT_WARN("No log file path set - cannot clear log file");
133
+ return;
134
+ }
135
+
136
+ try {
137
+ // Temporarily disable file logging
138
+ bool was_enabled = logger.is_file_logging_enabled();
139
+ if (was_enabled) {
140
+ logger.set_file_logging_enabled(false);
141
+ }
142
+
143
+ // Delete the log file
144
+ std::remove(log_path.c_str());
145
+
146
+ // Re-enable file logging if it was enabled
147
+ if (was_enabled) {
148
+ logger.set_file_logging_enabled(true);
149
+ }
150
+
151
+ LOG_CLIENT_INFO("Log file cleared: " << log_path);
152
+
153
+ } catch (const std::exception& e) {
154
+ LOG_CLIENT_ERROR("Failed to clear log file: " << e.what());
155
+ }
156
+ }
157
+
158
+ }