GithubHelp home page GithubHelp logo

tweedegolf / mailcrab Goto Github PK

View Code? Open in Web Editor NEW
691.0 8.0 30.0 978 KB

Email test server for development, written in Rust

License: Apache License 2.0

Dockerfile 0.19% Rust 84.62% HTML 0.64% SCSS 12.39% Smarty 2.15%

mailcrab's Introduction

MailCrab logo

MailCrab

Email test server for development, written in Rust.

Inspired by MailHog and MailCatcher.

MailCrab was created as an exercise in Rust, trying out Axum and functional components with Yew, but most of all because it is really enjoyable to write Rust code.

TLDR

docker run --rm -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

Features

  • Accept-all SMTP server
  • Web interface to view and inspect all incoming email
  • View formatted mail, download attachments, view headers or the complete raw mail contents
  • Single binary
  • Runs on all amd64 and arm64 platforms using docker
  • Just a 7.77 MB docker image

MailCrab screenshot

Related projects

  • CrabAlert is a macOS status bar application that notifies you of incoming messages in MailCrab

Technical overview

Both the backend server and the frontend are written in Rust. The backend receives email over an unencrypted connection on a configurable port. All email is stored in memory while the application is running. An API exposes all received email:

  • GET /api/messages return all message metadata
  • GET /api/message/[id] returns a complete message, given its id
  • POST /api/delete/[id] deletes a message, given its id
  • POST /api/delete-all deletes all messages
  • GET /api/version returns version information about the executable
  • GET /ws send email metadata to each connected client when a new email is received

The frontend initially performs a call to /api/messages to receive all existing email metadata and then subscribes for new messages using the websocket connection. When opening a message, the /api/message/[id] endpoint is used to retrieve the complete message body and raw email.

The backend also accepts a few commands over the websocket, to mark a message as opened, to delete a single message or delete all messages.

Installation and usage

You can run MailCrab using docker. Start MailCrab using the following command:

docker run --rm -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

Open a browser and navigate to http://localhost:1080 to view the web interface.

There are also (single) binary builds available, see https://github.com/tweedegolf/mailcrab/releases

Ports

The default SMTP port is 1025, the default HTTP port is 1080. You can configure the SMTP and HTTP port using environment variables (SMTP_PORT and HTTP_PORT), or by exposing them on different ports using docker:

docker run --rm -p 3000:1080 -p 2525:1025 marlonb/mailcrab:latest

Host

You can specify the host address MailCrab will listen on for HTTP request using the HTTP_HOST environment variable. In the docker image the default address is 0.0.0.0, when running MailCrab directly using cargo or a binary, the default is 127.0.0.1.

TLS

You can enable TLS and authentication by setting the environment variable ENABLE_TLS_AUTH=true. MailCrab will generate a key-pair and print the self-signed certificate. Any username/password combination is accepted. For example:

docker run --rm --env ENABLE_TLS_AUTH=true -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

It is also possible to provide your own certificate by mounting a key and a certificate to /app/key.pem and /app/cert.pem:

docker run --rm --env ENABLE_TLS_AUTH=true -v key.pem:/app/key.pem:ro -v cert.pem:/app/cert.pem:ro -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

Path prefix

You can configure a prefix path for the web interface by setting and environment variable named MAILCRAB_PREFIX, for example:

docker run --rm --env MAILCRAB_PREFIX=emails -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

The web interface will also be served at http://localhost:1080/emails/

Reverse proxy

See the reverse proxy guide.

Retention period

By default messages will be stored in memory until MailCrab is restarted. This might cause an OOM when MailCrab lives long enough and receives enough messages.

By setting MAILCRAB_RETENTION_PERIOD to a number of seconds, messages older than the provided duration will be cleared.

Performance

MailCrab is fast, although there is a bottleneck in the throughput of the websocket connection (between the server and the browser). If there are many messages sent at once (more than 100 per second) a client can lag behind and messages can get lost. When dealing with many messages at once, increasing the internal queue size can help to prevent losing messages. Use the QUEUE_CAPACITY environment variable to set the queue size. De default is 32, which means that MailCrab can handle 32 messages if the are all sent at the same time.

docker compose

Usage in a docker-compose.yml file:

