Skip to content

Commit 7370dc9

Browse files
committed
feat: enhance GPG key import to return fingerprint and handle missing user IDs
1. Changed return value: import_gpg_key() now returns: - Fingerprint string (40 chars) on success - None on failure (instead of True/False) 2. Fingerprint extraction (20 lines): - Extract using --import-options show-only before import - Fallback to --list-keys after import if needed 3. "No user ID" detection (14 lines): - Detect when keys.openpgp.org keys fail (no user ID) - Show helpful error message with solutions 4. Use fingerprint for encryption (5 lines): - If fingerprint returned, use it instead of email address
1 parent 9b8009e commit 7370dc9

File tree

1 file changed

+52
-11
lines changed

1 file changed

+52
-11
lines changed

imapbackup.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def import_gpg_key(source):
242242
source: String containing env var name, file path, URL, or key content
243243
244244
Returns:
245-
True if import succeeded, False otherwise
245+
Fingerprint string if import succeeded, None otherwise
246246
"""
247247
try:
248248
key_content = None
@@ -334,19 +334,55 @@ def download_key_operation():
334334
temp_key_file = f.name
335335

336336
try:
337+
# First, extract fingerprint using show-only
338+
fingerprint = None
339+
try:
340+
show_cmd = ['gpg', '--batch', '--import-options', 'show-only', '--import', '--with-colons', temp_key_file]
341+
show_result = subprocess.run(show_cmd, capture_output=True, text=True, check=True)
342+
for line in show_result.stdout.split('\n'):
343+
if line.startswith('fpr:'):
344+
fields = line.split(':')
345+
if len(fields) >= 10 and len(fields[9]) == 40:
346+
fingerprint = fields[9]
347+
print(" Extracted fingerprint: %s" % fingerprint)
348+
break
349+
except:
350+
pass # Will try after import
351+
352+
# Import the key
337353
cmd = ['gpg', '--batch', '--import', temp_key_file]
338354
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
339355

356+
# Check if key was skipped due to missing user ID
357+
if 'contains no user ID' in result.stderr or 'w/o user IDs' in result.stderr:
358+
print("\nERROR: GPG key import failed - key has no user ID")
359+
print("ERROR: The key from '%s' does not contain a user ID." % source_description)
360+
print("ERROR: This typically happens with keys from keys.openpgp.org when the email isn't verified.")
361+
print("ERROR:")
362+
print("ERROR: Solutions:")
363+
print("ERROR: 1. Verify your email on keys.openpgp.org and use the by-email URL")
364+
print("ERROR: 2. Provide the full PGP public key block directly (with user ID)")
365+
print("ERROR: 3. Upload your key to keyserver.ubuntu.com with full user ID")
366+
return None
367+
340368
print(" Successfully imported GPG key from %s" % source_description)
341369

342-
# Show imported key info if available in stderr
343-
if result.stderr:
344-
# GPG outputs import info to stderr
345-
for line in result.stderr.split('\n'):
346-
if 'imported:' in line.lower() or 'public key' in line.lower():
347-
print(" %s" % line.strip())
370+
# If fingerprint extraction failed before import, try after
371+
if not fingerprint:
372+
try:
373+
list_cmd = ['gpg', '--batch', '--list-keys', '--with-colons']
374+
list_result = subprocess.run(list_cmd, capture_output=True, text=True, check=True)
375+
for line in list_result.stdout.split('\n'):
376+
if line.startswith('fpr:'):
377+
fields = line.split(':')
378+
if len(fields) >= 10 and len(fields[9]) == 40:
379+
fingerprint = fields[9]
380+
break
381+
except:
382+
pass
348383

349-
return True
384+
# Return fingerprint if found, otherwise True for backwards compatibility
385+
return fingerprint if fingerprint else True
350386

351387
finally:
352388
# Clean up temp file
@@ -355,7 +391,7 @@ def download_key_operation():
355391

356392
except Exception as e:
357393
print(" WARNING: Failed to import GPG key: %s" % str(e))
358-
return False
394+
return None
359395

360396

361397
def encrypt_file_gpg(input_file, recipient):
@@ -1886,14 +1922,19 @@ def process_account(config):
18861922
# Import GPG key if specified (for encryption)
18871923
if config.get('gpg_import_key') and config.get('gpg_encrypt'):
18881924
print ("\nImporting GPG public key...")
1889-
key_imported = import_gpg_key(config['gpg_import_key'])
1890-
if not key_imported:
1925+
key_result = import_gpg_key(config['gpg_import_key'])
1926+
if not key_result:
18911927
print ("\nERROR: Failed to import GPG key for account '%s'" % config.get('account_name', 'unknown'))
18921928
print ("ERROR: Cannot proceed with GPG encryption enabled but key import failed")
18931929
print ("ERROR: Aborting backup to prevent unencrypted data from being created")
18941930
server.logout()
18951931
return False
18961932

1933+
# If import returned a fingerprint, use it instead of the configured recipient
1934+
if isinstance(key_result, str) and len(key_result) >= 16:
1935+
print (" Using imported key fingerprint for encryption: %s" % key_result)
1936+
config['gpg_recipient'] = key_result
1937+
18971938
# S3 Restore: Download and decrypt files before restore
18981939
if config.get('restore') and config.get('s3_upload'):
18991940
print ("\nDownloading files from S3 for restore...")

0 commit comments

Comments
 (0)