|
| 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