{"id":18041651,"url":"https://github.com/katyo/new-tokio-smtp","last_synced_at":"2025-06-28T23:38:44.060Z","repository":{"id":66335238,"uuid":"156162506","full_name":"katyo/new-tokio-smtp","owner":"katyo","description":"extendible SMTP (Simple Mail Transfer Protocol) implementation using tokio","archived":false,"fork":false,"pushed_at":"2018-11-05T06:04:14.000Z","size":197,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T02:28:08.394Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/katyo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-11-05T04:51:03.000Z","updated_at":"2022-06-05T20:02:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"37e4115e-e99a-4cd3-aef8-cc62124975cf","html_url":"https://github.com/katyo/new-tokio-smtp","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/katyo/new-tokio-smtp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/katyo%2Fnew-tokio-smtp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/katyo%2Fnew-tokio-smtp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/katyo%2Fnew-tokio-smtp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/katyo%2Fnew-tokio-smtp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/katyo","download_url":"https://codeload.github.com/katyo/new-tokio-smtp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/katyo%2Fnew-tokio-smtp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262513634,"owners_count":23322663,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T16:11:16.812Z","updated_at":"2025-06-28T23:38:44.036Z","avatar_url":"https://github.com/katyo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"new-tokio-smtp [![docs](https://docs.rs/new-tokio-smtp/badge.svg)](https://docs.rs/new-tokio-smtp) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n=====================\n\nThe new-tokio-smtp crate provides an extendible SMTP (Simple Mail Transfer Protocol)\nimplementation using tokio.\n\nThis crate provides _only_ SMTP functionality, this means it does neither\nprovides functionality for creating mails, nor for e.g. retrying sending\na mail if the receiver was temporary not available.\n\nWhile it only provides SMTP functionality it is written in a way to\nmake it easy to integrate with higher level libraries. The interoperability\nis provided through two mechanisms:\n\n1. SMTP commands are defined in a way which allow library user to\n   define there own commands, all commands provided by this library\n   could theoretically have been implemented in an external library,\n   this includes some of the more special commands like `STARTTLS`,\n   `EHLO` and `DATA`. Moreover a `Connection` can be converted into\n   a `Io` instance which provides a number of useful functionalities\n   for easily implementing new commands, e.g. `Io.parse_response`.\n\n2. syntactic construct's like e.g. `Domain` or `ClientId` can\n   be parsed but also have \"unchecked\" constructors, this allows libraries\n   which have there own validation to skip redundant validations, e.g.\n   if a mail library might provide a `Mailbox` type of mail addresses and\n   names, which is guaranteed to be syntactically correct if can implement\n   a simple `From`/`Into` impl to cheaply convert it to an `Forward-Path`.\n   (Alternative they also could implement their own `Mail` cmd if this\n   has any benefit for them)\n\n3. provided commands (and syntax constructs) are written in a robust way,\n   allowing for example extensions like `SMTPUTF8` to be implemented on it.\n   The only drawback of this is that it trusts that parts created by more\n   higher level libraries are valid, e.g. it won't validate that the mail\n   given to it is actually 7bit ascii or that it does not contain \"orphan\"\n   `'\\n'` (or `'\\r'`) chars. But this is fine as this library is for using\n   smtp to send mails, but _not_ for creating such mails. (Note that while\n   it is trusting it does validate if a command can be used through checking\n   the result from the last `EHLO` command, i.e. it wont allow you to send\n   a `STARTTLS` command on a mail server not supporting it)\n\n4. handling logic errors (i.e. server responded with code 550) separately\n   from more fatal errors like e.g. a broken pipe\n\nExample\n---------\n\n```rust\nextern crate futures;\nextern crate tokio;\nextern crate new_tokio_smtp;\n#[macro_use]\nextern crate vec1;\nextern crate rpassword;\n\nuse std::io::{stdin, stdout, Write};\n\nuse futures::stream::Stream;\nuse futures::future::lazy;\nuse new_tokio_smtp::error::GeneralError;\nuse new_tokio_smtp::{command, Connection, ConnectionConfig, Domain};\nuse new_tokio_smtp::send_mail::{\n    Mail, EncodingRequirement,\n    MailAddress, MailEnvelop,\n};\n\nstruct Request {\n    config: ConnectionConfig\u003ccommand::auth::Plain\u003e,\n    mails: Vec\u003cMailEnvelop\u003e\n}\n\nfn main() {\n    let Request { config, mails } = read_request();\n    // We only have iter map overhead because we\n    // don't have a failable mail encoding step, which normally is required.\n    let mails = mails.into_iter().map(|m| -\u003e Result\u003c_, GeneralError\u003e { Ok(m) });\n\n    println!(\"[now starting tokio]\");\n    tokio::run(lazy(move || {\n        println!(\"[start connect_send_quit]\");\n        Connection::connect_send_quit(config, mails)\n            //Stream::for_each is design wise broken in futures v0.1\n            .then(|result| Ok(result))\n            .for_each(|result| {\n                if let Err(err) = result {\n                    println!(\"[sending mail failed]: {}\", err);\n                } else {\n                    println!(\"[successfully send mail]\")\n                }\n                Ok(())\n            })\n    }))\n}\n\n\nfn read_request() -\u003e Request {\n\n    println!(\"preparing to send mail with ethereal.email\");\n    let sender = read_email();\n    let passwd = read_password();\n\n    // The `from_unchecked` will turn into a `.parse()` in the future.\n    let config = ConnectionConfig\n        ::builder(Domain::from_unchecked(\"smtp.ethereal.email\"))\n            .expect(\"resolving domain failed\")\n        .auth(command::auth::Plain::from_username(sender.clone(), passwd)\n            .expect(\"username/password can not contain \\\\0 bytes\"))\n        .build();\n\n    // the from_unchecked normally can be used if we know the address is valid\n    // a mail address parser will be added at some point in the future\n    let send_to = MailAddress::from_unchecked(\"invalid@test.test\");\n\n    // using string fmt to crate mails IS A\n    // REALLY BAD IDEA there are a ton of ways\n    // this can go wrong, so don't do this in\n    // practice, use some library to crate mails\n    let raw_mail = format!(concat!(\n        \"Date: Thu, 14 Jun 2018 11:22:18 +0000\\r\\n\",\n        \"From: You \u003c{}\u003e\\r\\n\",\n        //ethereal doesn't delivers any mail so it's fine\n        \"To: Invalid \u003c{}\u003e\\r\\n\",\n        \"Subject: I am spam?\\r\\n\",\n        \"\\r\\n\",\n        \"...\\r\\n\"\n    ), sender.as_str(), send_to.as_str());\n\n    // this normally adapts to a higher level abstraction\n    // of mail then this crate provides\n    let mail_data = Mail::new(EncodingRequirement::None, raw_mail.to_owned());\n\n    let mail = MailEnvelop::new(sender, vec1![ send_to ], mail_data);\n\n    Request {\n        config,\n        mails: vec![ mail ]\n    }\n}\n\nfn read_email() -\u003e MailAddress {\n    let stdout = stdout();\n    let mut handle = stdout.lock();\n    write!(handle, \"enter ethereal.email mail address\\n[Note mail is not validated in this example]: \")\n        .unwrap();\n    handle.flush().unwrap();\n\n    let mut line = String::new();\n    stdin().read_line(\u0026mut line).unwrap();\n    MailAddress::from_unchecked(line.trim())\n}\n\nfn read_password() -\u003e String {\n    rpassword::prompt_password_stdout(\"password: \").unwrap()\n}\n```\n\nConcept\n--------\n\nThe concept of behind the library is explained\nin the [notes/concept.md](./notes/concept.md) file.\n\nUsability Helpers\n------------------\n\nThe library provides a number of usability helpers:\n\n1. `chain::chain` provides a easy way to chain a number of\n    SMTP commands, sending each command when the previous\n    command in the chain did not fail in any way.\n\n2. `mock_support` feature:\n    Extends the Socket abstraction to not only abstract over\n    the socket being either a `TcpStream` or a `TlsStream` but\n    also adds another variant which is a boxed `MockStream`, making\n    the smtp libraries, but also libraries build on top of it more\n    testable.\n\n3. `mock::MockStream` (use the features `mock-impl`)\n    A simple implementation for a `MockStream` which allows you\n    to test which data was send to it and mock responses for it.\n    (Through it's currently limited to a fixed predefined conversation,\n    if more is needed a custom `MockStream` impl. has to be used)\n\n4. `future_ext::ResultWithContextExt`:\n    Provides a `ctx_and_then` and `ctx_or_else` methods making\n    it easier to handle results resolving _as Item_ to an tuple\n    of a context (here the connection) and a `Result` belonging\n    to an different abstraction level than the futures `Error`\n    (here a possible `CommandError` while the future `Error` is\n    an connection error like e.g. a broken pipe)\n\nLimitations / TODOs\n--------------------\n\nLike mentioned before this library has some limitations as it's\nmeant to _only_ do SMTP and nothing more. Through there are\nsome other limitations, which will be likely to be fixed\nin future versions:\n\n1. no mail address parser for `send_mail::MailAddress` and neither\n   a parser for `ForwardPath`/`ReversePath` (they can be constructed\n   using `from_unchecked`).  This will be fixed when I find a library\n   \"just\" doing mail addresses and doing it right.\n\n2. no \"build-in\" support for extended status codes, this is mainly\n   the way because I hadn't had time for this, changing this in a nice\n   build-in way might require some API changes wrt. to the\n   `Response` type and it should be done before `v1.0`\n\n3. The number of provided commands is currently limited to a\n   small but useful subset, commands which would be nice\n   to provide include `BDAT` and more variations of `AUTH`\n   (currently provided are `PLAIN` and simple `LOGIN` which\n    is enough for most cases but supporting e.g. `OAuth2` would\n    be good)\n\n4. no support for `PIPELINING`, while most extensions can be\n   implemented using custom commands, this is not true for\n   pipelining. While there exists a concept how pipelining\n   can be implemented without to much API brakeage this is\n   for now not planed due to time limitations.\n\n5. no stable version (`v1.0`) for now, as `tokio` is not stable yet.\n   When tokio becomes stable a stable version should be released,\n   through another one might have to be released at some point if\n   `PIPELINING` is implemented later one (through in the\n   current concept for implementing it there are little\n   braking changes, except for implementors of custom commands)\n\nDocumentation\n--------------\n\nDocumentation can be [viewed on docs.rs](https://docs.rs/new-tokio-smtp).\n\nLicense\n--------\n\nLicensed under either of\n\n- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\nContribution\n-------------\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any\nadditional terms or conditions.\n\nChange Log\n----------\n\n- `v0.4`:\n  - renamed `from_str_unchecked` to `from_unchecked`\n  - `Cmd.exec` accepts now `Io` instead of `Connection`\n    - replace `CmdFuture` with `ExecFuture`\n    - `Connection.send_simple_cmd` is now `Io.exec_simple_cmd`\n\n- `v0.5`:\n  - improved `ClientId`\n    - renamed `ClientIdentity` to `ClientId`\n    - added `hostname()` constructor\n  - added builder for `ConnectionConfig`\n    - removed old `with_` constructors\n  - placed all `Auth*` commands into a `auth` module\n    (e.g. `AuthPlain` =\u003e `auth::Plain`)\n  - changed feature naming schema\n\n- `v0.6`\n  - added connection builder for local non secure connections\n  - addec constructors for builders on types they build\n  - renamed `auth::plain::NullCodePoint` to `auth::plain::NullCodePointError`\n\n- `v0.7`\n  - `send_all_mails` and `connect_send_quit` now accept a `IntoIterable` instead of stream\n    - you need to have all values already ready when sending so `Stream` didn't fit well\n    - it also means you can now pass in a `Vec` or a `std::iter:once`\n  - `GeneralError` as no longer the `PreviousRequestKilledConnection` error variant instead\n    a `std::io::Error::new(std::io::ErrorKind::NoConnection, \"...\")` is returned which makes\n    it easier to adapt to by libraries using it and fit the semantics as good as previous\n    solution\n  - `send_all_mails` and `connect_send_quit` now return a stream instead of a future resolving\n    to a stream.\n\n- `v0.7.1`\n  - updated dependencies, makes sure tokio doesn't produces a deprecation warning\n    as a import moved to a different place in tokio\n\n- `v0.8.0`\n  - update `tokio-tls`/`native-tls` to v0.2.x\n  - renamed method creating a builder from `build*` to `builder*`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkatyo%2Fnew-tokio-smtp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkatyo%2Fnew-tokio-smtp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkatyo%2Fnew-tokio-smtp/lists"}