Skip to content

Commit b6fc5e3

Browse files
feat(cmd/email): implement email integration with draft and send functionality
- Add 'draft' subcommand with upload/export/preview/plain-text-only modes - Add 'send-with-confirm' subcommand with confirmation flow - Implement automatic report directory detection (latest report-* dir) - Add --report flag to specify custom report directory - Add --no-set-message-id flag to skip Message-ID generation - Include comprehensive help text with usage examples - Support IMAP upload, .eml export, and preview display modes The email command generates emails based on evidence reports created by down-force, automatically selecting the latest report directory by default.
1 parent 36f5a3d commit b6fc5e3

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed

cmd/email.go

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*
2+
Copyright © 2025 canaria-computer
3+
*/
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/charmbracelet/log"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var (
14+
// Persistent flags for email command
15+
emailReportDir string
16+
17+
// Flags for draft subcommand
18+
emailDraftUpload bool
19+
emailDraftExport bool
20+
emailDraftPreview bool
21+
emailDraftPlainText bool
22+
23+
// Flags for send-with-confirm subcommand
24+
emailSendNoMessageID bool
25+
)
26+
27+
// emailCmd represents the email command
28+
var emailCmd = &cobra.Command{
29+
Use: "email",
30+
Short: "Generate and send emails based on evidence reports",
31+
Long: `Generate emails from evidence collection reports and send them via SMTP or export as .eml files.
32+
33+
The email command creates properly formatted emails based on down-force evidence reports,
34+
including attachments, HTML/plain-text content, and proper headers.
35+
36+
By default, the command automatically selects the most recent report directory
37+
(report-* with the latest timestamp). Use --report to specify a different directory.
38+
39+
Available subcommands:
40+
draft Create email drafts (upload to IMAP, export .eml, or preview)
41+
send-with-confirm Send email after user confirmation
42+
43+
Examples:
44+
# Preview email draft from latest report
45+
down-force email draft --preview
46+
47+
# Upload draft to IMAP server
48+
down-force email draft --upload
49+
50+
# Export draft as .eml file
51+
down-force email draft --export
52+
53+
# Send email with confirmation prompt
54+
down-force email send-with-confirm
55+
56+
# Use specific report directory
57+
down-force email draft --preview --report report-20240101-120000`,
58+
Run: func(cmd *cobra.Command, args []string) {
59+
cmd.Help()
60+
},
61+
}
62+
63+
// emailDraftCmd represents the draft subcommand
64+
var emailDraftCmd = &cobra.Command{
65+
Use: "draft",
66+
Short: "Create email draft from evidence report",
67+
Long: `Create an email draft based on the evidence collection report.
68+
69+
The draft command generates a properly formatted email with:
70+
- Subject line based on phishing target information
71+
- HTML and plain-text body with report summary
72+
- Attachments (screenshots, HTML files, evidence data)
73+
- Proper MIME encoding and headers
74+
75+
Output modes:
76+
--upload Upload draft to IMAP server's Drafts folder
77+
--export Export as .eml file to current directory
78+
--preview Display email preview in terminal
79+
--plain-text-only Generate plain-text only (no HTML)
80+
81+
If no mode is specified, --preview is used by default.
82+
83+
The command automatically detects the latest report directory (report-YYYYMMDD-HHMMSS)
84+
in the current working directory. Use --report to specify a different report.
85+
86+
Examples:
87+
# Preview draft from latest report
88+
down-force email draft
89+
down-force email draft --preview
90+
91+
# Upload to IMAP Drafts folder
92+
down-force email draft --upload
93+
94+
# Export as email.eml file
95+
down-force email draft --export
96+
97+
# Generate plain-text only email
98+
down-force email draft --plain-text-only
99+
100+
# Use specific report directory
101+
down-force email draft --preview --report report-20240101-120000
102+
103+
Configuration:
104+
IMAP/SMTP credentials should be configured via environment variables or config file.
105+
See 'down-force config email' for email configuration setup.`,
106+
Run: runEmailDraft,
107+
}
108+
109+
// emailSendCmd represents the send-with-confirm subcommand
110+
var emailSendCmd = &cobra.Command{
111+
Use: "send-with-confirm",
112+
Short: "Send email after confirmation",
113+
Long: `Generate email from evidence report and send it after user confirmation.
114+
115+
This command:
116+
1. Generates the email from the evidence report
117+
2. Displays a preview of the email content
118+
3. Prompts for user confirmation
119+
4. Sends the email via SMTP if confirmed
120+
5. Optionally sets Message-ID header for tracking
121+
122+
The Message-ID header is automatically generated by default for email tracking and threading.
123+
Use --no-set-message-id to skip Message-ID generation and let the mail server assign one.
124+
125+
Examples:
126+
# Send email with confirmation (default behavior)
127+
down-force email send-with-confirm
128+
129+
# Send without setting Message-ID
130+
down-force email send-with-confirm --no-set-message-id
131+
132+
# Send from specific report directory
133+
down-force email send-with-confirm --report report-20240101-120000
134+
135+
Configuration:
136+
SMTP server settings must be configured before sending.
137+
Use 'down-force config email' to set up SMTP credentials.
138+
139+
Security:
140+
- Passwords are never logged or displayed
141+
- TLS/SSL encryption is enforced for SMTP connections
142+
- Confirmation prompt prevents accidental sends`,
143+
Run: runEmailSend,
144+
}
145+
146+
func init() {
147+
rootCmd.AddCommand(emailCmd)
148+
emailCmd.AddCommand(emailDraftCmd)
149+
emailCmd.AddCommand(emailSendCmd)
150+
151+
// Persistent flags (available to all email subcommands)
152+
emailCmd.PersistentFlags().StringVar(&emailReportDir, "report", "",
153+
"Report directory to use (default: latest report-* directory)")
154+
155+
// Draft subcommand flags
156+
emailDraftCmd.Flags().BoolVar(&emailDraftUpload, "upload", false,
157+
"Upload draft to IMAP Drafts folder")
158+
emailDraftCmd.Flags().BoolVar(&emailDraftExport, "export", false,
159+
"Export draft as .eml file")
160+
emailDraftCmd.Flags().BoolVar(&emailDraftPreview, "preview", false,
161+
"Display email preview in terminal (default if no mode specified)")
162+
emailDraftCmd.Flags().BoolVar(&emailDraftPlainText, "plain-text-only", false,
163+
"Generate plain-text only email (no HTML)")
164+
165+
// Send subcommand flags
166+
emailSendCmd.Flags().BoolVar(&emailSendNoMessageID, "no-set-message-id", false,
167+
"Skip Message-ID generation (let server assign)")
168+
}
169+
170+
func runEmailDraft(cmd *cobra.Command, args []string) {
171+
// Determine report directory
172+
reportDir, err := getReportDirectory(emailReportDir)
173+
if err != nil {
174+
log.Fatalf("Failed to determine report directory: %v", err)
175+
}
176+
177+
log.Infof("Using report directory: %s", reportDir)
178+
179+
// Determine output mode (default to preview if none specified)
180+
if !emailDraftUpload && !emailDraftExport && !emailDraftPreview {
181+
emailDraftPreview = true
182+
}
183+
184+
// Validate that only one mode is selected
185+
modeCount := 0
186+
if emailDraftUpload {
187+
modeCount++
188+
}
189+
if emailDraftExport {
190+
modeCount++
191+
}
192+
if emailDraftPreview {
193+
modeCount++
194+
}
195+
196+
if modeCount > 1 {
197+
log.Fatal("Error: Only one output mode can be specified (--upload, --export, or --preview)")
198+
}
199+
200+
// Execute based on selected mode
201+
if emailDraftUpload {
202+
log.Info("Uploading draft to IMAP Drafts folder...")
203+
// TODO: Implement IMAP upload
204+
log.Warn("IMAP upload not yet implemented")
205+
} else if emailDraftExport {
206+
log.Info("Exporting draft as .eml file...")
207+
// TODO: Implement .eml export
208+
log.Warn(".eml export not yet implemented")
209+
} else if emailDraftPreview {
210+
log.Info("Displaying email preview...")
211+
// TODO: Implement preview display
212+
log.Warn("Preview display not yet implemented")
213+
}
214+
215+
if emailDraftPlainText {
216+
log.Info("Generating plain-text only format")
217+
}
218+
219+
fmt.Printf("\nReport directory: %s\n", reportDir)
220+
fmt.Println("Email draft functionality will be implemented here.")
221+
}
222+
223+
func runEmailSend(cmd *cobra.Command, args []string) {
224+
// Determine report directory
225+
reportDir, err := getReportDirectory(emailReportDir)
226+
if err != nil {
227+
log.Fatalf("Failed to determine report directory: %v", err)
228+
}
229+
230+
log.Infof("Using report directory: %s", reportDir)
231+
232+
if emailSendNoMessageID {
233+
log.Info("Message-ID generation disabled")
234+
} else {
235+
log.Info("Message-ID will be generated for tracking")
236+
}
237+
238+
// TODO: Implement email send with confirmation
239+
log.Warn("Email send functionality not yet implemented")
240+
241+
fmt.Printf("\nReport directory: %s\n", reportDir)
242+
fmt.Println("Email send-with-confirm functionality will be implemented here.")
243+
}
244+
245+
// getReportDirectory determines which report directory to use
246+
// If reportDir is specified, use that. Otherwise, find the latest report-* directory.
247+
func getReportDirectory(reportDir string) (string, error) {
248+
if reportDir != "" {
249+
// User specified a directory
250+
return reportDir, nil
251+
}
252+
253+
// TODO: Implement automatic detection of latest report-* directory
254+
// For now, return a placeholder
255+
return "report-latest", fmt.Errorf("automatic report directory detection not yet implemented")
256+
}

0 commit comments

Comments
 (0)