version: '3.8'
services:
  mailcrab:
    image: marlonb/mailcrab:latest
    #        environment:
    #            ENABLE_TLS_AUTH: true # optionally enable TLS for the SMTP server
    #            MAILCRAB_PREFIX: emails # optionally prefix the webinterface with a path
    #        volumes:
    #           key.pem:/app/key.pem:ro # optionally provide your own keypair for TLS, else a pair will be generated
    #           cert.pem:/app/cert.pem:ro
    ports:
      - '1080:1080'
      - '1025:1025'
    networks: [default]

Kubernetes deployment

To deploy MailCrab to a Kubernetes cluster, you can use Helm Chart by cloning this repository and running:

helm install mailcrab ./charts/mailcrab -f values.yaml

For more information on configuring the Helm Chart, see the chart README.

Sample messages

The samples directory contains a couple of test messages. These can be sent using by running:

cd backend/
cargo test send_sample_messages -- --ignored

Alternatively you can send messages using curl:

curl smtp://127.0.0.1:1025 --mail-from [email protected] --mail-rcpt [email protected] --upload-file samples/normal.email
# with tls
curl -k --ssl-reqd smtps://127.0.0.1:1025 --mail-from [email protected] --mail-rcpt [email protected] --upload-file samples/normal.email --user 'user:pass'

Development

Install Rust and Trunk

# Add wasm as target if it it not present after following the install instructions for Trunk
rustup target add wasm32-unknown-unknown

# clone the code
git clone [email protected]:tweedegolf/mailcrab.git

# start the backend
cd backend
cargo run

# serve the frontend (in a new terminal window)
cd ../frontend
trunk serve

# optionally send test messages in an interval
cd ../backend
cargo test

mailcrab's People

Contributors

alekangelov avatar alienscience avatar epilys avatar hdevalke avatar luiseduardobrito avatar marlonbaeten avatar nfedyashev avatar norris1z avatar sahidrahman404 avatar stappersg avatar thanhquang1988 avatar tk-nguyen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mailcrab's Issues

Have less real test email addresses

Hi,

Yeah, tests should be a real as possible. And yes, tests should just remain tests.

Currently there a "real" test addresses.

image

This feels wrong.

Please replace the gmail and hotmail email addresses with bogus ones.

Mailcrab and Lettre

I've been using Mailhog in development and I switched it with Mailcrab.

I'm using lettre to send email.

I use the following code to send email https://github.com/purton-tech/barricade/blob/master/crates/actix-server/src/email.rs

with the following creds

SMTP_HOST: smtp
SMTP_PORT: 1025
SMTP_USERNAME: thisisnotused
SMTP_PASSWORD: thisisnotused
SMTP_TLS_OFF: 'true'

I get

Could not send email: lettre::transport::smtp::Error { kind: Client, source: "No compatible authentication mechanism was found" }

Is there something obvious I'm missing.

Thanks for building this BTW it looks really great.

UI title is not updated

When we have an email in our inbox we see in the title of the webpage (N) MailCrab, where N is the number of new emails

After clicking the Remove All button the title is not updated and is left with (N). To update a title the page must be reloaded.

Unable to send multiple emails

To reproduce, run the following code inside an actix-web route (adapted from Lettre example, just testing things out here):

    let email = Message::builder()
        .from("Test <[email protected]>".parse().unwrap())
        .reply_to("Test <[email protected]>".parse().unwrap())
        .to("Bob <[email protected]>".parse().unwrap())
        .subject("Happy new year!!")
        .header(ContentType::TEXT_PLAIN)
        .body(String::from("Be happy!!!"))
        .unwrap();

    let mailer = SmtpTransport::builder_dangerous("127.0.0.1")
        .port(1025)
        .build();

    // Send the email
    match mailer.send(&email) {
        Ok(_) => println!("Email sent successfully!"),
        Err(e) => panic!("Could not send email: {e:?}"),
    }
    return HttpResponse::Ok().finish();

First email sends successfully, subsequent emails cause the following error:

thread 'actix-rt|system:0|arbiter:1' panicked at 'Could not send email: lettre::transport::smtp::Error { kind: Network, source: Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" } }', src/routes/users.rs:27:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'actix-rt|system:0|arbiter:0' panicked at 'Could not send email: lettre::transport::smtp::Error { kind: Network, source: Os { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" } }', src/routes/users.rs:27:19

The Docker container is still running fine. Any idea what could be causing this?

