mirror of
https://github.com/overcuriousity/trace.git
synced 2025-12-20 13:02:21 +00:00
Display raw PGP signature in verification dialog
Simplified approach: When pressing 'V' in note details, the TUI temporarily exits to terminal mode and prints the raw PGP signature directly to stdout. This allows users to: - Select and copy the signature using their terminal's native copy/paste - Works with any shell (bash, fish, zsh, etc.) - No dependency on clipboard tools (xclip, xsel, pbcopy, clip) - No complex shell-specific commands needed The signature is displayed with verification status and clear formatting, then waits for Enter to return to the TUI. This is the simplest and most universal solution for viewing and copying PGP signatures for external verification in Kleopatra or GPG tools.
This commit is contained in:
146
trace/tui_app.py
146
trace/tui_app.py
@@ -119,7 +119,7 @@ class TUI:
|
|||||||
self.flash_time = time.time()
|
self.flash_time = time.time()
|
||||||
|
|
||||||
def verify_note_signature(self):
|
def verify_note_signature(self):
|
||||||
"""Show detailed verification dialog for current note with signature export"""
|
"""Show signature verification and print raw signature to terminal"""
|
||||||
if not self.current_note:
|
if not self.current_note:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -137,139 +137,31 @@ class TUI:
|
|||||||
self._show_simple_dialog(title, message)
|
self._show_simple_dialog(title, message)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Save signature to file and attempt clipboard copy
|
# Temporarily exit curses to print signature to terminal
|
||||||
import subprocess
|
curses.endwin()
|
||||||
import platform
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
sig_file = Path.home() / ".trace" / "last_signature.txt"
|
|
||||||
sig_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sig_file.write_text(self.current_note.signature)
|
# Print verification status
|
||||||
file_saved = True
|
print("\n" + "=" * 70)
|
||||||
except Exception:
|
|
||||||
file_saved = False
|
|
||||||
|
|
||||||
# Try to copy to clipboard
|
|
||||||
clipboard_success = False
|
|
||||||
clipboard_method = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
system = platform.system()
|
|
||||||
if system == "Linux":
|
|
||||||
# Try xclip first, then xsel
|
|
||||||
try:
|
|
||||||
subprocess.run(['xclip', '-selection', 'clipboard'],
|
|
||||||
input=self.current_note.signature.encode(),
|
|
||||||
check=True, timeout=2, capture_output=True)
|
|
||||||
clipboard_success = True
|
|
||||||
clipboard_method = "xclip"
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
||||||
try:
|
|
||||||
subprocess.run(['xsel', '--clipboard', '--input'],
|
|
||||||
input=self.current_note.signature.encode(),
|
|
||||||
check=True, timeout=2, capture_output=True)
|
|
||||||
clipboard_success = True
|
|
||||||
clipboard_method = "xsel"
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
||||||
pass
|
|
||||||
elif system == "Darwin": # macOS
|
|
||||||
try:
|
|
||||||
subprocess.run(['pbcopy'],
|
|
||||||
input=self.current_note.signature.encode(),
|
|
||||||
check=True, timeout=2, capture_output=True)
|
|
||||||
clipboard_success = True
|
|
||||||
clipboard_method = "pbcopy"
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
||||||
pass
|
|
||||||
elif system == "Windows":
|
|
||||||
try:
|
|
||||||
subprocess.run(['clip'],
|
|
||||||
input=self.current_note.signature.encode(),
|
|
||||||
check=True, timeout=2, capture_output=True)
|
|
||||||
clipboard_success = True
|
|
||||||
clipboard_method = "clip"
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Build dialog message based on verification and export status
|
|
||||||
if verified:
|
if verified:
|
||||||
title = "✓ Signature Verified"
|
print(f"✓ SIGNATURE VERIFIED - Signed by: {info}")
|
||||||
message = [
|
|
||||||
"The note's signature is valid.",
|
|
||||||
"",
|
|
||||||
f"Signed by: {info}",
|
|
||||||
"",
|
|
||||||
"This note has not been tampered with since signing.",
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
title = "✗ Signature Verification Failed"
|
print(f"✗ SIGNATURE VERIFICATION FAILED - Reason: {info}")
|
||||||
message = [
|
print("=" * 70)
|
||||||
"The note's signature could not be verified.",
|
print("\nRAW PGP SIGNATURE (select and copy from terminal):")
|
||||||
"",
|
print("-" * 70)
|
||||||
f"Reason: {info}",
|
|
||||||
"",
|
|
||||||
"Possible causes:",
|
|
||||||
"- Public key not in keyring",
|
|
||||||
"- Note content was modified after signing",
|
|
||||||
"- Signature is corrupted",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add export status information
|
# Print the actual signature
|
||||||
message.append("")
|
print(self.current_note.signature)
|
||||||
message.append("─" * 60)
|
|
||||||
message.append("EXPORT STATUS:")
|
|
||||||
message.append("")
|
|
||||||
|
|
||||||
# Clipboard status with clear feedback
|
print("-" * 70)
|
||||||
if clipboard_success:
|
print("\nPress Enter to return to trace...")
|
||||||
message.append(f"✓ Clipboard: Copied successfully (using {clipboard_method})")
|
input()
|
||||||
message.append("")
|
|
||||||
message.append(" You can paste directly into Kleopatra or GPG tools.")
|
|
||||||
else:
|
|
||||||
message.append("✗ Clipboard: Failed to copy")
|
|
||||||
message.append("")
|
|
||||||
if system == "Linux":
|
|
||||||
message.append(" Install xclip or xsel for clipboard support:")
|
|
||||||
message.append(" sudo apt install xclip # Debian/Ubuntu")
|
|
||||||
message.append(" sudo dnf install xclip # Fedora")
|
|
||||||
else:
|
|
||||||
message.append(" Clipboard tool not available on this system.")
|
|
||||||
|
|
||||||
message.append("")
|
finally:
|
||||||
|
# Restore curses mode
|
||||||
# File save status
|
self.stdscr.refresh()
|
||||||
if file_saved:
|
curses.doupdate()
|
||||||
message.append(f"✓ File: Saved to {sig_file}")
|
|
||||||
else:
|
|
||||||
message.append("✗ File: Failed to save (check permissions)")
|
|
||||||
|
|
||||||
# Add GPG verification commands if file was saved
|
|
||||||
if file_saved:
|
|
||||||
message.append("")
|
|
||||||
message.append("─" * 60)
|
|
||||||
message.append("VERIFY WITH GPG:")
|
|
||||||
message.append("")
|
|
||||||
|
|
||||||
# Linux/macOS commands
|
|
||||||
message.append("Linux/macOS:")
|
|
||||||
message.append(f" gpg --verify <(cat {sig_file})")
|
|
||||||
message.append("")
|
|
||||||
message.append(" # Or view the signature:")
|
|
||||||
message.append(f" cat {sig_file}")
|
|
||||||
message.append("")
|
|
||||||
|
|
||||||
# Windows PowerShell commands
|
|
||||||
message.append("Windows PowerShell:")
|
|
||||||
message.append(f" Get-Content {sig_file} | gpg --verify")
|
|
||||||
message.append("")
|
|
||||||
message.append(" # Or view the signature:")
|
|
||||||
message.append(f" Get-Content {sig_file}")
|
|
||||||
|
|
||||||
self._show_simple_dialog(title, message)
|
|
||||||
|
|
||||||
def _show_simple_dialog(self, title, message_lines):
|
def _show_simple_dialog(self, title, message_lines):
|
||||||
"""Display a simple scrollable dialog with the given title and message lines"""
|
"""Display a simple scrollable dialog with the given title and message lines"""
|
||||||
|
|||||||
Reference in New Issue
Block a user