From 5b6a7f0fa5bbcfc9b2251ffae66b4498ebe89aa2 Mon Sep 17 00:00:00 2001 From: Amal Graafstra Date: Thu, 6 Nov 2025 22:56:11 -0800 Subject: [PATCH] Initial commit: NFC Actions Windows tray application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement PC/SC-based NFC card reader monitoring application with WPF UI. Features include: - System tray integration with single-click to open main window - Dynamic reader detection and management with enable/disable per reader - NDEF payload extraction supporting Type 2 and Type 4 tags - Auto-detection of block sizes (4-byte vs 16-byte) for different reader types - Configurable actions: copy to clipboard, launch URLs, keyboard input simulation - URI record type detection - only launches browser for actual URI records - Real-time activity logging with color-coded levels (Debug, Info, Warning, Error) - File-based debug logging for troubleshooting - Settings persistence between sessions - Dangerous Things branding with custom icons and clickable logo πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 105 +++++ NfcActions.sln | 22 + NfcActions/App.xaml | 10 + NfcActions/App.xaml.cs | 111 +++++ NfcActions/AssemblyInfo.cs | 10 + NfcActions/MainWindow.xaml | 149 +++++++ NfcActions/MainWindow.xaml.cs | 42 ++ NfcActions/Models/AppSettings.cs | 11 + NfcActions/Models/ReaderItem.cs | 30 ++ NfcActions/NfcActions.csproj | 28 ++ NfcActions/Resources/icon.ico | Bin 0 -> 33910 bytes NfcActions/Resources/icon.png | Bin 0 -> 23482 bytes NfcActions/Resources/logo.png | Bin 0 -> 64997 bytes NfcActions/Services/ActionService.cs | 69 +++ NfcActions/Services/CardReaderService.cs | 544 +++++++++++++++++++++++ NfcActions/Services/LogService.cs | 106 +++++ NfcActions/Services/NdefParser.cs | 311 +++++++++++++ NfcActions/Services/SettingsService.cs | 53 +++ NfcActions/ViewModels/MainViewModel.cs | 216 +++++++++ README.md | 164 +++++++ 20 files changed, 1981 insertions(+) create mode 100644 .gitignore create mode 100644 NfcActions.sln create mode 100644 NfcActions/App.xaml create mode 100644 NfcActions/App.xaml.cs create mode 100644 NfcActions/AssemblyInfo.cs create mode 100644 NfcActions/MainWindow.xaml create mode 100644 NfcActions/MainWindow.xaml.cs create mode 100644 NfcActions/Models/AppSettings.cs create mode 100644 NfcActions/Models/ReaderItem.cs create mode 100644 NfcActions/NfcActions.csproj create mode 100644 NfcActions/Resources/icon.ico create mode 100644 NfcActions/Resources/icon.png create mode 100644 NfcActions/Resources/logo.png create mode 100644 NfcActions/Services/ActionService.cs create mode 100644 NfcActions/Services/CardReaderService.cs create mode 100644 NfcActions/Services/LogService.cs create mode 100644 NfcActions/Services/NdefParser.cs create mode 100644 NfcActions/Services/SettingsService.cs create mode 100644 NfcActions/ViewModels/MainViewModel.cs create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b71676b --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ + +# Visual Studio Code +.vscode/ + +# ReSharper +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JetBrains Rider +.idea/ +*.sln.iml + +# NuGet Packages +*.nupkg +**/packages/* +!**/packages/build/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# Publish +publish/ + +# macOS +.DS_Store + +# Windows +Thumbs.db +ehthumbs.db diff --git a/NfcActions.sln b/NfcActions.sln new file mode 100644 index 0000000..ece58d1 --- /dev/null +++ b/NfcActions.sln @@ -0,0 +1,22 @@ +ο»Ώ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfcActions", "NfcActions\NfcActions.csproj", "{50B8E3CE-224F-4B32-B49B-05631DB3D015}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50B8E3CE-224F-4B32-B49B-05631DB3D015}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50B8E3CE-224F-4B32-B49B-05631DB3D015}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50B8E3CE-224F-4B32-B49B-05631DB3D015}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50B8E3CE-224F-4B32-B49B-05631DB3D015}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/NfcActions/App.xaml b/NfcActions/App.xaml new file mode 100644 index 0000000..12668a1 --- /dev/null +++ b/NfcActions/App.xaml @@ -0,0 +1,10 @@ +ο»Ώ + + + + diff --git a/NfcActions/App.xaml.cs b/NfcActions/App.xaml.cs new file mode 100644 index 0000000..90cb1d8 --- /dev/null +++ b/NfcActions/App.xaml.cs @@ -0,0 +1,111 @@ +ο»Ώusing System; +using System.Drawing; +using System.Windows; +using System.Windows.Forms; +using NfcActions.Services; +using NfcActions.ViewModels; +using Application = System.Windows.Application; + +namespace NfcActions; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ + private NotifyIcon? _notifyIcon; + private Icon? _customIcon; + private MainWindow? _mainWindow; + private CardReaderService? _cardReaderService; + private SettingsService? _settingsService; + private ActionService? _actionService; + private LogService? _logService; + private MainViewModel? _viewModel; + + private void Application_Startup(object sender, StartupEventArgs e) + { + // Initialize services + _logService = new LogService(); + _cardReaderService = new CardReaderService(_logService); + _settingsService = new SettingsService(); + _actionService = new ActionService(); + + // Initialize ViewModel + _viewModel = new MainViewModel(_cardReaderService, _settingsService, _actionService, _logService); + + // Create main window but don't show it yet + _mainWindow = new MainWindow(_viewModel); + + // Load custom icon + try + { + var iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "icon.ico"); + if (System.IO.File.Exists(iconPath)) + { + _customIcon = new Icon(iconPath); + } + } + catch + { + // Fall back to default if custom icon can't be loaded + } + + // Create system tray icon + _notifyIcon = new NotifyIcon + { + Icon = _customIcon ?? SystemIcons.Application, + Visible = true, + Text = "NFC Actions" + }; + + // Set up context menu + var contextMenu = new ContextMenuStrip(); + contextMenu.Items.Add("Open", null, (s, args) => ShowMainWindow()); + contextMenu.Items.Add(new ToolStripSeparator()); + contextMenu.Items.Add("Exit", null, (s, args) => ExitApplication()); + + _notifyIcon.ContextMenuStrip = contextMenu; + _notifyIcon.MouseClick += (s, args) => + { + if (args.Button == MouseButtons.Left) + { + ShowMainWindow(); + } + }; + + // Start the card reader service + _cardReaderService.Start(); + + // Show a notification that the app is running + _notifyIcon.ShowBalloonTip( + 3000, + "NFC Actions", + "NFC Actions is now monitoring for card events. Right-click the tray icon to configure.", + ToolTipIcon.Info); + } + + private void ShowMainWindow() + { + if (_mainWindow != null) + { + _mainWindow.Show(); + _mainWindow.WindowState = WindowState.Normal; + _mainWindow.Activate(); + } + } + + private void ExitApplication() + { + _notifyIcon?.Dispose(); + _customIcon?.Dispose(); + _cardReaderService?.Dispose(); + Shutdown(); + } + + private void Application_Exit(object sender, ExitEventArgs e) + { + _notifyIcon?.Dispose(); + _customIcon?.Dispose(); + _cardReaderService?.Dispose(); + } +} diff --git a/NfcActions/AssemblyInfo.cs b/NfcActions/AssemblyInfo.cs new file mode 100644 index 0000000..2211234 --- /dev/null +++ b/NfcActions/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/NfcActions/MainWindow.xaml b/NfcActions/MainWindow.xaml new file mode 100644 index 0000000..c386bb5 --- /dev/null +++ b/NfcActions/MainWindow.xaml @@ -0,0 +1,149 @@ +ο»Ώ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NfcActions/MainWindow.xaml.cs b/NfcActions/MainWindow.xaml.cs new file mode 100644 index 0000000..d0627ab --- /dev/null +++ b/NfcActions/MainWindow.xaml.cs @@ -0,0 +1,42 @@ +ο»Ώusing System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Input; +using NfcActions.ViewModels; + +namespace NfcActions; + +/// +/// Interaction logic for MainWindow.xaml +/// +public partial class MainWindow : Window +{ + public MainWindow(MainViewModel viewModel) + { + InitializeComponent(); + DataContext = viewModel; + } + + private void Window_Closing(object? sender, CancelEventArgs e) + { + // Minimize to tray instead of closing + e.Cancel = true; + Hide(); + } + + private void Logo_Click(object sender, MouseButtonEventArgs e) + { + try + { + Process.Start(new ProcessStartInfo + { + FileName = "https://dangerousthings.com", + UseShellExecute = true + }); + } + catch + { + // Silently fail if browser can't be opened + } + } +} diff --git a/NfcActions/Models/AppSettings.cs b/NfcActions/Models/AppSettings.cs new file mode 100644 index 0000000..b0dfc87 --- /dev/null +++ b/NfcActions/Models/AppSettings.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace NfcActions.Models; + +public class AppSettings +{ + public HashSet DisabledReaders { get; set; } = new(); + public bool CopyToClipboard { get; set; } = false; + public bool LaunchUrls { get; set; } = true; + public bool TypeAsKeyboard { get; set; } = false; +} diff --git a/NfcActions/Models/ReaderItem.cs b/NfcActions/Models/ReaderItem.cs new file mode 100644 index 0000000..fbcdd61 --- /dev/null +++ b/NfcActions/Models/ReaderItem.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; + +namespace NfcActions.Models; + +public class ReaderItem : INotifyPropertyChanged +{ + private bool _isEnabled; + + public string Name { get; set; } = string.Empty; + + public bool IsEnabled + { + get => _isEnabled; + set + { + if (_isEnabled != value) + { + _isEnabled = value; + OnPropertyChanged(nameof(IsEnabled)); + } + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/NfcActions/NfcActions.csproj b/NfcActions/NfcActions.csproj new file mode 100644 index 0000000..5a714ac --- /dev/null +++ b/NfcActions/NfcActions.csproj @@ -0,0 +1,28 @@ +ο»Ώ + + + WinExe + net7.0-windows + enable + true + true + Resources\icon.ico + + + + + + + + + + + + + + + + + + + diff --git a/NfcActions/Resources/icon.ico b/NfcActions/Resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..601c72a21393fd7adee7a1ae29013fc759fdb80e GIT binary patch literal 33910 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 N=JvSS<7|)n{{Y9p-su1U literal 0 HcmV?d00001 diff --git a/NfcActions/Resources/icon.png b/NfcActions/Resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b09fd602de4d9c0ed6226d0a6a9df3ecbf63515b GIT binary patch literal 23482 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 literal 0 HcmV?d00001 diff --git a/NfcActions/Resources/logo.png b/NfcActions/Resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d0f771f9391d000df88fb4a2b4ec8e481d9f9a73 GIT binary patch literal 64997 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 literal 0 HcmV?d00001 diff --git a/NfcActions/Services/ActionService.cs b/NfcActions/Services/ActionService.cs new file mode 100644 index 0000000..415b4a1 --- /dev/null +++ b/NfcActions/Services/ActionService.cs @@ -0,0 +1,69 @@ +using System; +using System.Diagnostics; +using System.Windows; +using WindowsInput; +using WindowsInput.Native; + +namespace NfcActions.Services; + +public class ActionService +{ + private readonly InputSimulator _inputSimulator = new(); + + public void CopyToClipboard(string text) + { + try + { + Clipboard.SetText(text); + } + catch (Exception) + { + // Failed to set clipboard + } + } + + public void LaunchUrl(string text) + { + try + { + // Check if the text looks like a URL + if (Uri.TryCreate(text, UriKind.Absolute, out var uri) && + (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) + { + Process.Start(new ProcessStartInfo + { + FileName = text, + UseShellExecute = true + }); + } + else if (text.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || + text.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + Process.Start(new ProcessStartInfo + { + FileName = text, + UseShellExecute = true + }); + } + } + catch (Exception) + { + // Failed to launch URL + } + } + + public void TypeText(string text) + { + try + { + // Give a small delay to allow user to position cursor if needed + System.Threading.Thread.Sleep(100); + + _inputSimulator.Keyboard.TextEntry(text); + } + catch (Exception) + { + // Failed to simulate keyboard input + } + } +} diff --git a/NfcActions/Services/CardReaderService.cs b/NfcActions/Services/CardReaderService.cs new file mode 100644 index 0000000..3a5d54d --- /dev/null +++ b/NfcActions/Services/CardReaderService.cs @@ -0,0 +1,544 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using PCSC; + +namespace NfcActions.Services; + +public class CardReaderService : IDisposable +{ + private readonly Timer _pollTimer; + private readonly Dictionary _readerCardPresent = new(); + private readonly HashSet _disabledReaders = new(); + private readonly SynchronizationContext? _syncContext; + private readonly object _lock = new(); + private readonly LogService? _logService; + + private const int POLL_INTERVAL_MS = 500; + + public event EventHandler? ReaderAdded; + public event EventHandler? ReaderRemoved; + public event EventHandler? CardInserted; + public event EventHandler? CardRemoved; + + public CardReaderService(LogService? logService = null) + { + _syncContext = SynchronizationContext.Current; + _pollTimer = new Timer(PollReaders, null, Timeout.Infinite, Timeout.Infinite); + _logService = logService; + } + + public void Start() + { + _logService?.Info("Starting CardReaderService..."); + + // Initialize with current readers + RefreshReaders(); + + // Start polling + _pollTimer.Change(POLL_INTERVAL_MS, POLL_INTERVAL_MS); + _logService?.Info("CardReaderService started successfully"); + } + + private void PollReaders(object? state) + { + try + { + RefreshReaders(); + MonitorCardStates(); + } + catch (Exception) + { + // Error during polling, continue anyway + } + } + + private void RefreshReaders() + { + lock (_lock) + { + try + { + using var context = ContextFactory.Instance.Establish(SCardScope.System); + var currentReaders = context.GetReaders()?.ToList() ?? new List(); + + // Find removed readers + var removedReaders = _readerCardPresent.Keys.Except(currentReaders).ToList(); + foreach (var reader in removedReaders) + { + _readerCardPresent.Remove(reader); + _logService?.Info($"Reader removed: {reader}"); + RaiseEvent(() => ReaderRemoved?.Invoke(this, new ReaderEventArgs(reader))); + } + + // Find new readers + var newReaders = currentReaders.Except(_readerCardPresent.Keys).ToList(); + foreach (var reader in newReaders) + { + _readerCardPresent[reader] = false; + _logService?.Info($"Reader added: {reader}"); + RaiseEvent(() => ReaderAdded?.Invoke(this, new ReaderEventArgs(reader))); + } + } + catch (Exception ex) + { + _logService?.Error($"Error refreshing readers: {ex.Message}"); + } + } + } + + private void MonitorCardStates() + { + lock (_lock) + { + foreach (var readerName in _readerCardPresent.Keys.ToList()) + { + // Skip disabled readers + if (_disabledReaders.Contains(readerName)) + continue; + + try + { + var isPresent = IsCardPresent(readerName); + var wasPresent = _readerCardPresent[readerName]; + + if (isPresent && !wasPresent) + { + _readerCardPresent[readerName] = true; + _logService?.Info($"Card inserted on reader: {readerName}"); + var cardData = ReadCardData(readerName); + + if (cardData != null && cardData.Length > 0) + { + _logService?.Debug($"Read {cardData.Length} bytes from card"); + _logService?.Debug($"Card data (hex): {BitConverter.ToString(cardData).Replace("-", " ")}"); + } + else + { + _logService?.Warning("No data read from card"); + } + + RaiseEvent(() => CardInserted?.Invoke(this, new CardEventArgs(readerName, cardData))); + } + else if (!isPresent && wasPresent) + { + _readerCardPresent[readerName] = false; + _logService?.Info($"Card removed from reader: {readerName}"); + RaiseEvent(() => CardRemoved?.Invoke(this, new CardEventArgs(readerName, null))); + } + } + catch (Exception ex) + { + _logService?.Error($"Error monitoring reader {readerName}: {ex.Message}"); + } + } + } + } + + private bool IsCardPresent(string readerName) + { + try + { + using var context = ContextFactory.Instance.Establish(SCardScope.System); + + var readerStates = new[] + { + new SCardReaderState + { + ReaderName = readerName + } + }; + + var result = context.GetStatusChange(0, readerStates); + + if (result == SCardError.Success && readerStates.Length > 0) + { + var state = readerStates[0].EventState; + return (state & SCRState.Present) == SCRState.Present; + } + + return false; + } + catch + { + return false; + } + } + + public List GetAvailableReaders() + { + try + { + using var context = ContextFactory.Instance.Establish(SCardScope.System); + return context.GetReaders()?.ToList() ?? new List(); + } + catch + { + return new List(); + } + } + + public void EnableReader(string readerName) + { + lock (_lock) + { + _disabledReaders.Remove(readerName); + } + } + + public void DisableReader(string readerName) + { + lock (_lock) + { + _disabledReaders.Add(readerName); + } + } + + public bool IsReaderEnabled(string readerName) + { + lock (_lock) + { + return !_disabledReaders.Contains(readerName); + } + } + + public void SetDisabledReaders(IEnumerable disabledReaders) + { + lock (_lock) + { + _disabledReaders.Clear(); + foreach (var reader in disabledReaders) + { + _disabledReaders.Add(reader); + } + } + } + + private byte[]? ReadCardData(string readerName) + { + try + { + _logService?.Debug($"--- Starting card read from {readerName} ---"); + + using var context = ContextFactory.Instance.Establish(SCardScope.System); + + _logService?.Debug("Connecting to reader..."); + using var reader = context.ConnectReader(readerName, SCardShareMode.Shared, SCardProtocol.Any); + + _logService?.Debug($"Connected. Active protocol: {reader.Protocol}"); + + // Get ATR (Answer To Reset) + var atr = reader.GetAttrib(SCardAttribute.AtrString); + if (atr != null && atr.Length > 0) + { + _logService?.Debug($"ATR: {BitConverter.ToString(atr).Replace("-", " ")}"); + } + + byte[]? ndefData = null; + + // Strategy 1: Try Type 4 Tag (ISO 14443-4 / ISO-DEP) + _logService?.Debug("=== Attempting Type 4 Tag NDEF read ==="); + ndefData = TryReadType4Tag(reader); + if (ndefData != null && ndefData.Length > 0) + { + _logService?.Info("Successfully read NDEF data using Type 4 method"); + return ndefData; + } + + // Strategy 2: Try direct NDEF file read + _logService?.Debug("=== Attempting direct NDEF file read ==="); + ndefData = TryReadNdefDirect(reader); + if (ndefData != null && ndefData.Length > 0) + { + _logService?.Info("Successfully read NDEF data using direct method"); + return ndefData; + } + + // Strategy 3: Try reading raw tag memory (Type 2) + _logService?.Debug("=== Attempting Type 2 Tag read ==="); + ndefData = TryReadType2Tag(reader); + if (ndefData != null && ndefData.Length > 0) + { + _logService?.Info("Successfully read data using Type 2 method"); + return ndefData; + } + + _logService?.Warning("All read strategies failed - no NDEF data retrieved"); + return null; + } + catch (Exception ex) + { + _logService?.Error($"Exception in ReadCardData: {ex.Message}"); + _logService?.Debug($"Stack trace: {ex.StackTrace}"); + return null; + } + } + + private byte[]? TryReadType4Tag(ICardReader reader) + { + try + { + // Select NDEF Tag Application (AID: D2760000850101) + var selectNdef = new byte[] { 0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; + var response = TransmitApdu(reader, selectNdef, "Select NDEF Application"); + + if (!IsSuccess(response)) + { + _logService?.Debug("NDEF application not found (this is normal for non-Type 4 tags)"); + return null; + } + + // Select Capability Container file + var selectCC = new byte[] { 0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x03 }; + response = TransmitApdu(reader, selectCC, "Select CC File"); + + if (IsSuccess(response)) + { + // Read CC + var readCC = new byte[] { 0x00, 0xB0, 0x00, 0x00, 0x0F }; + response = TransmitApdu(reader, readCC, "Read CC"); + } + + // Select NDEF file + var selectNdefFile = new byte[] { 0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04 }; + response = TransmitApdu(reader, selectNdefFile, "Select NDEF File"); + + if (!IsSuccess(response)) + { + return null; + } + + // Read NDEF length (first 2 bytes) + var readLength = new byte[] { 0x00, 0xB0, 0x00, 0x00, 0x02 }; + response = TransmitApdu(reader, readLength, "Read NDEF Length"); + + if (response == null || response.Length < 4) + { + return null; + } + + int ndefLength = (response[0] << 8) | response[1]; + _logService?.Debug($"NDEF message length: {ndefLength} bytes"); + + if (ndefLength == 0 || ndefLength > 8192) + { + _logService?.Warning($"Invalid NDEF length: {ndefLength}"); + return null; + } + + // Read actual NDEF data + var readNdef = new byte[] { 0x00, 0xB0, 0x00, 0x02, (byte)Math.Min(ndefLength, 250) }; + response = TransmitApdu(reader, readNdef, "Read NDEF Data"); + + if (response != null && response.Length > 2) + { + var data = new byte[response.Length - 2]; + Array.Copy(response, data, data.Length); + return data; + } + + return null; + } + catch (Exception ex) + { + _logService?.Debug($"Type 4 read exception: {ex.Message}"); + return null; + } + } + + private byte[]? TryReadNdefDirect(ICardReader reader) + { + try + { + // Try reading from common NDEF file locations + var selectFile = new byte[] { 0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04 }; + var response = TransmitApdu(reader, selectFile, "Direct select NDEF"); + + if (IsSuccess(response)) + { + var readData = new byte[] { 0x00, 0xB0, 0x00, 0x00, 0xF0 }; + response = TransmitApdu(reader, readData, "Direct read data"); + + if (response != null && response.Length > 2) + { + var data = new byte[response.Length - 2]; + Array.Copy(response, data, data.Length); + return data; + } + } + + return null; + } + catch (Exception ex) + { + _logService?.Debug($"Direct read exception: {ex.Message}"); + return null; + } + } + + private byte[]? TryReadType2Tag(ICardReader reader) + { + try + { + // Type 2 tags use direct memory read commands + // Read blocks starting from block 4 (where NDEF usually starts) + _logService?.Debug("Attempting Type 2 tag read (direct memory access)"); + + var allData = new List(); + int blockSize = 4; // Default NTAG/MIFARE Ultralight block size + bool blockSizeDetected = false; + + // Read first few blocks to get NDEF length + for (byte block = 4; block < 64;) + { + var readBlock = new byte[] { 0xFF, 0xB0, 0x00, block, 0x10 }; + var response = TransmitApdu(reader, readBlock, $"Read block {block}"); + + if (response == null || response.Length < 2) + { + break; + } + + if (!IsSuccess(response)) + { + // Try alternative command + readBlock = new byte[] { 0x30, block }; + response = TransmitApdu(reader, readBlock, $"Read block {block} (alt)"); + + if (response == null || !IsSuccess(response)) + { + break; + } + } + + // Detect block size from first successful read + var dataLength = response.Length - 2; // Exclude SW1 SW2 + if (!blockSizeDetected && dataLength > 0) + { + blockSize = dataLength; + blockSizeDetected = true; + _logService?.Debug($"Detected block size: {blockSize} bytes"); + } + + // Add data (excluding status words) + if (dataLength > 0) + { + for (int i = 0; i < dataLength; i++) + { + allData.Add(response[i]); + } + } + + // Stop if we've hit terminator TLV + if (allData.Count > 0 && allData[allData.Count - 1] == 0xFE) + { + _logService?.Debug("Found NDEF terminator TLV (0xFE)"); + break; + } + + if (allData.Count > 200) + { + _logService?.Debug("Read limit reached (200 bytes)"); + break; + } + + // Advance block pointer based on detected block size + // Type 2 tags have 4-byte blocks, so if we got 16 bytes, we read 4 blocks + block += (byte)(blockSize / 4); + } + + if (allData.Count > 0) + { + _logService?.Debug($"Read {allData.Count} bytes from Type 2 tag"); + return allData.ToArray(); + } + + return null; + } + catch (Exception ex) + { + _logService?.Debug($"Type 2 read exception: {ex.Message}"); + return null; + } + } + + private byte[]? TransmitApdu(ICardReader reader, byte[] apdu, string description) + { + try + { + _logService?.Debug($"TX [{description}]: {BitConverter.ToString(apdu).Replace("-", " ")}"); + + var response = new byte[256]; + var receivedLength = reader.Transmit(apdu, response); + + if (receivedLength > 0) + { + var result = new byte[receivedLength]; + Array.Copy(response, result, receivedLength); + + _logService?.Debug($"RX [{description}]: {BitConverter.ToString(result).Replace("-", " ")}"); + + return result; + } + + _logService?.Debug($"RX [{description}]: No data received"); + return null; + } + catch (Exception ex) + { + _logService?.Debug($"TX/RX [{description}] Exception: {ex.Message}"); + return null; + } + } + + private bool IsSuccess(byte[]? response) + { + if (response == null || response.Length < 2) + return false; + + var sw1 = response[response.Length - 2]; + var sw2 = response[response.Length - 1]; + + return (sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91; + } + + private void RaiseEvent(Action action) + { + if (_syncContext != null) + { + _syncContext.Post(_ => action(), null); + } + else + { + action(); + } + } + + public void Dispose() + { + _pollTimer?.Dispose(); + } +} + +public class ReaderEventArgs : EventArgs +{ + public string ReaderName { get; } + + public ReaderEventArgs(string readerName) + { + ReaderName = readerName; + } +} + +public class CardEventArgs : EventArgs +{ + public string ReaderName { get; } + public byte[]? CardData { get; } + + public CardEventArgs(string readerName, byte[]? cardData) + { + ReaderName = readerName; + CardData = cardData; + } +} diff --git a/NfcActions/Services/LogService.cs b/NfcActions/Services/LogService.cs new file mode 100644 index 0000000..be97e06 --- /dev/null +++ b/NfcActions/Services/LogService.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Windows; + +namespace NfcActions.Services; + +public class LogService +{ + private readonly SynchronizationContext? _syncContext; + private readonly string _logFilePath; + private readonly object _fileLock = new(); + + public ObservableCollection LogEntries { get; } = new(); + + private const int MAX_LOG_ENTRIES = 500; + + public LogService() + { + _syncContext = SynchronizationContext.Current; + + // Create log file in the same directory as the executable + var exePath = Assembly.GetExecutingAssembly().Location; + var exeDir = Path.GetDirectoryName(exePath) ?? Environment.CurrentDirectory; + var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + _logFilePath = Path.Combine(exeDir, $"nfc-actions-debug-{timestamp}.log"); + + // Write initial header + WriteToFile($"=== NFC Actions Debug Log - Started at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ==="); + WriteToFile($"Log file: {_logFilePath}"); + WriteToFile(""); + } + + public void Log(string message, LogLevel level = LogLevel.Info) + { + var entry = new LogEntry + { + Timestamp = DateTime.Now, + Message = message, + Level = level + }; + + // Write to file immediately + WriteToFile($"[{entry.Timestamp:HH:mm:ss.fff}] [{level}] {message}"); + + // Update UI + if (_syncContext != null) + { + _syncContext.Post(_ => AddEntry(entry), null); + } + else + { + Application.Current?.Dispatcher.Invoke(() => AddEntry(entry)); + } + } + + private void WriteToFile(string message) + { + try + { + lock (_fileLock) + { + File.AppendAllText(_logFilePath, message + Environment.NewLine); + } + } + catch + { + // Ignore file write errors + } + } + + private void AddEntry(LogEntry entry) + { + LogEntries.Insert(0, entry); + + // Keep only the last MAX_LOG_ENTRIES + while (LogEntries.Count > MAX_LOG_ENTRIES) + { + LogEntries.RemoveAt(LogEntries.Count - 1); + } + } + + public void Debug(string message) => Log(message, LogLevel.Debug); + public void Info(string message) => Log(message, LogLevel.Info); + public void Warning(string message) => Log(message, LogLevel.Warning); + public void Error(string message) => Log(message, LogLevel.Error); +} + +public class LogEntry +{ + public DateTime Timestamp { get; set; } + public string Message { get; set; } = string.Empty; + public LogLevel Level { get; set; } + + public string FormattedMessage => $"[{Timestamp:HH:mm:ss.fff}] {Message}"; +} + +public enum LogLevel +{ + Debug, + Info, + Warning, + Error +} diff --git a/NfcActions/Services/NdefParser.cs b/NfcActions/Services/NdefParser.cs new file mode 100644 index 0000000..568017c --- /dev/null +++ b/NfcActions/Services/NdefParser.cs @@ -0,0 +1,311 @@ +using System; +using System.Text; + +namespace NfcActions.Services; + +public class NdefRecord +{ + public string Payload { get; set; } = string.Empty; + public bool IsUri { get; set; } +} + +public static class NdefParser +{ + /// + /// Extracts the first NDEF payload from raw card data. + /// Returns the payload as a string, or null if no valid NDEF message found. + /// + public static string? ExtractFirstPayload(byte[] data) + { + var record = ExtractFirstRecord(data); + return record?.Payload; + } + + /// + /// Extracts the first NDEF record from raw card data with type information. + /// Returns an NdefRecord object, or null if no valid NDEF message found. + /// + public static NdefRecord? ExtractFirstRecord(byte[] data) + { + if (data == null || data.Length < 3) + return null; + + try + { + int position = 0; + + // Look for NDEF TLV (Type-Length-Value) structure + // We're looking for TLV type 0x03 which indicates NDEF Message + while (position < data.Length - 2) + { + byte tlvType = data[position]; + + if (tlvType == 0x00) // NULL TLV, skip + { + position++; + continue; + } + + if (tlvType == 0xFE) // Terminator TLV + { + break; + } + + position++; // Move to length byte + + if (position >= data.Length) + break; + + int length; + if (data[position] == 0xFF) // 3-byte length format + { + if (position + 2 >= data.Length) + break; + length = (data[position + 1] << 8) | data[position + 2]; + position += 3; + } + else + { + length = data[position]; + position++; + } + + if (tlvType == 0x03) // NDEF Message TLV + { + if (position + length > data.Length) + break; + + // Parse NDEF message + var record = ParseNdefMessage(data, position, length); + if (record != null) + return record; + } + + position += length; + } + + // If we didn't find TLV structure, try to parse as raw NDEF message + return ParseNdefMessage(data, 0, data.Length); + } + catch + { + return null; + } + } + + private static NdefRecord? ParseNdefMessage(byte[] data, int offset, int length) + { + if (offset + length > data.Length || length < 3) + return null; + + int position = offset; + int endPosition = offset + length; + + while (position < endPosition) + { + if (position >= data.Length) + break; + + byte header = data[position]; + position++; + + bool mb = (header & 0x80) != 0; // Message Begin + bool me = (header & 0x40) != 0; // Message End + bool cf = (header & 0x20) != 0; // Chunk Flag + bool sr = (header & 0x10) != 0; // Short Record + bool il = (header & 0x08) != 0; // ID Length present + byte tnf = (byte)(header & 0x07); // Type Name Format + + if (position >= endPosition) + break; + + int typeLength = data[position]; + position++; + + int typePosition = 0; // Track where type field starts + + if (position >= endPosition) + break; + + int payloadLength; + if (sr) // Short record - 1 byte payload length + { + payloadLength = data[position]; + position++; + } + else // Normal record - 4 byte payload length + { + if (position + 3 >= endPosition) + break; + + payloadLength = (data[position] << 24) | + (data[position + 1] << 16) | + (data[position + 2] << 8) | + data[position + 3]; + position += 4; + } + + int idLength = 0; + if (il) + { + if (position >= endPosition) + break; + idLength = data[position]; + position++; + } + + // Remember type position before skipping + typePosition = position; + + // Read type field to check if it's a URI record + byte[]? typeField = null; + if (typeLength > 0 && position + typeLength <= endPosition) + { + typeField = new byte[typeLength]; + Array.Copy(data, position, typeField, 0, typeLength); + } + + // Skip type + position += typeLength; + + // Skip ID + position += idLength; + + if (position + payloadLength > endPosition) + break; + + // Extract payload + if (payloadLength > 0) + { + byte[] payload = new byte[payloadLength]; + Array.Copy(data, position, payload, 0, payloadLength); + + // Determine if this is a URI record + bool isUri = IsUriRecord(tnf, typeField, payload); + + // Decode payload + string payloadText = DecodeTextPayload(payload); + + return new NdefRecord + { + Payload = payloadText, + IsUri = isUri + }; + } + + position += payloadLength; + + // If this was the first record, return what we found (or null) + break; + } + + return null; + } + + private static bool IsUriRecord(byte tnf, byte[]? typeField, byte[] payload) + { + // TNF Well-Known (0x01) with type "U" is a URI record + if (tnf == 0x01 && typeField != null && typeField.Length == 1 && typeField[0] == 0x55) // 'U' + { + return true; + } + + // TNF Absolute URI (0x03) + if (tnf == 0x03) + { + return true; + } + + // Check if payload starts with URI identifier code (0x00-0x23) + if (payload.Length > 0 && payload[0] <= 0x23) + { + string prefix = GetUriPrefix(payload[0]); + // If we have a recognized URI prefix, it's likely a URI + if (!string.IsNullOrEmpty(prefix) || payload[0] == 0x00) + { + return true; + } + } + + return false; + } + + private static string DecodeTextPayload(byte[] payload) + { + if (payload.Length == 0) + return string.Empty; + + // Check if first byte indicates URI identifier code + if (payload[0] <= 0x23) // URI identifier codes range from 0x00 to 0x23 + { + string prefix = GetUriPrefix(payload[0]); + string uri = Encoding.UTF8.GetString(payload, 1, payload.Length - 1); + return prefix + uri; + } + + // Check if it's a text record (first byte is status byte) + if (payload.Length > 1) + { + byte statusByte = payload[0]; + bool isUtf16 = (statusByte & 0x80) != 0; + int languageCodeLength = statusByte & 0x3F; + + if (languageCodeLength < payload.Length) + { + int textStart = 1 + languageCodeLength; + if (textStart < payload.Length) + { + var encoding = isUtf16 ? Encoding.Unicode : Encoding.UTF8; + return encoding.GetString(payload, textStart, payload.Length - textStart); + } + } + } + + // Default: try UTF8 decoding of entire payload + return Encoding.UTF8.GetString(payload); + } + + private static string GetUriPrefix(byte code) + { + return code switch + { + 0x00 => "", + 0x01 => "http://www.", + 0x02 => "https://www.", + 0x03 => "http://", + 0x04 => "https://", + 0x05 => "tel:", + 0x06 => "mailto:", + 0x07 => "ftp://anonymous:anonymous@", + 0x08 => "ftp://ftp.", + 0x09 => "ftps://", + 0x0A => "sftp://", + 0x0B => "smb://", + 0x0C => "nfs://", + 0x0D => "ftp://", + 0x0E => "dav://", + 0x0F => "news:", + 0x10 => "telnet://", + 0x11 => "imap:", + 0x12 => "rtsp://", + 0x13 => "urn:", + 0x14 => "pop:", + 0x15 => "sip:", + 0x16 => "sips:", + 0x17 => "tftp:", + 0x18 => "btspp://", + 0x19 => "btl2cap://", + 0x1A => "btgoep://", + 0x1B => "tcpobex://", + 0x1C => "irdaobex://", + 0x1D => "file://", + 0x1E => "urn:epc:id:", + 0x1F => "urn:epc:tag:", + 0x20 => "urn:epc:pat:", + 0x21 => "urn:epc:raw:", + 0x22 => "urn:epc:", + 0x23 => "urn:nfc:", + _ => "" + }; + } +} diff --git a/NfcActions/Services/SettingsService.cs b/NfcActions/Services/SettingsService.cs new file mode 100644 index 0000000..6a54091 --- /dev/null +++ b/NfcActions/Services/SettingsService.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Text.Json; +using NfcActions.Models; + +namespace NfcActions.Services; + +public class SettingsService +{ + private readonly string _settingsPath; + + public SettingsService() + { + var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var appFolder = Path.Combine(appDataPath, "NfcActions"); + Directory.CreateDirectory(appFolder); + _settingsPath = Path.Combine(appFolder, "settings.json"); + } + + public AppSettings Load() + { + try + { + if (File.Exists(_settingsPath)) + { + var json = File.ReadAllText(_settingsPath); + return JsonSerializer.Deserialize(json) ?? new AppSettings(); + } + } + catch (Exception) + { + // Failed to load settings, return defaults + } + + return new AppSettings(); + } + + public void Save(AppSettings settings) + { + try + { + var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions + { + WriteIndented = true + }); + File.WriteAllText(_settingsPath, json); + } + catch (Exception) + { + // Failed to save settings + } + } +} diff --git a/NfcActions/ViewModels/MainViewModel.cs b/NfcActions/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..3cd536f --- /dev/null +++ b/NfcActions/ViewModels/MainViewModel.cs @@ -0,0 +1,216 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using NfcActions.Models; +using NfcActions.Services; + +namespace NfcActions.ViewModels; + +public class MainViewModel : INotifyPropertyChanged +{ + private readonly CardReaderService _cardReaderService; + private readonly SettingsService _settingsService; + private readonly ActionService _actionService; + private readonly LogService _logService; + private AppSettings _settings; + + private bool _copyToClipboard; + private bool _launchUrls; + private bool _typeAsKeyboard; + + public ObservableCollection Readers { get; } = new(); + + public bool CopyToClipboard + { + get => _copyToClipboard; + set + { + if (_copyToClipboard != value) + { + _copyToClipboard = value; + OnPropertyChanged(nameof(CopyToClipboard)); + _settings.CopyToClipboard = value; + _settingsService.Save(_settings); + } + } + } + + public bool LaunchUrls + { + get => _launchUrls; + set + { + if (_launchUrls != value) + { + _launchUrls = value; + OnPropertyChanged(nameof(LaunchUrls)); + _settings.LaunchUrls = value; + _settingsService.Save(_settings); + } + } + } + + public bool TypeAsKeyboard + { + get => _typeAsKeyboard; + set + { + if (_typeAsKeyboard != value) + { + _typeAsKeyboard = value; + OnPropertyChanged(nameof(TypeAsKeyboard)); + _settings.TypeAsKeyboard = value; + _settingsService.Save(_settings); + } + } + } + + public ObservableCollection LogEntries => _logService.LogEntries; + + public MainViewModel( + CardReaderService cardReaderService, + SettingsService settingsService, + ActionService actionService, + LogService logService) + { + _cardReaderService = cardReaderService; + _settingsService = settingsService; + _actionService = actionService; + _logService = logService; + + // Load settings + _settings = _settingsService.Load(); + _copyToClipboard = _settings.CopyToClipboard; + _launchUrls = _settings.LaunchUrls; + _typeAsKeyboard = _settings.TypeAsKeyboard; + + // Set up event handlers + _cardReaderService.ReaderAdded += OnReaderAdded; + _cardReaderService.ReaderRemoved += OnReaderRemoved; + _cardReaderService.CardInserted += OnCardInserted; + + // Set disabled readers from settings + _cardReaderService.SetDisabledReaders(_settings.DisabledReaders); + + // Load current readers + LoadReaders(); + } + + private void LoadReaders() + { + Readers.Clear(); + var readers = _cardReaderService.GetAvailableReaders(); + + foreach (var readerName in readers) + { + var item = new ReaderItem + { + Name = readerName, + IsEnabled = _cardReaderService.IsReaderEnabled(readerName) + }; + + item.PropertyChanged += OnReaderItemPropertyChanged; + Readers.Add(item); + } + } + + private void OnReaderAdded(object? sender, ReaderEventArgs e) + { + var existing = Readers.FirstOrDefault(r => r.Name == e.ReaderName); + if (existing == null) + { + var item = new ReaderItem + { + Name = e.ReaderName, + IsEnabled = _cardReaderService.IsReaderEnabled(e.ReaderName) + }; + + item.PropertyChanged += OnReaderItemPropertyChanged; + Readers.Add(item); + } + } + + private void OnReaderRemoved(object? sender, ReaderEventArgs e) + { + var item = Readers.FirstOrDefault(r => r.Name == e.ReaderName); + if (item != null) + { + item.PropertyChanged -= OnReaderItemPropertyChanged; + Readers.Remove(item); + } + } + + private void OnReaderItemPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (sender is ReaderItem item && e.PropertyName == nameof(ReaderItem.IsEnabled)) + { + if (item.IsEnabled) + { + _cardReaderService.EnableReader(item.Name); + _settings.DisabledReaders.Remove(item.Name); + } + else + { + _cardReaderService.DisableReader(item.Name); + _settings.DisabledReaders.Add(item.Name); + } + + _settingsService.Save(_settings); + } + } + + private void OnCardInserted(object? sender, CardEventArgs e) + { + if (e.CardData == null) + { + _logService.Warning("Card inserted but no data available"); + return; + } + + _logService.Info($"Processing card data ({e.CardData.Length} bytes)"); + + var record = NdefParser.ExtractFirstRecord(e.CardData); + if (record == null || string.IsNullOrEmpty(record.Payload)) + { + _logService.Warning("Failed to extract NDEF payload from card data"); + return; + } + + _logService.Info($"NDEF Payload extracted: {record.Payload}"); + _logService.Info($"Record type: {(record.IsUri ? "URI" : "Text/Other")}"); + + // Perform actions based on settings + if (CopyToClipboard) + { + _logService.Info("Copying to clipboard..."); + _actionService.CopyToClipboard(record.Payload); + _logService.Info("Copied to clipboard successfully"); + } + + if (LaunchUrls) + { + if (record.IsUri) + { + _logService.Info("Attempting to launch URL..."); + _actionService.LaunchUrl(record.Payload); + } + else + { + _logService.Info("Skipping browser launch - record is not a URI"); + } + } + + if (TypeAsKeyboard) + { + _logService.Info("Typing as keyboard input..."); + _actionService.TypeText(record.Payload); + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed2ab8b --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# NFC Actions + +A Windows 11 system tray application that monitors NFC card readers and performs automated actions when NFC tags are detected. + +## Features + +- **System Tray Application**: Runs in the background with a system tray icon +- **Multi-Reader Support**: Automatically detects and monitors all connected PC/SC-compatible NFC readers +- **Dynamic USB Detection**: Automatically handles USB reader plug/unplug events +- **Reader Selection**: Enable/disable monitoring for specific readers +- **NDEF Payload Extraction**: Reads NDEF data from NFC tags +- **Configurable Actions**: + - Copy NDEF payload to clipboard + - Launch URLs in default browser + - Type NDEF content as keyboard input +- **Persistent Settings**: Remembers reader preferences and action settings between sessions + +## Requirements + +- Windows 11 (or Windows 10 with PC/SC service) +- .NET 7.0 Runtime or later +- PC/SC-compatible NFC reader + +## Building the Application + +### Using Visual Studio 2022 + +1. Open `NfcActions.sln` in Visual Studio 2022 +2. Build the solution (Ctrl+Shift+B) +3. Run the application (F5) + +### Using .NET CLI + +```bash +dotnet build NfcActions.sln +dotnet run --project NfcActions/NfcActions.csproj +``` + +## Usage + +### Starting the Application + +When you run NFC Actions, it will: +1. Start minimized to the system tray +2. Show a notification balloon indicating it's running +3. Automatically detect all connected NFC readers +4. Begin monitoring enabled readers for card events + +### Accessing the Main Window + +- **Double-click** the tray icon, OR +- **Right-click** the tray icon and select "Open" + +### Configuring Readers + +In the main window: +1. View all detected NFC readers in the "Active Readers" list +2. Check/uncheck readers to enable/disable monitoring +3. Settings are saved automatically + +### Configuring Actions + +Select which actions to perform when an NFC tag is detected: + +- **Copy NDEF data to clipboard**: Copies the NDEF payload text to the clipboard +- **Launch URLs in default browser**: If the NDEF payload is a URL, opens it in your default browser +- **Type NDEF content as keyboard input**: Simulates typing the NDEF payload (useful for form filling) + +**Note**: Only the payload from the first NDEF record is used. Tags with multiple messages/records will use the first one. + +### Exiting the Application + +- Right-click the tray icon and select "Exit" + +## Settings + +Settings are stored in: +``` +%APPDATA%\NfcActions\settings.json +``` + +The settings file includes: +- List of disabled readers +- Action preferences (clipboard, URLs, keyboard) + +## Project Structure + +``` +NfcActions/ +β”œβ”€β”€ Models/ +β”‚ β”œβ”€β”€ AppSettings.cs # Application settings model +β”‚ └── ReaderItem.cs # Reader list item model +β”œβ”€β”€ Services/ +β”‚ β”œβ”€β”€ ActionService.cs # Handles clipboard, browser, and keyboard actions +β”‚ β”œβ”€β”€ CardReaderService.cs # PC/SC reader monitoring and card detection +β”‚ β”œβ”€β”€ NdefParser.cs # NDEF message parsing +β”‚ └── SettingsService.cs # Settings persistence +β”œβ”€β”€ ViewModels/ +β”‚ └── MainViewModel.cs # Main window view model +β”œβ”€β”€ App.xaml # Application resources and startup +β”œβ”€β”€ App.xaml.cs # Application lifecycle and tray icon +β”œβ”€β”€ MainWindow.xaml # Main window UI +└── MainWindow.xaml.cs # Main window code-behind +``` + +## Dependencies + +- **PCSC** (v7.0.1): PC/SC wrapper for smart card communication +- **PCSC.Iso7816** (v7.0.1): ISO 7816 APDU commands +- **InputSimulatorCore** (v1.0.5): Keyboard input simulation +- **System.Management** (v9.0.10): USB device detection (via WMI) + +## Technical Details + +### NDEF Parsing + +The application reads NDEF (NFC Data Exchange Format) messages from NFC tags using standard ISO 7816 APDUs: +- Selects the NDEF Tag Application +- Reads the Capability Container +- Reads the NDEF file +- Parses the NDEF message structure to extract the payload + +### Reader Monitoring + +The application polls PC/SC readers every 500ms to detect: +- New readers being connected +- Readers being disconnected +- Cards being inserted +- Cards being removed + +### Thread Safety + +All PC/SC operations are thread-safe and properly synchronized to handle concurrent reader access. + +## Troubleshooting + +### No Readers Detected + +- Ensure your NFC reader is properly connected +- Check that the Windows Smart Card service is running: + ``` + services.msc β†’ Smart Card + ``` +- Try unplugging and replugging the reader + +### Card Not Detected + +- Ensure the reader is enabled in the main window +- Try holding the card on the reader for a longer duration +- Some readers require specific card positioning + +### Actions Not Working + +- **Clipboard**: Ensure no other application has locked the clipboard +- **Browser**: Check that you have a default browser configured +- **Keyboard**: The keyboard simulation requires the target window to have focus + +## License + +This project is provided as-is for educational and development purposes. + +## Contributing + +Feel free to submit issues or pull requests for improvements or bug fixes.