Clean commits

This issue is for having a place to elaborate upcoming merge requests for me.
Some of those _MR_s will have @marlonbaeten as author.

Goal of this issue is having clean commits. Assumed is that we know that the reason for clean commits is that it is a way to tell our future selfs and co-workers what did change and for which reason.

Sendmail client

In PHP I've used https://github.com/mailhog/mhsendmail so I could use PHP to talk to a SMTP server (in this instance MailHog):

SMTP = mailhog
smtp_port = 1025
sendmail_path = '/usr/bin/mhsendmail --smtp-addr="mailhog:1025"'

(php.ini)

Is that something that should work just the same (Note: I have not tried yet) with mailcrab or should that part also be rewritten in Rust?

Thanks!

// Meanwhile I tried this setup but replaced "mailhog" with "mailcrab" (both listen on the 1025 port) but it didnt work out of the box. Not sure where to look now.

Dedicated tab for displaying the plain/text part?

I'm sending emails with both text/html and text/plain mime parts.

The MailCrab UI shows me the HTML email in the Formatted tab, and I can probably look at the plain part in Raw, but it's not very convenient.

It'd be nice to have a dedicated Plain tab which shows the text/plain version.

Running mailcrab with a prefix behind a proxy

Hei, I saw the LinkedIn post and I immediately jumped on this (as I was using MailHog and a MacBook M1), great job!

That said, in our current setup we had all dev tools behind a proxy (MailHog was at localhost:8001/smtp). MailCrab does not support running from a different prefix and will instead grab files from http://localhost:8001/mailcrab-frontend-520e3bc03b391c36.js even though it should be http://localhost:8001/smtp/mailcrab-frontend-520e3bc03b391c36.js.

Is it something you can and want to support?

Image does not shut down cleanly in Docker

When running Mailcrab in docker-compose, the image does not cleanly shut down. It waits 10 seconds, whereupon Docker forcibly kills it. You should listen to the SIGINT and SIGTERM signals and shut down the application.

I don't have an example for rust, but in JavaScript:

process.on('SIGINT', () => {
  console.info('SIGINT signal received: Interrupted')
  process.exit(0)
})

export function registerSigTerminate(httpServer: any) {
  process.on('SIGTERM', () => {
    console.info('SIGTERM signal received: closing HTTP server')
    httpServer.close(() => {
      console.log('')
    })
  })
}

P.S. Love Mailcrab, displays emails so well compared to our previous application!

How many connections mailcrab can open?

I have a strange situation when using mailcrab docker image with php and symfony.

Iโ€™ve started a mailcrab container, symfony consumer container and symfony cli container.

When consumer is started I can not open a connection from cli container. I get request timeout. If I stop the consumer container everything is working from cli.

It seems that consumer container connects to mailcrab and makes its port busy so mailcrab can not open more connections for other clients at the same time.

Mailcrab has only one thread? Or this is only mine problem ?

thanks

Links open in IFrame

Hi folks,
really nice you provide a solution for the not-touched-in-2-years mailhog (that has no arm image on dockerhub).

When receiving mails in mailcrab, opening links in those mails will not open them in the top frame but in the little iframe, where the mail is displayed.

I would be fine with links opening in a new tab by default.

Thanks for the effort you've put into this, really appreciate it!

Kind regards
Patrick

Wish: Report `250 2.0.0 Ok: queued as 17409e25-7190-4d09-954f-5f0ca794940f`

Hello,

A nice to have would be mailcrab reporting to sending side with which reference the message was queued. Simular as a MTA as postfix does.

$ swaks --to [email protected]
=== Trying rosa.stappers.it:25...
=== Connected to rosa.stappers.it.
<-  220 rosa.stappers.it ESMTP Postfix (Debian/GNU)
 -> EHLO redacted
    ...
 -> 
 -> .
<-  250 2.0.0 Ok: queued as E191A1FD3
 -> QUIT
<-  221 2.0.0 Bye
=== Connection closed with remote host.
$

It would be something like

--- a/backend/src/mail_server.rs
+++ b/backend/src/mail_server.rs
@@ -93,13 +93,13 @@ impl mailin::Handler for MailHandler {
         Ok(())
     }
 
