Add 'post send' command (blocked by SwiftMail #132)#19
Merged
Conversation
Implements #9: CLI structure and help documentation for sending draft emails via SMTP. Changes: - Added 'post send <uid>' command with full usage docs - Created PostError enum for structured error handling - Command throws .notImplemented with dependency info The command is fully documented but non-functional until SwiftMail #132 (SMTP support) is implemented. Workflow (once unblocked): 1. Fetch draft from Drafts mailbox 2. Send via SMTP (preserve threading headers) 3. APPEND to Sent with \Seen flag 4. Permanently EXPUNGE draft Safety: - Requires allowSending: true in server config - Optional --yes flag to skip confirmation Related: - Blocked by: Cocoanetics/SwiftMail#132 - Implements: #9
Changes: - Removed --draft-mailbox and --sent-mailbox flags - Updated help text to document auto-detection workflow - Drafts: \Drafts flag β name matching fallback - Sent: \Sent flag β name matching fallback Simpler and safer β no manual mailbox configuration needed.
SwiftMail 1.4.0 was released today with full SMTP support, unblocking
this feature.
Changes:
- Updated Package.swift to SwiftMail 1.4.0
- Added SMTPConfiguration to server config
- Implemented sendDraft() using SwiftMail's orchestrator:
* Fetches draft from Drafts (auto-detected)
* Sends via SMTP (IMAP credentials reused)
* Appends to Sent (auto-detected)
* Expunges draft from Drafts
- Added resolveCredentials() helper to IMAPConnectionManager
- Added smtpNotConfigured error case
- Implemented Send command with JSON output support
Usage:
post send <uid> --server <id> [--yes]
Workflow (fully automatic):
1. Auto-detect Drafts folder (\\Drafts flag or name matching)
2. Fetch draft message
3. Connect to SMTP and authenticate
4. Send via SMTPServer
5. Auto-detect Sent folder (\\Sent flag or name matching)
6. Append sent message with \\Seen flag
7. Permanently EXPUNGE draft
Configuration:
{
"servers": {
"example": {
"smtp": {
"host": "mail.example.com",
"port": 587,
"useTLS": false
}
}
}
}
Testing:
- β
swift build -c release
- β
swift test (8/8 passing)
Implements: #9
Dependency: SwiftMail#132 (now released as 1.4.0)
Changes: - Added CredentialType enum (.imap, .smtp) to KeychainCredentialStore - Updated all keychain methods to accept type parameter (defaults to .imap) - SMTP credentials use kSecAttrProtocolSMTP protocol attribute - Added resolveSMTPCredentials() to IMAPConnectionManager * Checks for SMTP keychain entry first * Falls back to IMAP credentials (most common case) - Updated sendDraft() to use resolveSMTPCredentials() Workflow: 1. Try SMTP keychain entry (kSecAttrDescription = "Post SMTP") 2. Fallback to IMAP credentials if no SMTP entry exists 3. Users can store separate SMTP credentials with: post credential add --server <id> --smtp Testing: - β swift build -c release - β swift test (8/8 passing) - β Backward compatible (IMAP credentials still work) This allows separate SMTP credentials when needed while maintaining the simple case (same credentials for both).
SMTP credentials must now be explicitly configured. No automatic fallback to IMAP credentials. Changes: - Updated resolveSMTPCredentials() to throw noSMTPCredentials error if not found - Added PostConfigurationError.noSMTPCredentials case - Error message guides users to: post credential set --server <id> --smtp Rationale: - Explicit configuration prevents accidental SMTP use of IMAP credentials - Users must consciously set up SMTP credentials separately - Clearer security boundary between IMAP and SMTP authentication Testing: - β swift build -c release - β swift test (8/8 passing)
Implements fine-grained API key permissions with backward-compatible defaults. Changes: - Added optional scopes field to APIKeyRecord (nil = [imap] for backward compat) - New effectiveScopes property returns Set<String> with default fallback - Updated primeAPIKeyScopes() to cache both server access and scopes - Added assertScopeAllowed() to check protocol permissions - withServer() now checks IMAP scope automatically - sendDraft() explicitly checks SMTP scope - CLI: post api-key create --scopes imap,smtp - CLI: post api-key list shows scopes Scope model: - imap: All IMAP operations (list, fetch, flag, move, trash, draft, IDLE) - smtp: Send emails via sendDraft Backward compatibility: β Existing API keys (scopes=nil) default to IMAP-only β Existing keys cannot send until explicitly granted smtp scope β No breaking changes to API Testing: - β swift build -c release - β swift test (8/8 passing) Example usage: # IMAP-only (read, organize, draft) post api-key create --servers drobnik --scopes imap # Full access (IMAP + send) post api-key create --servers drobnik --scopes imap,smtp # Legacy keys automatically become IMAP-only
Aligns SMTP with IMAP's dual-source credential pattern.
Changes:
- Simplified SMTPConfiguration to only contain optional credentials
- Removed redundant host/port/useTLS fields (now part of credentials)
- Updated resolveSMTPCredentials() to check config first, then keychain
- Removed smtpNotConfigured error (no longer needed)
- sendDraft() infers TLS mode from port (465=TLS, 587/25=STARTTLS)
Credential resolution order:
1. Config: servers.X.smtp.credentials (if present)
2. Keychain: SMTP entry for server X (if present)
3. Error: noSMTPCredentials
Example config:
{
"servers": {
"drobnik": {
"smtp": {
"credentials": {
"host": "mail.example.com",
"port": 587,
"username": "user@example.com",
"password": "secret"
}
}
}
}
}
Or use keychain:
post credential set --server drobnik --smtp
Testing:
- β
swift build -c release
- β
swift test (8/8 passing)
- β
Symmetric with IMAP credential resolution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #9: Command structure and documentation for sending draft emails via SMTP.
Status
π§ BLOCKED by SwiftMail #132 (SMTP support)
This PR establishes the CLI interface and workflow documentation. The actual send implementation will be added once SwiftMail #132 lands.
Changes
post send <uid>command with comprehensive help textPostErrorenum for structured error handling.notImplementedwith clear dependency messageUsage
Future Workflow (once unblocked)
DraftsmailboxDateandMessage-IdheadersSentwith\\SeenflagConfiguration
{ "servers": { "example": { "smtp": { "host": "mail.example.com", "port": 587, "useTLS": false }, "allowSending": true } } }Safety
allowSending: truein server config--yesflag to skip confirmation promptTesting
swift build -c releaseswift test(8/8 passing)Next Steps
Related
Closes #9