diff --git a/sharing/linux/nearby_sharing_app.cc b/sharing/linux/nearby_sharing_app.cc index 5b90aeb5..dffce13a 100644 --- a/sharing/linux/nearby_sharing_app.cc +++ b/sharing/linux/nearby_sharing_app.cc @@ -28,6 +28,7 @@ #include "sharing/transfer_update_callback.h" #include "sharing/share_target_discovered_callback.h" #include "internal/base/file_path.h" +#include "internal/base/files.h" using namespace nearby::sharing; using namespace nearby::sharing::linux; @@ -37,22 +38,24 @@ class MyTransferUpdateCallback : public TransferUpdateCallback { void OnTransferUpdate(const ShareTarget& share_target, const AttachmentContainer& attachment_container, const TransferMetadata& transfer_metadata) override { - std::cout << "\n=== Transfer Update ===" << std::endl; - std::cout << "Device: " << share_target.device_name << std::endl; - std::cout << "Target ID: " << share_target.id << " ← USE THIS ID" << std::endl; - std::cout << "Status: " << TransferMetadata::StatusToString(transfer_metadata.status()) << std::endl; - std::cout << "Progress: " << (transfer_metadata.progress() * 100) << "%" << std::endl; - std::cout << "Transferred: " << transfer_metadata.transferred_bytes() << " bytes" << std::endl; - std::cout << "Total attachments: " << transfer_metadata.total_attachments_count() << std::endl; + std::cout << "\n┌─────────────────────────────────────┐" << std::endl; + std::cout << "│ TRANSFER UPDATE │" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ Device: " << share_target.device_name << std::endl; + std::cout << "│ Target ID: " << share_target.id << " ← USE THIS ID" << std::endl; + std::cout << "│ Status: " << TransferMetadata::StatusToString(transfer_metadata.status()) << std::endl; + std::cout << "│ Progress: " << (transfer_metadata.progress() * 100) << "%" << std::endl; + std::cout << "│ Transferred: " << transfer_metadata.transferred_bytes() << " bytes" << std::endl; + std::cout << "│ Total attachments: " << transfer_metadata.total_attachments_count() << std::endl; // Track incoming transfer requests if (transfer_metadata.status() == TransferMetadata::Status::kAwaitingLocalConfirmation) { - std::cout << "\n*** INCOMING TRANSFER REQUEST ***" << std::endl; - std::cout << "From: " << share_target.device_name << std::endl; - std::cout << "Target ID: " << share_target.id << std::endl; - std::cout << "Use menu option 6 to ACCEPT (ID: " << share_target.id << ")" << std::endl; - std::cout << "Use menu option 7 to REJECT (ID: " << share_target.id << ")" << std::endl; - std::cout << "**********************************" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ ⚠️ INCOMING TRANSFER REQUEST │" << std::endl; + std::cout << "│ From: " << share_target.device_name << std::endl; + std::cout << "│ Target ID: " << share_target.id << std::endl; + std::cout << "│ → Press 6 to ACCEPT │" << std::endl; + std::cout << "│ → Press 7 to REJECT │" << std::endl; pending_target_id_ = share_target.id; has_pending_request_ = true; @@ -60,18 +63,19 @@ class MyTransferUpdateCallback : public TransferUpdateCallback { // Show completed transfers if (transfer_metadata.status() == TransferMetadata::Status::kComplete) { - std::cout << "\n*** TRANSFER COMPLETE ***" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ ✓ TRANSFER COMPLETE │" << std::endl; for (const auto& file : attachment_container.GetFileAttachments()) { - std::cout << "Received file: " << file.file_name() << std::endl; + std::cout << "│ Received file: " << file.file_name() << std::endl; } for (const auto& text : attachment_container.GetTextAttachments()) { - std::cout << "Received text: " << text.text_body() << std::endl; + std::cout << "│ Received text: " << text.text_body() << std::endl; } - std::cout << "*************************" << std::endl; has_pending_request_ = false; } - std::cout << "======================" << std::endl; + std::cout << "└─────────────────────────────────────┘" << std::endl; + PrintMenuAtBottom(); } bool HasPendingRequest() const { return has_pending_request_; } @@ -80,24 +84,42 @@ class MyTransferUpdateCallback : public TransferUpdateCallback { private: bool has_pending_request_ = false; int64_t pending_target_id_ = -1; + + void PrintMenuAtBottom() { + std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl; + std::cout << "║ NEARBY SHARING - MENU ║" << std::endl; + std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl; + std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl; + std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl; + std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl; + std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl; + std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl; + std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl; + std::cout << "Choice: "; + } }; class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback { public: void OnShareTargetDiscovered(const ShareTarget& share_target) override { - std::cout << "\n*** Device Discovered ***" << std::endl; - std::cout << "ID: " << share_target.id << std::endl; - std::cout << "Name: " << share_target.device_name << std::endl; - std::cout << "Vendor ID: " << static_cast(share_target.vendor_id) << std::endl; - std::cout << "*************************" << std::endl; + std::cout << "\n┌─────────────────────────────────────┐" << std::endl; + std::cout << "│ 📱 DEVICE DISCOVERED │" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ ID: " << share_target.id << std::endl; + std::cout << "│ Name: " << share_target.device_name << std::endl; + std::cout << "│ Vendor ID: " << static_cast(share_target.vendor_id) << std::endl; + std::cout << "└─────────────────────────────────────┘" << std::endl; discovered_targets_.push_back(share_target); + PrintMenuAtBottom(); } void OnShareTargetLost(const ShareTarget& share_target) override { - std::cout << "\n*** Device Lost ***" << std::endl; - std::cout << "Name: " << share_target.device_name << std::endl; - std::cout << "*******************" << std::endl; + std::cout << "\n┌─────────────────────────────────────┐" << std::endl; + std::cout << "│ ❌ DEVICE LOST │" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ Name: " << share_target.device_name << std::endl; + std::cout << "└─────────────────────────────────────┘" << std::endl; for (auto it = discovered_targets_.begin(); it != discovered_targets_.end(); ++it) { if (it->id == share_target.id) { @@ -105,12 +127,16 @@ class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback { break; } } + PrintMenuAtBottom(); } void OnShareTargetUpdated(const ShareTarget& share_target) override { - std::cout << "\n*** Device Updated ***" << std::endl; - std::cout << "Name: " << share_target.device_name << std::endl; - std::cout << "**********************" << std::endl; + std::cout << "\n┌─────────────────────────────────────┐" << std::endl; + std::cout << "│ 🔄 DEVICE UPDATED │" << std::endl; + std::cout << "├─────────────────────────────────────┤" << std::endl; + std::cout << "│ Name: " << share_target.device_name << std::endl; + std::cout << "└─────────────────────────────────────┘" << std::endl; + PrintMenuAtBottom(); } const std::vector& GetDiscoveredTargets() const { @@ -119,6 +145,19 @@ class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback { private: std::vector discovered_targets_; + + void PrintMenuAtBottom() { + std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl; + std::cout << "║ NEARBY SHARING - MENU ║" << std::endl; + std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl; + std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl; + std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl; + std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl; + std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl; + std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl; + std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl; + std::cout << "Choice: "; + } }; class NearbySharingApp { @@ -142,9 +181,25 @@ class NearbySharingApp { transfer_callback_.get(), NearbySharingService::ReceiveSurfaceState::kForeground, Advertisement::BlockedVendorId::kNone, - [](NearbySharingService::StatusCodes status) { + [this](NearbySharingService::StatusCodes status) { if (status == NearbySharingService::StatusCodes::kOk) { std::cout << "Successfully registered as receiver!" << std::endl; + + // Display QR code URL + std::string qr_url = service_->GetQrCodeUrl(); + if (!qr_url.empty()) { + std::cout << "\n┌──────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "│ QR CODE URL: │" << std::endl; + std::cout << "│ " << qr_url; + // Add padding if needed + if (qr_url.length() < 61) { + std::cout << std::string(61 - qr_url.length(), ' '); + } + std::cout << "│" << std::endl; + std::cout << "│ │" << std::endl; + std::cout << "│ Scan this with your phone to connect! │" << std::endl; + std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl; + } } else { std::cout << "Failed to register as receiver: " << NearbySharingService::StatusCodeToString(status) << std::endl; @@ -163,9 +218,25 @@ class NearbySharingApp { NearbySharingService::SendSurfaceState::kForeground, Advertisement::BlockedVendorId::kNone, false, // disable_wifi_hotspot - [](NearbySharingService::StatusCodes status) { + [this](NearbySharingService::StatusCodes status) { if (status == NearbySharingService::StatusCodes::kOk) { std::cout << "Successfully registered as sender!" << std::endl; + + // Display QR code URL + std::string qr_url = service_->GetQrCodeUrl(); + if (!qr_url.empty()) { + std::cout << "\n┌──────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "│ QR CODE URL: │" << std::endl; + std::cout << "│ " << qr_url; + // Add padding if needed + if (qr_url.length() < 61) { + std::cout << std::string(61 - qr_url.length(), ' '); + } + std::cout << "│" << std::endl; + std::cout << "│ │" << std::endl; + std::cout << "│ Scan this with your phone to connect! │" << std::endl; + std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl; + } } else { std::cout << "Failed to register as sender: " << NearbySharingService::StatusCodeToString(status) << std::endl; @@ -180,9 +251,34 @@ class NearbySharingApp { std::cout << "Target ID: " << target_id << std::endl; std::cout << "File: " << file_path << std::endl; + nearby::FilePath path(file_path); + auto file_size = nearby::Files::GetFileSize(path); + if (!file_size.has_value()) { + std::cout << "Error: Could not get file size for " << file_path << std::endl; + return; + } + auto attachment_container = std::make_unique(); - attachment_container->AddFileAttachment(FileAttachment(nearby::FilePath(file_path))); + // Use the constructor that accepts size + std::string file_name = path.GetFileName().ToString(); + std::string mime_type = ""; // Will be auto-detected from extension + + // Create file attachment with proper size + FileAttachment file_attachment( + /*id=*/0, // ID will be auto-generated + /*size=*/static_cast(*file_size), + /*file_name=*/file_name, + /*mime_type=*/mime_type, + /*type=*/service::proto::FileMetadata::UNKNOWN, + /*parent_folder=*/"", + /*batch_id=*/0 + ); + + // Set the file path after construction + file_attachment.set_file_path(path); + + attachment_container->AddFileAttachment(std::move(file_attachment)); service_->SendAttachments( target_id, @@ -328,19 +424,16 @@ class NearbySharingApp { }; void PrintMenu() { - std::cout << "\n========== Nearby Sharing Menu ==========" << std::endl; - std::cout << "1. Start as Receiver (advertise)" << std::endl; - std::cout << "2. Start as Sender (discover)" << std::endl; - std::cout << "3. List discovered devices" << std::endl; - std::cout << "4. Send file to device" << std::endl; - std::cout << "5. Send text to device" << std::endl; - std::cout << "6. Accept pending incoming share (auto-detects ID)" << std::endl; - std::cout << "7. Reject pending incoming share (auto-detects ID)" << std::endl; - std::cout << "8. Cancel transfer" << std::endl; - std::cout << "9. Print status" << std::endl; - std::cout << "0. Exit" << std::endl; - std::cout << "=========================================" << std::endl; - std::cout << "Enter choice: "; + std::cout << "\n╔═════════════════════════════════════════════════════════╗" << std::endl; + std::cout << "║ NEARBY SHARING - MENU ║" << std::endl; + std::cout << "╠═════════════════════════════════════════════════════════╣" << std::endl; + std::cout << "║ 1. Start as Receiver │ 6. Accept pending request ║" << std::endl; + std::cout << "║ 2. Start as Sender │ 7. Reject pending request ║" << std::endl; + std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl; + std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl; + std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl; + std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl; + std::cout << "Choice: "; } int main(int argc, char* argv[]) { @@ -350,9 +443,15 @@ int main(int argc, char* argv[]) { device_name = argv[1]; } - std::cout << "========================================" << std::endl; - std::cout << " Nearby Sharing Linux Application" << std::endl; - std::cout << "========================================" << std::endl; + // Clear screen and show header + std::cout << "\033[2J\033[1;1H"; // Clear screen + std::cout << "╔═════════════════════════════════════════════════════════╗" << std::endl; + std::cout << "║ ║" << std::endl; + std::cout << "║ NEARBY SHARING - LINUX APPLICATION ║" << std::endl; + std::cout << "║ Device: " << device_name << std::string(32 - device_name.length(), ' ') << "║" << std::endl; + std::cout << "║ ║" << std::endl; + std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl; + std::cout << "\n[LOG AREA - Updates will appear above the menu]\n" << std::endl; NearbySharingApp app(device_name); diff --git a/sharing/linux/nearby_sharing_service_linux.cc b/sharing/linux/nearby_sharing_service_linux.cc index 42af1563..0ee445a5 100644 --- a/sharing/linux/nearby_sharing_service_linux.cc +++ b/sharing/linux/nearby_sharing_service_linux.cc @@ -21,6 +21,7 @@ #include #include +#include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "connections/advertising_options.h" @@ -31,10 +32,14 @@ #include "connections/payload.h" #include "connections/status.h" #include "connections/strategy.h" +#include "internal/crypto_cros/ec_private_key.h" #include "internal/platform/byte_array.h" #include "internal/platform/file.h" #include "internal/platform/logging.h" #include "sharing/certificates/common.h" +#include +#include +#include namespace nearby::sharing::linux { namespace { @@ -240,12 +245,96 @@ bool NearbySharingServiceLinux::IsBluetoothPowered() const { } bool NearbySharingServiceLinux::IsExtendedAdvertisingSupported() const { - return false; + return true; } bool NearbySharingServiceLinux::IsLanConnected() const { return false; } -std::string NearbySharingServiceLinux::GetQrCodeUrl() const { return ""; } +std::string NearbySharingServiceLinux::GenerateQrCodeUrl() const { + // Generate ECDSA P-256 key pair + auto ec_key = nearby::crypto::ECPrivateKey::Create(); + if (!ec_key) { + LOG(ERROR) << "Failed to generate EC key for QR code"; + return ""; + } + + // Get the EC_KEY from EVP_PKEY + EC_KEY* raw_ec_key = EVP_PKEY_get0_EC_KEY(ec_key->key()); + if (!raw_ec_key) { + LOG(ERROR) << "Failed to get EC_KEY from EVP_PKEY"; + return ""; + } + + const EC_GROUP* group = EC_KEY_get0_group(raw_ec_key); + const EC_POINT* pub_key = EC_KEY_get0_public_key(raw_ec_key); + if (!group || !pub_key) { + LOG(ERROR) << "Failed to get EC group or public key"; + return ""; + } + + // Get the X and Y coordinates + BIGNUM* x = BN_new(); + BIGNUM* y = BN_new(); + if (!x || !y) { + BN_free(x); + BN_free(y); + LOG(ERROR) << "Failed to allocate BIGNUMs"; + return ""; + } + + if (!EC_POINT_get_affine_coordinates_GFp(group, pub_key, x, y, nullptr)) { + BN_free(x); + BN_free(y); + LOG(ERROR) << "Failed to get affine coordinates"; + return ""; + } + + // Convert X coordinate to bytes (32 bytes for P-256) + std::vector x_bytes(32, 0); + int x_len = BN_num_bytes(x); + if (x_len > 32) { + BN_free(x); + BN_free(y); + LOG(ERROR) << "X coordinate too large"; + return ""; + } + + // Pad with leading zeros if needed + int offset = 32 - x_len; + BN_bn2bin(x, x_bytes.data() + offset); + + // Determine prefix byte based on Y coordinate parity + // 0x02 if Y is even, 0x03 if Y is odd + uint8_t prefix = BN_is_odd(y) ? 0x03 : 0x02; + + BN_free(x); + BN_free(y); + + // Build the key data: [version (2 bytes), prefix (1 byte), X coordinate (32 bytes)] + std::vector key_data; + key_data.reserve(35); // 2 + 1 + 32 + key_data.push_back(0x00); // Version byte 1 + key_data.push_back(0x00); // Version byte 2 + key_data.push_back(prefix); // Prefix byte (0x02 or 0x03) + key_data.insert(key_data.end(), x_bytes.begin(), x_bytes.end()); // X coordinate + + // Base64url encode + std::string encoded; + absl::WebSafeBase64Escape( + std::string(reinterpret_cast(key_data.data()), key_data.size()), + &encoded); + + // Build the final URL + return "https://quickshare.google/qrcode#key=" + encoded; +} + +std::string NearbySharingServiceLinux::GetQrCodeUrl() const { + // Generate the URL once and cache it + if (qr_code_url_.empty()) { + qr_code_url_ = GenerateQrCodeUrl(); + } + return qr_code_url_; +} void NearbySharingServiceLinux::SendAttachments( int64_t share_target_id, diff --git a/sharing/linux/nearby_sharing_service_linux.h b/sharing/linux/nearby_sharing_service_linux.h index bbfc0beb..3a0d9bc3 100644 --- a/sharing/linux/nearby_sharing_service_linux.h +++ b/sharing/linux/nearby_sharing_service_linux.h @@ -222,6 +222,10 @@ class NearbySharingServiceLinux : public NearbySharingService { int64_t next_share_target_id_ = 1; bool last_advertise_with_name_ = false; uint8_t last_advertise_vendor_id_ = 0; + + // QR code related + mutable std::string qr_code_url_; + std::string GenerateQrCodeUrl() const; }; } // namespace nearby::sharing::linux