-    fn data_end(&mut self) -> mailin::Response {
+    fn data_end(&mut self, id: &str) -> mailin::Response {
         if let Err(e) = self.parse_mail() {
             event!(Level::WARN, "Error parsing email: {}", e);
 
             mailin::response::Response::custom(500, "Error parsing message".to_string())
         } else {
-            mailin::response::OK
+            mailin::response::Response::custom(250, "2.0.0 Ok: queued as {}".to_string(), id)
         }
     }
 

But that will need a change in the mailin library.

Maybe there are other options.

MailCrab always proceeds `AUTH PLAIN` even though `ENABLE_TLS_AUTH` is not set

I just discovered the app today, and thanks a lot for this awesome tiny app! The UI looks so nice and clean, and it even has dark mode which is just ๐Ÿš€.

However, when I'm using it as a replacement for MailCatcher, I cannot receive any email from my app. When launching MailCrab with RUST_LOG=debug to find the pitfall, I found that the backend always got Sending: 503 Bad sequence of commands after Received: AUTH PLAIN AAA=.
When I set ENABLE_TLS_AUTH=true, my app just hangs and on the backend side of MailCrab nothing is logged after the message Connection from 127.0.0.1:{random port} (just like when running the curl command without -k --ssl-reqd options).
Therefore, I think my app just doesn't have a proper SSL authentication in the development environment, and as MailCrab always proceeds AUTH PLAIN no matter ENABLE_TLS_AUTH is set or not, I cannot see any email from my app.

The key thing here is that I'm running MailCatcher/MailCrab mostly in a development environment, so I think it should be better to just ignore the AUTH PLAIN header, right? Both MailCatcher and MailHog seem to be doing it as they can receive my app emails normally (just that their UI is meh...).

Net::SMTPAuthenticationError: 503 Bad sequence of commands

Thanks a lot for your work, Mailcrab looks nice!

I tried to use it as a drop in replacement for Mailcatcher, but I'm getting Net::SMTPAuthenticationError: 503 Bad sequence of commands probably because the SMTP library tries to authenticate. Is it possible to add support for this (without actual authentication, but just accepting the messages)?

Certificate not printed

The README states:

You can enable TLS and authentication by setting the environment variable ENABLE_TLS_AUTH=true. MailCrab will generate a key-pair and print the self-signed certificate.

With the latest version, though, the certificate is no longer printed. All I see in the logs is this:

2024-02-08T08:14:04.442398Z  INFO mailcrab_backend::smtp::tls: Generating self-signed certificate...
2024-02-08T08:14:04.443203Z  INFO mailcrab_backend::smtp::tls: TLS configuration loaded

It would be great to get this behaviour back as it made standing MailCrab up for quick tests much simpler.

Inline attachments not rendering

Hi,

Love this project! Thanks for making mailcrab :)

I have trouble with my emails sent from lettre - for some reason the inline attachments (CID:) are not rendered in frontend.
Emails are rendered as if they got a normal attachment and the inline image has cid:XXXXX in src (it's not replaced with base64 image).

Tested the issue on all versions starting with v0.19.0 in which you implemented inline attachments :)

Here is an example email that is not rendered:

From: "Mario" <[email protected]>
Reply-To: "Mario" <[email protected]>
To: [email protected]
Subject: Hello there Luigi
MIME-Version: 1.0
Date: Wed, 20 Sep 2023 11:42:25 +0000
Content-Type: multipart/mixed;
 boundary="vpATvfXU7VV6ausCY1QJpvHeVAnLg3FPdHr6QBLj"

--vpATvfXU7VV6ausCY1QJpvHeVAnLg3FPdHr6QBLj
Content-ID: <cid:TCrOweD5>
Content-Disposition: inline
Content-Type: image/png
Content-Transfer-Encoding: base64

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
+v8AJXhwo04lqu0AAAAASUVORK5CYII=
--vpATvfXU7VV6ausCY1QJpvHeVAnLg3FPdHr6QBLj
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit

<!DOCTYPE>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <p>Hello,</p>
    <p>It's a me Mario.</p>
    
    <p>-- </p>
    <p>Best regards,  </p>
    <img src="cid:TCrOweD5" />
  </body>
</html>
--vpATvfXU7VV6ausCY1QJpvHeVAnLg3FPdHr6QBLj--

Support CID embeds

Problem

For Symfony projects I've switched from Mailtrap to Mailcrab, and I've just noticed that images embedded with CID are not being displayed in Mailcrab's formatted view.

Raw (truncated email example)

MIME-Version: 1.0
Date: Sat, 27 May 2023 14:45:54 +0200
Content-Type: multipart/related; boundary=VALLHhYX

--VALLHhYX
Content-Type: multipart/alternative; boundary=1mS-w7PU

--1mS-w7PU
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

--1mS-w7PU
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<img src=3D"cid:03487338a9ba70e29f37dd06781f9a95@symfony" alt=3D"">
--1mS-w7PU--

--VALLHhYX
Content-ID: <03487338a9ba70e29f37dd06781f9a95@symfony>
Content-Type: image/png; name="03487338a9ba70e29f37dd06781f9a95@symfony"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 name="03487338a9ba70e29f37dd06781f9a95@symfony";
 filename="@images/favicon.png"

<base64-string-here>
--VALLHhYX--

As you can see in the raw example above, the image src with cid 03487338a9ba70e29f37dd06781f9a95@symfony is available, which contains a base64 encoded string of the image file. But the image itself is available in the email as an attachment instead of being embedded.

image

So it just shows the broken image icon in the formatted view.

image

Proposal

Support for CID embeds.

Convert new line characters to br tag

Hello. It would be pretty nice to convert new line characters in webUI to
tag in HTML

Or at least have such an option.

current behavior
if we have text

this is 
text with new line

it will be displayed in webUI as

this istext with new line

It raise WebSocket error when binding to other ports on Docker image.

Hello.
When I bind HTTP port to 8025, it raise ws connection error on js console.

panicked at 'called Result::unwrap() on an Err value: JsError { name: "SyntaxError", message: "Failed to construct 'WebSocket': The URL contains a fragment identifier ('/ws'). Fragment identifiers are not allowed in WebSocket URLs." }', src/websocket.rs:31:45
Stack:
Error
at imports.wbg.__wbg_new_abda76e883ba8a5f (http://localhost:8025/static/mailcrab-frontend-b60440a4cc688da.js:368:21)

Uncaught (in promise) RuntimeError: unreachable
at __rust_start_panic (mailcrab-frontend-b60440a4cc688da_bg.wasm:0x258678)

My docker-compose.yaml

services:
  mail:
    image: marlonb/mailcrab
    ports:
      - '8025:1080'
      - '1025:1025'

Path prefix

Hi there thanks for making this! I've been bummed about mailhog ever since buying my M1 MacBook.

No issue for me because at this point it's just a configuration change in my dev server, but FYI I'm not getting any love from the path prefix setting. Adding a MAILCRAB_PREFIX to my docker-compose.yml doesn't produce any errors, but I still get 404 not found in the browser, because the HTML page arrives with links to files based in the root directory (i.e. src='./static/...').

Support TLS and (arbitrary) credentials

Support running the SMTP endpoint of mailcrab over a TLS connection and accept any credentials. This might help keeping your app configuration as close to production as possible.

Build to single binary

MailCrab could be build to a single binary:

  • Allow building to single binary (by including static assets)
  • Automatically build binaries for various architectures on every release using GA

logging of expire deletion

The events of incoming email and API delete email are being logged.

Deletion of email due expire, age > MAILCRAB_RETENTION_PERIOD, is not logged.

Unclear what is needed for development

It's unclear for me how to start the develop trajectory. Apparently I need to have a dist directory created by running trunk build. And also have rustup target add wasm32-unknown-unknown added. And then still it complains I need https://docs.rs/getrandom/latest/getrandom/#webassembly-support. Can you add that in the description part?

Storage retention

Thank you for this nice project.

Storing the mails in a hashmap eventually will make the process crash with oom.
I had a quick look and it looks like it should be possible to add a retention interval to keep the memory usage under control.

I had a quick look into the code and i think this patch is one way to implement it.

diff --git a/backend/src/main.rs b/backend/src/main.rs
index f9ea6d5..467a484 100644
--- a/backend/src/main.rs
+++ b/backend/src/main.rs
@@ -8,13 +8,14 @@ use std::{
     process,
     str::FromStr,
     sync::{Arc, RwLock},
+    time::SystemTime,
 };
 use tokio::sync::broadcast::{Receiver, Sender};
 use tokio::time::Duration;
 use tokio_graceful_shutdown::{SubsystemHandle, Toplevel};
 use tracing::{event, Level};
 use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt};
-use types::{MailMessage, MessageId};
+use types::{MailMessage, MailMessageMetadata, MessageId};
 
 use crate::web_server::http_server;
 
@@ -27,6 +28,7 @@ pub struct AppState {
     storage: RwLock<HashMap<MessageId, MailMessage>>,
     prefix: String,
     index: Option<String>,
+    retention_interval: Duration,
 }
 
 #[derive(RustEmbed)]
@@ -63,6 +65,7 @@ async fn storage(
     handle: SubsystemHandle,
 ) -> Result<(), Infallible> {
     let mut running = true;
+    let mut retention_interval = tokio::time::interval(state.retention_interval);
 
     while running {
         tokio::select! {
@@ -73,6 +76,21 @@ async fn storage(
                     }
                 }
             },
+            _ = retention_interval.tick() => {
+                if let Ok(mut storage) = state.storage.write() {
+                    storage.retain(|_, mail_message| {
+                        let meta = MailMessageMetadata::from(mail_message.clone());
+                        let remove_before = std::time::SystemTime::now()
+                        .checked_sub(Duration::from_secs(60))
+                        .unwrap()
+                        .duration_since(SystemTime::UNIX_EPOCH)
+                        .unwrap()
+                        .as_secs() as i64;
+                        meta.time
+                            > remove_before
+                    });
+                }
+            }
             _ = handle.on_shutdown_requested() => {
                 running = false;
             }
@@ -129,6 +147,13 @@ async fn main() {
     let prefix = std::env::var("MAILCRAB_PREFIX").unwrap_or_default();
     let prefix = format!("/{}", prefix.trim_matches('/'));
 
+    let retention_interval =
+        std::env::var("MAILCRAB_RETENTION_INTERVAL").unwrap_or_else(|_| "3600".into());
+    let retention_interval = retention_interval
+        .parse::<u64>()
+        .map(Duration::from_secs)
+        .unwrap_or_default();
+
     event!(
         Level::INFO,
         "MailCrab HTTP server starting on {http_host}:{http_port} and SMTP server on {smtp_host}:{smtp_port}"
@@ -142,6 +167,7 @@ async fn main() {
         storage: Default::default(),
         index: load_index(&prefix),
         prefix,
+        retention_interval,
     });
 
     // store broadcasted messages in a key/value store

Error parsing email: Could not parse From address header

Hey!

I ran into this project while researching ARM supported alternatives to Mailhog, it looks great! I'm all setup, but can't get Mailcrab to receive e-mails yet..

I'm on a docker setup, and when I use docker exec -it mailcrab sh to get into the container, and then run something like:

echo "Subject: hello" | sendmail -S 127.0.0.1:1025 -v [email protected] < mail.txt -f "[email protected]"

The response I get is:

sendmail: recv:'220 mailcrab-backend ESMTP'
sendmail: send:'EHLO f3f6ce0b2354'
sendmail: recv:'250-server offers extensions:'
sendmail: recv:'250 8BITMIME'
sendmail: send:'MAIL FROM:<[email protected]>'
sendmail: recv:'250 OK'
sendmail: send:'RCPT TO:<[email protected]>'
sendmail: recv:'250 OK'
sendmail: send:'DATA'
sendmail: recv:'354 Start mail input; end with <CRLF>.<CRLF>'
sendmail: send:'To: [email protected]'
sendmail: send:'test'
sendmail: send:'.'
sendmail: recv:'250 OK'
sendmail: send:'QUIT'
sendmail: recv:'221 Goodbye'

The mailhog docker logs show:

mailcrab  | 2023-05-08T12:19:10.465302Z  INFO mailcrab_backend::mail_server: New email on f3f6ce0b2354 from [email protected] to ["[email protected]"]
mailcrab  | 2023-05-08T12:19:10.465468Z  WARN mailcrab_backend::mail_server: Error parsing email: Could not parse From  address header

This is my configuration:

docker-compose.yml

  mailcrab:
    container_name: mailcrab
    image: marlonb/mailcrab:latest
    ports:
      - '1080:1080'
      - '1025:1025'
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mail.rule=Host(`mail.c1`)"
      - "traefik.http.services.mail.loadbalancer.server.port=1080"
      - "traefik.http.routers.mail.tls=true"

I'm not sure how to further debug this issue, and I really don't get why Mailcrab is not able to parse the address header.

I'd appreciate any insight on this!

/ Marinus

test test::send_sample_messages

Hello,

Sharing this with you all

stappers@messer:~/src/github/mailcrab/backend
$ cargo test send_sample_messages -- --ignored
    Finished test [unoptimized + debuginfo] target(s) in 0.06s
     Running unittests src/main.rs (target/debug/deps/mailcrab_backend-97d1a98261d37e44)

running 1 test
test test::send_sample_messages ... FAILED

failures:

---- test::send_sample_messages stdout ----
thread 'test::send_sample_messages' panicked at 'called `Result::unwrap()` on
 an `Err` value: MissingParts', src/main.rs:366:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    test::send_sample_messages

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.09s

error: test failed, to rerun pass `--bin mailcrab-backend`
stappers@messer:~/src/github/mailcrab/backend
$ git checkout coexist 
Switched to branch 'coexist'
stappers@messer:~/src/github/mailcrab/backend
$ cargo test send_sample_messages -- --ignored
   Compiling mailcrab-backend v0.10.0 (/home/stappers/src/github/mailcrab/backend)
    Finished test [unoptimized + debuginfo] target(s) in 1.80s
     Running unittests src/main.rs (target/debug/deps/mailcrab_backend-97d1a98261d37e44)

running 1 test
test test::send_sample_messages ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.19s

stappers@messer:~/src/github/mailcrab/backend
$ cargo test send_sample_messages -- --ignored
    Finished test [unoptimized + debuginfo] target(s) in 0.06s
     Running unittests src/main.rs (target/debug/deps/mailcrab_backend-97d1a98261d37e44)

running 1 test
test test::send_sample_messages ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.20s

stappers@messer:~/src/github/mailcrab/backend
$ 

It says:

  • When I do cargo test send_sample_messages -- --ignored I get a completely useless error.
  • When executing that same command in branch coexist I get ok.

This issue should make explaining an upcoming merge request easy.

Feature request: Stay on current tab if I click on another email

Hey, thanks for this! <3

It would be quite a QoL improvement if the tab didn't always jump back to 'Plain' whenever I clicked on another email. It's often that I want to compare headers between two emails I've received, and I always have to click back to the 'Headers' tab after selecting a different email. It's a bit tedious.

Keep alive on websocket

If you tab away from mailcrab for a few minutes (4+ minutes) it will not receive any new emails automatically and it will require a refresh. Even if you stay in the tab for a hot minute it will not show new mails. The websocket seems to be in limbo.

Tested with Chrome and MailCrab v0.16

Fail to send an email when TLS is enabled

Hi,
Thank you for the tool.
With the latest version, I can't seem to send an email when TLS is enabled, the sending loops indefinitely, this wasn't the case with previous versions.

Steps to reproduce:

  • Run the docker container:
docker run --rm --env ENABLE_TLS_AUTH=true -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest
  • Send the email:
curl smtp://127.0.0.1:1025 --mail-from [email protected] --mail-rcpt [email protected] --upload-file samples/normal.email --user 'user:pass'

It does work when using:

docker run --rm --env ENABLE_TLS_AUTH=true -p 1080:1080 -p 1025:1025 marlonb/mailcrab:latest

Behind a reverse proxy

Summary: What to do extra / special to put mailcrab behind a reverse proxy

Just mailcarb works as documented in the README.md. ( backend: cargo run, frontend: trunk serve, sendmail: swaks --to foo@bar --server 127.0.0.1:1025, URL: http://127.0.0.1:1080 )

Behind a reverse proxy I have not so much success.

It is Apache that I use as reverse proxy, this is in /etc/apache2/sites-available/000-default.conf:

$ grep -ve \# /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>

	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/html

	<location /mailcrab>
        Require all granted
        ProxyPass http://127.0.0.1:1080
        ProxyPassReverse http://127.0.0.1:1080
        </location>


	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>
$

Visiting URL http://127.0.0.1/mailcrab or http://127.0.0.1/mailcrab/ gives nothing.
Visiting URL http://127.0.0.1/mailcrab/api/messages gives JSON.

Webbrowser is firefox-esr, 91.12.0esr-1~deb10u1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.