Compare commits

...

83 Commits

Author SHA1 Message Date
Alexis Mousset
73d823c8ca Merge pull request #255 from amousset/prepare-0.8.1
Prepare 0.8.1
2018-04-11 23:36:46 +02:00
Alexis Mousset
d45ee40f4a fix(all): No cache on build folders 2018-04-11 23:28:03 +02:00
Alexis Mousset
e295c5db5e Prepare 0.8.1 release 2018-04-11 22:54:29 +02:00
Alexis Mousset
a3d6722e7e Merge pull request #253 from Eijebong/skeptic
tests: Replace skeptic by some custom logic code and rustdoc calls
2018-04-11 22:09:52 +02:00
Bastien Orivel
fd56ec8877 test(lettre_email): Replace skeptic by some custom rustdoc invocations 2018-04-11 22:00:52 +02:00
Bastien Orivel
81bad13175 test(lettre): Replace skeptic by some custom rustdoc invocations
Unfortunately skeptic pulls all its dependencies in projects using
lettre (see https://github.com/budziq/rust-skeptic/issues/60).
2018-04-11 22:00:20 +02:00
Alexis Mousset
27c8e206cf Merge pull request #252 from amousset/add-changelog-sections
docs(all): Add changelog sections for style and docs
2018-04-08 15:14:45 +02:00
Alexis Mousset
b4d03ead8c docs(all): Add changelog sections for style and docs 2018-04-08 14:56:19 +02:00
Alexis Mousset
d692a9488f Merge pull request #251 from amousset/avoid-empty-format-strings
style(transport-smtp): Avoid useless empty format strings
2018-04-08 14:46:47 +02:00
Alexis Mousset
f3271715ec style(transport-smtp): Avoid useless empty format strings 2018-04-08 14:31:50 +02:00
Alexis Mousset
ee51cf7454 Merge pull request #250 from amousset/formal-changelog
Use clog to generate changelogs
2018-04-08 14:23:23 +02:00
Alexis Mousset
8981a7758c docs(all): Use clog to generate changelogs
Changelog will be unique, and generated from commit
messages using clog-cli.
Commit message should follow guidelines in CONTRIBUTING.md,
and GitCop has been (re)enabled.

Fixes #233.
2018-04-08 13:58:09 +02:00
Alexis Mousset
b91cb0770d Update contact email 2018-04-01 19:41:51 +02:00
Alexis Mousset
0cff889ace Merge pull request #247 from amousset/improve-readme
Add lettre.at link to README
2018-04-01 19:37:23 +02:00
Alexis Mousset
d00568cbd6 Add lettre.at link to README 2018-04-01 19:36:55 +02:00
Alexis Mousset
32cace1252 Merge pull request #246 from amousset/fix-base-url
Fix site base url
2018-04-01 19:29:32 +02:00
Alexis Mousset
a86cc3328e Fix site base url 2018-04-01 19:28:54 +02:00
Alexis Mousset
b51b2843f4 Create CNAME 2018-04-01 17:58:49 +02:00
Alexis Mousset
91a17ae281 Merge pull request #243 from amousset/prepare-0-8
Prepare 0.8 release
2018-03-31 21:11:13 +02:00
Alexis Mousset
bd752daf85 Prepare 0.8 release 2018-03-31 20:38:40 +02:00
Alexis Mousset
ed01efd890 Merge pull request #242 from amousset/more-lints
feat(all): Add more compiler lints
2018-03-31 20:00:12 +02:00
Alexis Mousset
088db45e41 feat(all): Add more compiler lints 2018-03-31 19:36:51 +02:00
Alexis Mousset
955a453df9 Merge pull request #241 from amousset/add-serde-impls
feat(transport): Add serde derive when possible
2018-03-31 17:49:14 +02:00
Alexis Mousset
71eda4b174 feat(transport): Add serde derive when possible 2018-03-31 17:30:36 +02:00
Alexis Mousset
d283254b1a Merge pull request #239 from amousset/move-envelope-lettre
feat(all): Move Envelope from lettre_email to lettre
2018-03-31 16:38:40 +02:00
Alexis Mousset
f3f963c6a5 feat(all): Move Envelope from lettre_email to lettre 2018-03-31 16:25:33 +02:00
Alexis Mousset
bef45c48f7 Merge pull request #237 from amousset/fix-style
style(all): rustfmt and clippy
2018-03-20 11:03:40 +01:00
Alexis Mousset
e024806402 style(all): rustfmt and clippy 2018-03-20 10:44:41 +01:00
Alexis Mousset
d7a8574464 Merge pull request #236 from amousset/prepare-0-8
Prepare changelog for 0.8
2018-03-11 15:39:54 +01:00
Alexis Mousset
9b22f5867e Prepare changelog for 0.8 2018-03-11 15:25:26 +01:00
Alexis Mousset
17abeb3957 Merge pull request #235 from amousset/html-utf8
fix(builder): Specify utf-8 charset for html
2018-03-11 10:49:50 +01:00
Alexis Mousset
e6a5c158da fix(builder): Specify utf-8 charset for html 2018-03-11 10:36:58 +01:00
Alexis Mousset
f4fc427a03 Merge pull request #234 from amousset/fix-attachment-multipart
fix(builder): Use parts for text and html methods to fix attachment i…
2018-03-11 01:19:24 +01:00
Alexis Mousset
662072e692 fix(builder): Use parts for text and html methods to fix attachment inclusion 2018-03-10 21:16:01 +01:00
Alexis Mousset
4f16d9ee69 Merge pull request #231 from amousset/clippy-redundant-field-name
style(all): Run stable rustfmt and remove redundant field names in st…
2018-03-03 07:52:27 +01:00
Alexis Mousset
9d68629bb6 style(all): Run stable rustfmt and remove redundant field names in structs 2018-03-03 00:28:45 +01:00
Alexis Mousset
96e4f845ec Merge pull request #230 from amousset/missing-bcc
fix(builder): Add bcc headers in builder
2018-03-01 07:38:12 +01:00
Alexis Mousset
4dc95281ad fix(builder): Add bcc headers in builder 2018-03-01 00:33:17 +01:00
Alexis Mousset
f3311456ad Merge pull request #228 from SpiderPigSpy/master
feat(email): Support binary file as attachment
2018-02-25 21:52:59 +01:00
Alex
98a250f015 feat(email): Support binary file as attachment
A pretty easy fix by using base64 encoding
worked pretty well in my project
934fb660b7/src/bot/email.rs

Closes issue
https://github.com/lettre/lettre/issues/224
2018-02-25 21:59:54 +03:00
Alexis Mousset
f2f2f98905 Merge pull request #227 from amousset/clippt-fix
feat(all): Apply clippy advice
2018-02-21 00:01:19 +01:00
Alexis Mousset
53e79d9620 feat(all): Apply clippy advice 2018-02-20 23:47:53 +01:00
Alexis Mousset
4f11ae61ef Merge pull request #225 from amousset/update-depends
feat(all): Update uuid to 0.6
2018-02-17 10:19:45 +01:00
Alexis Mousset
9344ff7e5c feat(all): Update uuid to 0.6 2018-02-17 09:53:58 +01:00
Alexis Mousset
36d20bc7b6 Merge pull request #223 from amousset/update-ci-conf
feat(all): Do not install OpenSSL for Windows tests
2018-01-27 16:20:20 +01:00
Alexis Mousset
620c3e96dc feat(all): Do not install OpenSSL for Windows tests 2018-01-27 16:08:05 +01:00
Alexis Mousset
7cab860cde Merge pull request #222 from amousset/prepare-doc-0-8
feat(all): Move doc to website and test it
2018-01-27 15:55:53 +01:00
Alexis Mousset
f10e4e81d0 feat(all): Move doc to website and test it 2018-01-27 15:44:18 +01:00
Alexis Mousset
5face8614b Merge pull request #220 from amousset/update-env-logger-0-5
feat(all): Update env_logger to 0.5
2018-01-18 09:02:20 +01:00
Alexis Mousset
480ed11785 feat(all): Update env_logger to 0.5 2018-01-18 00:14:14 +01:00
Alexis Mousset
a082da6ea4 Merge pull request #219 from amousset/fix-doc
fix(all): Fix documentation issues
2018-01-15 19:59:25 +01:00
Alexis Mousset
2a847c1b3b fix(all): Fix documentation issues 2018-01-15 19:50:47 +01:00
Alexis Mousset
f6c07f0720 Merge pull request #216 from amousset/update-log-04
feat(transport): Update log to 0.4
2017-12-28 21:01:51 +01:00
Alexis Mousset
bff687f55c feat(transport): Update log to 0.4 2017-12-28 20:17:22 +01:00
Alexis Mousset
66cd6fe3ac Merge pull request #215 from amousset/update-base64
feat(transport): Update base64 to 0.9
2017-12-23 08:24:59 +01:00
Alexis Mousset
bc4714a2c8 feat(transport): Update base64 to 0.9 2017-12-23 08:12:35 +01:00
Alexis Mousset
547be305c5 Merge pull request #214 from amousset/detail-type-enum
feat(transport-smtp): Make detail in response an enum
2017-12-10 19:12:07 +01:00
Alexis Mousset
ba719f7255 feat(transport-smtp): Make detail in response an enum 2017-12-10 18:56:35 +01:00
Alexis Mousset
4005fc88bc Merge pull request #212 from amousset/update-readme
feat(All): Update README and add CoC
2017-12-06 01:26:02 +01:00
Alexis Mousset
173f8aa2dd Merge branch 'master' into update-readme 2017-12-06 01:09:40 +01:00
Alexis Mousset
aa9e9dd96e feat(All): Update README and add CoC 2017-12-06 01:08:33 +01:00
Alexis Mousset
dd6601b9e5 Merge pull request #211 from lettre/add-code-of-conduct-1
Add a code of conduct
2017-12-06 01:03:51 +01:00
Alexis Mousset
78d8f9afb7 Add a code of conduct 2017-12-06 00:52:34 +01:00
Alexis Mousset
487bee0769 Merge pull request #210 from amousset/transport-public-methods
feat(transport-smtp): get_ehlo and reset in SmtpTransport should not …
2017-12-05 23:56:01 +01:00
Alexis Mousset
ab35bac204 feat(transport-smtp): get_ehlo and reset in SmtpTransport should not be public 2017-12-05 23:41:57 +01:00
Alexis Mousset
30ea70edab Merge pull request #209 from amousset/cleanup
Change rustfmt style
2017-11-25 12:02:09 +01:00
Alexis Mousset
eb4e7f9829 Change rustfmt style 2017-11-25 11:55:05 +01:00
Alexis Mousset
104935b443 Merge pull request #208 from amousset/use-hostname-clientid
feat(transport-smtp): Use hostname as clientid when available
2017-11-19 22:33:17 +01:00
Alexis Mousset
1936211f8e feat(transport-smtp): Use hostname as clientid when available 2017-11-19 22:21:13 +01:00
Alexis Mousset
87d0dbdf70 Merge pull request #207 from amousset/code-cleanup
feat(all): Add html_root_url
2017-11-19 14:59:38 +01:00
Alexis Mousset
7bc28caf27 feat(all): Add html_root_url 2017-11-19 14:54:04 +01:00
Alexis Mousset
7498bed378 Merge pull request #205 from amousset/update-openssl-windows-tests
fix(all): Update openssl for windows tests
2017-11-19 11:08:39 +01:00
Alexis Mousset
d2475ae1aa fix(all): Update openssl for windows tests 2017-11-19 11:00:25 +01:00
Alexis Mousset
12174676d3 Merge pull request #204 from amousset/improve-response-parsing
feat(transport-smtp): Add tests for response parsing and clean from_str
2017-11-19 01:44:26 +01:00
Alexis Mousset
1850d56ec1 feat(transport-smtp): Add tests for response parsing and clean from_str 2017-11-19 01:36:34 +01:00
Alexis Mousset
92134e22a4 Merge pull request #203 from amousset/response-parsing-with-nom
feat(transport): Use nom for parsing smtp responses
2017-11-19 01:11:12 +01:00
Alexis Mousset
01fde07a48 feat(transport): Use nom for parsing smtp responses 2017-11-19 00:42:07 +01:00
Alexis Mousset
16223ee9c3 Merge pull request #202 from amousset/update-dependencies
Update dependencies and improve style
2017-11-18 19:45:30 +01:00
Alexis Mousset
b010126c19 Update dependencies and improve style 2017-11-18 19:39:57 +01:00
Alexis Mousset
166178b011 Merge pull request #197 from jacobbudin/website-hugo-theme
fix(doc): Update Hugo theme
2017-10-16 23:02:35 +02:00
Jacob Budin
aecbce50e3 fix(doc): Update Hugo theme
Updated Hugo Learn Theme and regenerated web docs.
2017-10-15 18:17:26 -04:00
Alexis Mousset
cc324b4705 Merge pull request #196 from amousset/master
Add a version to lettre dependency in lettre_email
2017-10-08 17:59:02 +02:00
Alexis Mousset
2785f14f31 Add a version to lettre dependency in lettre_email 2017-10-08 17:41:01 +02:00
249 changed files with 22605 additions and 7370 deletions

View File

@@ -1,12 +1,7 @@
environment: environment:
matrix: matrix:
- TARGET: x86_64-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc
BITS: 64
OPENSSL_VERSION: 1_1_0f
OPENSSL_DIR: C:\OpenSSL
install: install:
- ps: Start-FileDownload "http://slproweb.com/download/Win${env:BITS}OpenSSL-${env:OPENSSL_VERSION}.exe"
- Win%BITS%OpenSSL-%OPENSSL_VERSION%.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL"
- curl -sSf -o rustup-init.exe https://win.rustup.rs/ - curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% - rustup-init.exe -y --default-host %TARGET%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin

7
.clog.toml Normal file
View File

@@ -0,0 +1,7 @@
[clog]
repository = "https://github.com/lettre/lettre"
changelog = "CHANGELOG.md"
[sections]
Style = ["style"]
Documentation = ["docs"]

4
.gitignore vendored
View File

@@ -1,4 +1,6 @@
.vscode/ .vscode/
.project .project/
.idea/
lettre.iml
target/ target/
/Cargo.lock /Cargo.lock

View File

@@ -12,13 +12,6 @@ matrix:
sudo: required sudo: required
cache:
directories:
- target/debug/deps
- target/debug/build
- target/release/deps
- target/release/build
addons: addons:
apt: apt:
packages: packages:
@@ -29,10 +22,5 @@ before_script:
- sudo chgrp -R postdrop /var/spool/postfix/maildrop - sudo chgrp -R postdrop /var/spool/postfix/maildrop
script: script:
- cargo test --verbose --manifest-path lettre/Cargo.toml --no-default-features - cargo test --verbose --all
- cargo test --verbose --manifest-path lettre/Cargo.toml
- cargo test --verbose --manifest-path lettre_email/Cargo.toml
env:
global:
secure: "MaZ3TzuaAHuxmxQkfJdqRfkh7/ieScJRk0T/2yjysZhDMTYyRmp5wh/zkfW1ADuG0uc4Pqsxrsh1J9SVO7O0U5NJA8NKZi/pgiL+FHh0g4YtlHxy2xmFNB5am3Kyc+E7B4XylwTbA9S8ublVM0nvX7yX/a5fbwEUInVk2bA8fpc="

97
CHANGELOG.md Normal file
View File

@@ -0,0 +1,97 @@
<a name="v0.8.1"></a>
### v0.8.1 (2018-04-11)
#### Fix
* **all:**
* Replace skeptic by some custom rustdoc invocations ([81bad131](https://github.com/lettre/lettre/commit/81bad1317519d330c46ea02f2b7a266b97cc00dd))
#### Documentation
* **all:**
* Add changelog sections for style and docs ([b4d03ead](https://github.com/lettre/lettre/commit/b4d03ead8cce04e0c3d65a30e7a07acca9530f30))
* Use clog to generate changelogs ([8981a775](https://github.com/lettre/lettre/commit/8981a7758c89be69974ef204c4390744aea94e4f), closes [#233](https://github.com/lettre/lettre/issues/233))
#### Style
* **transport-smtp:** Avoid useless empty format strings ([f3271715](https://github.com/lettre/lettre/commit/f3271715ecaf2793c9064462184867e4f22b0ead))
<a name="v0.8.0"></a>
### v0.8.0 (2018-03-31)
#### Added
* Support binary files as attachment
* Move doc to a dedicated website
* Add tests for the doc using skeptic
* Added a code of conduct
* Use hostname as `ClientId` when available
#### Changed
* Detail in SMTP Response is now an enum
* Use nom for parsing smtp responses
* `Envelope` was moved from `lettre_email` to `lettre`
* `EmailAddress::new()` now returns a `Result`
* `SendableEmail` replaces `from` and `to` by `envelope` that returns an `Envelope`
* `File` transport storage format has changed
#### Fixed
* Add missing "Bcc" headers when building the email
* Specify utf-8 charset for html
* Use parts for text and html methods to work with attachments
#### Removed
* `get_ehlo` and `reset` in SmtpTransport are now private
<a name="v0.7.0"></a>
### v0.7.0 (2017-10-08)
#### Added
* Allow validating server certificate
* Initial (incomplete) attachments support
#### Changed
* Split into the *lettre* and *lettre_email* crates
* A lot of small improvements
* Use *tls-native* instead of *openssl* in smtp transport
<a name="v0.6.2"></a>
### v0.6.2 (2017-02-18)
#### Changed
* Update env-logger crate to 0.4
* Update openssl crate to 0.9
* Update uuid crate to 0.4
<a name="v0.6.1"></a>
### v0.6.1 (2016-10-19)
#### Changes
* **documentation**
* #91: Build seperate docs for each release
* #96: Add complete documentation information to README
#### Fixed
* #85: Use address-list for "To", "From" etc.
* #93: Force building tests before coverage computing
<a name="v0.6.0"></a>
### v0.6.0 (2016-05-05)
#### Changes
* multipart support
* add non-consuming methods for Email builders
* `add_header` does not return the builder anymore,
for consistency with other methods. Use the `header`
method instead

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@lettre.at. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -22,10 +22,7 @@ Any line of the commit message cannot be longer 72 characters.
fix: A bug fix fix: A bug fix
docs: Documentation only changes docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug or adds a feature
perf: A code change that improves performance perf: A code change that improves performance
test: Adding missing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
**scope** is the lettre part that is being touched. Examples: **scope** is the lettre part that is being touched. Examples:

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014 Alexis Mousset Copyright (c) 2014-2018 Alexis Mousset
Permission is hereby granted, free of charge, to any Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated person obtaining a copy of this software and associated

View File

@@ -1,4 +1,5 @@
# lettre # lettre
[![Build Status](https://travis-ci.org/lettre/lettre.svg?branch=master)](https://travis-ci.org/lettre/lettre) [![Build Status](https://travis-ci.org/lettre/lettre.svg?branch=master)](https://travis-ci.org/lettre/lettre)
[![Build status](https://ci.appveyor.com/api/projects/status/mpwglemugjtkps2d/branch/master?svg=true)](https://ci.appveyor.com/project/amousset/lettre/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/mpwglemugjtkps2d/branch/master?svg=true)](https://ci.appveyor.com/project/amousset/lettre/branch/master)
[![Crate](https://img.shields.io/crates/v/lettre.svg)](https://crates.io/crates/lettre) [![Crate](https://img.shields.io/crates/v/lettre.svg)](https://crates.io/crates/lettre)
@@ -6,7 +7,7 @@
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Gitter](https://badges.gitter.im/lettre/lettre.svg)](https://gitter.im/lettre/lettre?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/lettre/lettre.svg)](https://gitter.im/lettre/lettre?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
This is an email library written in Rust. An email library written in Rust.
## Features ## Features
@@ -17,16 +18,49 @@ Lettre provides the following features:
* Secure delivery with SMTP using encryption and authentication * Secure delivery with SMTP using encryption and authentication
* Easy email builders * Easy email builders
## Example
```rust,no_run
extern crate lettre;
extern crate lettre_email;
extern crate mime;
use lettre::{EmailTransport, SmtpTransport};
use lettre_email::EmailBuilder;
use std::path::Path;
fn main() {
let email = EmailBuilder::new()
// Addresses can be specified by the tuple (email, alias)
.to(("user@example.org", "Firstname Lastname"))
// ... or by an address only
.from("user@example.com")
.subject("Hi, Hello world")
.text("Hello world.")
.attachment(Path::new("Cargo.toml"), None, &mime::TEXT_PLAIN).unwrap()
.build()
.unwrap();
// Open a local connection on port 25
let mut mailer = SmtpTransport::builder_unencrypted_localhost().unwrap()
.build();
// Send the email
let result = mailer.send(&email);
if result.is_ok() {
println!("Email sent");
} else {
println!("Could not send email: {:?}", result);
}
assert!(result.is_ok());
}
```
## Documentation ## Documentation
Released versions: * [User documentation](http://docs.lettre.at/)
* [API reference](https://docs.rs/lettre/)
* [latest](https://docs.rs/lettre/)
* [v0.7.0](https://docs.rs/lettre/0.7.0/lettre/)
* [v0.6.2](https://docs.rs/lettre/0.6.2/lettre/)
* [v0.6.1](https://docs.rs/lettre/0.6.1/lettre/)
* [v0.6.0](https://docs.rs/lettre/0.6.0/lettre/)
* [v0.5.1](https://docs.rs/lettre/0.5.1/lettre/)
## Install ## Install
@@ -35,12 +69,18 @@ To use this library, add the following to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]
lettre = "0.7" lettre = "0.8"
lettre_email = "0.8"
``` ```
## Testing ## Testing
The tests require an open mail server listening locally on port 2525 and the `sendmail` command. The `lettre` tests require an open mail server listening locally on port 2525 and the `sendmail` command.
## Code of conduct
Anyone who interacts with Lettre in any space, including but not limited to
this GitHub repository, must follow our [code of conduct](https://github.com/lettre/lettre/blob/master/CODE_OF_CONDUCT.md).
## License ## License

View File

@@ -4,18 +4,20 @@
<head> <head>
<meta charset="utf-8"> <meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta charset="utf-8"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>404 Page not found</title> <title>404 Page not found</title>
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/horsey.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<style type="text/css"> <style type="text/css">
:root #header + #content > #left > #rlblock_left { :root #header + #content > #left > #rlblock_left {
display: none !important; display: none !important;
@@ -31,6 +33,7 @@
list-style-type: none; list-style-type: none;
} }
</style> </style>
</head> </head>
<body> <body>
@@ -44,10 +47,10 @@
<h1>Error</h1> <h1>Error</h1>
<p> <p>
</p> </p>
<p>Woops. Looks like this page doesn't exist.</p> <p>Woops. Looks like this page doesn&#39;t exist ¯\_(ツ)_/¯.</p>
<p></p> <p></p>
<p><a href="https://lettre.github.io/lettre/">Go to homepage</a></p> <p><a href="http://docs.lettre.at/">Go to homepage</a></p>
<p><img src="https://lettre.github.io/lettre//images/gopher-404.jpg" style="width:50%"></img></p> <p><img src="http://docs.lettre.at//images/gopher-404.jpg" style="width:50%"></img></p>
</div> </div>
</div> </div>

1
docs/CNAME Normal file
View File

@@ -0,0 +1 @@
docs.lettre.at

593
docs/categories/index.html Normal file
View File

@@ -0,0 +1,593 @@
<!DOCTYPE html>
<html lang="en" class="js csstransforms3d">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Categories :: Lettre site</title>
<link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head>
<body class="" data-url="/categories/">
<nav id="sidebar" class="">
<div id="header-wrapper">
<div id="header">
<a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div>
<div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span>
</div>
<script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript">
var baseurl = "http:\/\/docs.lettre.at\/";
</script>
<script type="text/javascript" src="/js/search.js?1522603726"></script>
</div>
<div class="highlightable">
<ul class="topics">
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
Getting started
</a>
<ul>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a>
</li>
</ul>
</li>
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<a href="/creating-messages/">
Creating messages
</a>
<ul>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a>
</li>
</ul>
</li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul>
</li>
</ul>
<section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
</section>
</div>
</nav>
<section id="body">
<div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="sticky-spacer">
<div id="top-bar">
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle="">
<i class="fa fa-bars"></i>
</a>
</span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
Categories
</span>
</div>
<div class="progress">
<div class="wrapper">
</div>
</div>
</div>
</div>
<div id="body-inner">
<h1>Categories</h1>
<footer class=" footline" >
</footer>
</div>
</div>
<div id="navigation">
<a class="nav nav-next" href="/getting-started/" title="Getting started" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div>
</section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div>
<script src="/js/clipboard.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="/js/featherlight.min.js?1522603726"></script>
<script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="/js/learn.js?1522603726"></script>
<script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body>
</html>

View File

@@ -2,12 +2,12 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Categories on Lettre site</title> <title>Categories on Lettre site</title>
<link>https://lettre.github.io/lettre/categories/</link> <link>http://docs.lettre.at/categories/</link>
<description>Recent content in Categories on Lettre site</description> <description>Recent content in Categories on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<atom:link href="https://lettre.github.io/lettre/categories/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/categories/index.xml" rel="self" type="application/rss+xml" />
</channel> </channel>

View File

@@ -0,0 +1,731 @@
<!DOCTYPE html>
<html lang="en" class="js csstransforms3d">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Email creation :: Lettre site</title>
<link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head>
<body class="" data-url="/creating-messages/email/">
<nav id="sidebar" class="">
<div id="header-wrapper">
<div id="header">
<a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div>
<div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span>
</div>
<script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript">
var baseurl = "http:\/\/docs.lettre.at\/";
</script>
<script type="text/javascript" src="/js/search.js?1522603726"></script>
</div>
<div class="highlightable">
<ul class="topics">
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
Getting started
</a>
<ul>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a>
</li>
</ul>
</li>
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
parent
">
<a href="/creating-messages/">
Creating messages
</a>
<ul>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item active">
<a href="/creating-messages/email/">
Email creation
</a>
</li>
</ul>
</li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item active">
<a href="/creating-messages/email/">
Email creation
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul>
</li>
</ul>
<section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
</section>
</div>
</nav>
<section id="body">
<div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="sticky-spacer">
<div id="top-bar">
<div id="top-github-link">
<a class="github-link" href="https://github.com/lettre/lettre/edit/master/website/content/creating-messages/email.md" target="blank">
<i class="fa fa-code-fork"></i>
Edit this page
</a>
</div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle="">
<i class="fa fa-bars"></i>
</a>
</span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
<a href='/'>Lettre site</a> > <a href='/creating-messages/'>Creating messages</a> > Email creation
</span>
</div>
<div class="progress">
<div class="wrapper">
<nav id="TableOfContents">
<ul>
<li>
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#simple-example">Simple example</a></li>
<li><a href="#complete-example">Complete example</a></li>
</ul></li>
</ul></li>
</ul></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<div id="body-inner">
<h1>Email creation</h1>
<h4 id="simple-example">Simple example</h4>
<p>The <code>email</code> part builds email messages. For now, it does not support attachments.
An email is built using an <code>EmailBuilder</code>. The simplest email could be:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">crate</span> lettre_email;
<span style="color:#66d9ef">use</span> lettre_email::EmailBuilder;
<span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">main</span>() {
<span style="color:#75715e">// Create an email
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">let</span> email <span style="color:#f92672">=</span> EmailBuilder::new()
<span style="color:#75715e">// Addresses can be specified by the tuple (email, alias)
</span><span style="color:#75715e"></span> .to((<span style="color:#e6db74">&#34;user@example.org&#34;</span>, <span style="color:#e6db74">&#34;Firstname Lastname&#34;</span>))
<span style="color:#75715e">// ... or by an address only
</span><span style="color:#75715e"></span> .from(<span style="color:#e6db74">&#34;user@example.com&#34;</span>)
.subject(<span style="color:#e6db74">&#34;Hi, Hello world&#34;</span>)
.text(<span style="color:#e6db74">&#34;Hello world.&#34;</span>)
.build();
assert<span style="color:#f92672">!</span>(email.is_ok());
}</code></pre></div>
<p>When the <code>build</code> method is called, the <code>EmailBuilder</code> will add the missing headers (like
<code>Message-ID</code> or <code>Date</code>) and check for missing necessary ones (like <code>From</code> or <code>To</code>). It will
then generate an <code>Email</code> that can be sent.</p>
<p>The <code>text()</code> method will create a plain text email, while the <code>html()</code> method will create an
HTML email. You can use the <code>alternative()</code> method to provide both versions, using plain text
as fallback for the HTML version.</p>
<h4 id="complete-example">Complete example</h4>
<p>Below is a more complete example, not using method chaining:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">crate</span> lettre_email;
<span style="color:#66d9ef">use</span> lettre_email::EmailBuilder;
<span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">main</span>() {
<span style="color:#66d9ef">let</span> <span style="color:#66d9ef">mut</span> builder <span style="color:#f92672">=</span> EmailBuilder::new();
builder.add_to((<span style="color:#e6db74">&#34;user@example.org&#34;</span>, <span style="color:#e6db74">&#34;Alias name&#34;</span>));
builder.add_cc((<span style="color:#e6db74">&#34;user@example.net&#34;</span>, <span style="color:#e6db74">&#34;Alias name&#34;</span>));
builder.add_from(<span style="color:#e6db74">&#34;no-reply@example.com&#34;</span>);
builder.add_from(<span style="color:#e6db74">&#34;no-reply@example.eu&#34;</span>);
builder.set_sender(<span style="color:#e6db74">&#34;no-reply@example.com&#34;</span>);
builder.set_subject(<span style="color:#e6db74">&#34;Hello world&#34;</span>);
builder.set_alternative(<span style="color:#e6db74">&#34;&lt;h2&gt;Hi, Hello world.&lt;/h2&gt;&#34;</span>, <span style="color:#e6db74">&#34;Hi, Hello world.&#34;</span>);
builder.add_reply_to(<span style="color:#e6db74">&#34;contact@example.com&#34;</span>);
builder.add_header((<span style="color:#e6db74">&#34;X-Custom-Header&#34;</span>, <span style="color:#e6db74">&#34;my header&#34;</span>));
<span style="color:#66d9ef">let</span> email <span style="color:#f92672">=</span> builder.build();
assert<span style="color:#f92672">!</span>(email.is_ok());
}</code></pre></div>
<p>See the <code>EmailBuilder</code> documentation for a complete list of methods.</p>
<footer class=" footline" >
</footer>
</div>
</div>
<div id="navigation">
<a class="nav nav-prev" href="/creating-messages/" title="Creating messages"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/" title="Sending messages" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div>
</section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div>
<script src="/js/clipboard.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="/js/featherlight.min.js?1522603726"></script>
<script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="/js/learn.js?1522603726"></script>
<script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body>
</html>

View File

@@ -1,56 +1,66 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="js csstransforms3d"> <html lang="en" class="js csstransforms3d">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Creating messages :: Lettre site</title>
<title>Creating messages</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/creating-messages/"> <body class="" data-url="/creating-messages/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -62,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -126,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
parent
active
">
<a href="/creating-messages/">
<li class="dd-item active parent" data-nav-id="/lettre/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -214,13 +309,29 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
<div id="top-github-link">
<a class="github-link" href="https://github.com/lettre/lettre/edit/master/website/content/creating-messages/_index.md" target="blank">
<i class="fa fa-code-fork"></i>
Edit this page
</a>
</div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -228,6 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -235,72 +349,58 @@
<a href='/'>Lettre site</a> > Creating messages
<span itemprop="title"> Creating messages</span>
</span>
</div>
<div class="progress">
<div class="wrapper">
<nav id="TableOfContents">
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#creating-messages">Creating messages</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
</div>
</div> </div>
</div> </div>
</div>
<div id="chapter">
<div id="body-inner"> <div id="body-inner">
<h1>Creating messages</h1>
<h3 id="creating-messages">Creating messages</h3> <h3 id="creating-messages">Creating messages</h3>
<p>This section explains how to create emails.</p>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
</div>
<div id="navigation"> <div id="navigation">
@@ -313,12 +413,6 @@
<a class="nav nav-prev" href="/lettre/getting-started/intro/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -338,26 +432,200 @@
<a class="nav nav-prev" href="/getting-started/intro/" title="Introduction"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/creating-messages/email/" title="Email creation" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>
</html> </html>

View File

@@ -2,14 +2,24 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Creating messages on Lettre site</title> <title>Creating messages on Lettre site</title>
<link>https://lettre.github.io/lettre/creating-messages/</link> <link>http://docs.lettre.at/creating-messages/</link>
<description>Recent content in Creating messages on Lettre site</description> <description>Recent content in Creating messages on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate> <lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate>
<atom:link href="https://lettre.github.io/lettre/creating-messages/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/creating-messages/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Email creation</title>
<link>http://docs.lettre.at/creating-messages/email/</link>
<pubDate>Sun, 21 Jan 2018 23:46:17 +0200</pubDate>
<guid>http://docs.lettre.at/creating-messages/email/</guid>
<description>Simple example The email part builds email messages. For now, it does not support attachments. An email is built using an EmailBuilder. The simplest email could be:
extern crate lettre_email; use lettre_email::EmailBuilder; fn main() { // Create an email let email = EmailBuilder::new() // Addresses can be specified by the tuple (email, alias) .to((&amp;#34;user@example.org&amp;#34;, &amp;#34;Firstname Lastname&amp;#34;)) // ... or by an address only .from(&amp;#34;user@example.com&amp;#34;) .subject(&amp;#34;Hi, Hello world&amp;#34;) .</description>
</item>
</channel> </channel>
</rss> </rss>

View File

@@ -0,0 +1,47 @@
.autocomplete-suggestions {
text-align: left;
cursor: default;
border: 1px solid #ccc;
border-top: 0;
background: #fff;
box-shadow: -1px 1px 3px rgba(0,0,0,.1);
/* core styles should not be changed */
position: absolute;
display: none;
z-index: 9999;
max-height: 254px;
overflow: hidden;
overflow-y: auto;
box-sizing: border-box;
}
.autocomplete-suggestion {
position: relative;
cursor: pointer;
padding: 7px;
line-height: 23px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #333;
}
.autocomplete-suggestion b {
font-weight: normal;
color: #1f8dd6;
}
.autocomplete-suggestion.selected {
background: #333;
color: #fff;
}
.autocomplete-suggestion:hover {
background: #444;
color: #fff;
}
.autocomplete-suggestion > .context {
font-size: 12px;
}

File diff suppressed because one or more lines are too long

View File

@@ -172,7 +172,6 @@ h2 {
font-size: 2.5rem; font-size: 2.5rem;
line-height: 110% !important; line-height: 110% !important;
margin: 2.5rem 0 1.5rem 0; margin: 2.5rem 0 1.5rem 0;
text-transform: capitalize;
} }
h3 { h3 {
@@ -210,3 +209,44 @@ figcaption h4 {
text-align: center; text-align: center;
margin-top: -1.5em; margin-top: -1.5em;
} }
.select-style {
border: 0;
width: 150px;
border-radius: 0px;
overflow: hidden;
display: inline-flex;
}
.select-style svg {
fill: #ccc;
width: 14px;
height: 14px;
pointer-events: none;
margin: auto;
}
.select-style svg:hover {
fill: #e6e6e6;
}
.select-style select {
padding: 0;
width: 130%;
border: none;
box-shadow: none;
background: transparent;
background-image: none;
-webkit-appearance: none;
margin: auto;
margin-left: 0px;
margin-right: -20px;
}
.select-style select:focus {
outline: none;
}
.select-style :hover {
cursor: pointer;
}

104
docs/css/theme-blue.css Normal file
View File

@@ -0,0 +1,104 @@
:root{
--MAIN-TEXT-color:#323232; /* Color of text by default */
--MAIN-TITLES-TEXT-color: #5e5e5e; /* Color of titles h2-h3-h4-h5 */
--MAIN-LINK-color:#1C90F3; /* Color of links */
--MAIN-LINK-HOVER-color:#167ad0; /* Color of hovered links */
--MAIN-ANCHOR-color: #1C90F3; /* color of anchors on titles */
--MENU-HEADER-BG-color:#1C90F3; /* Background color of menu header */
--MENU-HEADER-BORDER-color:#33a1ff; /*Color of menu header border */
--MENU-SEARCH-BG-color:#167ad0; /* Search field background color (by default borders + icons) */
--MENU-SEARCH-BOX-color: #33a1ff; /* Override search field border color */
--MENU-SEARCH-BOX-ICONS-color: #a1d2fd; /* Override search field icons color */
--MENU-SECTIONS-ACTIVE-BG-color:#20272b; /* Background color of the active section and its childs */
--MENU-SECTIONS-BG-color:#252c31; /* Background color of other sections */
--MENU-SECTIONS-LINK-color: #ccc; /* Color of links in menu */
--MENU-SECTIONS-LINK-HOVER-color: #e6e6e6; /* Color of links in menu, when hovered */
--MENU-SECTION-ACTIVE-CATEGORY-color: #777; /* Color of active category text */
--MENU-SECTION-ACTIVE-CATEGORY-BG-color: #fff; /* Color of background for the active category (only) */
--MENU-VISITED-color: #33a1ff; /* Color of 'page visited' icons in menu */
--MENU-SECTION-HR-color: #20272b; /* Color of <hr> separator in menu */
}
body {
color: var(--MAIN-TEXT-color) !important;
}
textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="url"]:focus, input[type="color"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, select[multiple=multiple]:focus {
border-color: none;
box-shadow: none;
}
h2, h3, h4, h5 {
color: var(--MAIN-TITLES-TEXT-color) !important;
}
a {
color: var(--MAIN-LINK-color);
}
.anchor {
color: var(--MAIN-ANCHOR-color);
}
a:hover {
color: var(--MAIN-LINK-HOVER-color);
}
#sidebar ul li.visited > a .read-icon {
color: var(--MENU-VISITED-color);
}
#body a.highlight:after {
display: block;
content: "";
height: 1px;
width: 0%;
-webkit-transition: width 0.5s ease;
-moz-transition: width 0.5s ease;
-ms-transition: width 0.5s ease;
transition: width 0.5s ease;
background-color: var(--MAIN-HOVER-color);
}
#sidebar {
background-color: var(--MENU-SECTIONS-BG-color);
}
#sidebar #header-wrapper {
background: var(--MENU-HEADER-BG-color);
color: var(--MENU-SEARCH-BOX-color);
border-color: var(--MENU-HEADER-BORDER-color);
}
#sidebar .searchbox {
border-color: var(--MENU-SEARCH-BOX-color);
background: var(--MENU-SEARCH-BG-color);
}
#sidebar ul.topics > li.parent, #sidebar ul.topics > li.active {
background: var(--MENU-SECTIONS-ACTIVE-BG-color);
}
#sidebar .searchbox * {
color: var(--MENU-SEARCH-BOX-ICONS-color);
}
#sidebar a {
color: var(--MENU-SECTIONS-LINK-color);
}
#sidebar a:hover {
color: var(--MENU-SECTIONS-LINK-HOVER-color);
}
#sidebar ul li.active > a {
background: var(--MENU-SECTION-ACTIVE-CATEGORY-BG-color);
color: var(--MENU-SECTION-ACTIVE-CATEGORY-color) !important;
}
#sidebar hr {
border-color: var(--MENU-SECTION-HR-color);
}

104
docs/css/theme-green.css Normal file
View File

@@ -0,0 +1,104 @@
:root{
--MAIN-TEXT-color:#323232; /* Color of text by default */
--MAIN-TITLES-TEXT-color: #5e5e5e; /* Color of titles h2-h3-h4-h5 */
--MAIN-LINK-color:#599a3e; /* Color of links */
--MAIN-LINK-HOVER-color:#3f6d2c; /* Color of hovered links */
--MAIN-ANCHOR-color: #599a3e; /* color of anchors on titles */
--MENU-HEADER-BG-color:#74b559; /* Background color of menu header */
--MENU-HEADER-BORDER-color:#9cd484; /*Color of menu header border */
--MENU-SEARCH-BG-color:#599a3e; /* Search field background color (by default borders + icons) */
--MENU-SEARCH-BOX-color: #84c767; /* Override search field border color */
--MENU-SEARCH-BOX-ICONS-color: #c7f7c4; /* Override search field icons color */
--MENU-SECTIONS-ACTIVE-BG-color:#1b211c; /* Background color of the active section and its childs */
--MENU-SECTIONS-BG-color:#222723; /* Background color of other sections */
--MENU-SECTIONS-LINK-color: #ccc; /* Color of links in menu */
--MENU-SECTIONS-LINK-HOVER-color: #e6e6e6; /* Color of links in menu, when hovered */
--MENU-SECTION-ACTIVE-CATEGORY-color: #777; /* Color of active category text */
--MENU-SECTION-ACTIVE-CATEGORY-BG-color: #fff; /* Color of background for the active category (only) */
--MENU-VISITED-color: #599a3e; /* Color of 'page visited' icons in menu */
--MENU-SECTION-HR-color: #18211c; /* Color of <hr> separator in menu */
}
body {
color: var(--MAIN-TEXT-color) !important;
}
textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="url"]:focus, input[type="color"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, select[multiple=multiple]:focus {
border-color: none;
box-shadow: none;
}
h2, h3, h4, h5 {
color: var(--MAIN-TITLES-TEXT-color) !important;
}
a {
color: var(--MAIN-LINK-color);
}
.anchor {
color: var(--MAIN-ANCHOR-color);
}
a:hover {
color: var(--MAIN-LINK-HOVER-color);
}
#sidebar ul li.visited > a .read-icon {
color: var(--MENU-VISITED-color);
}
#body a.highlight:after {
display: block;
content: "";
height: 1px;
width: 0%;
-webkit-transition: width 0.5s ease;
-moz-transition: width 0.5s ease;
-ms-transition: width 0.5s ease;
transition: width 0.5s ease;
background-color: var(--MAIN-HOVER-color);
}
#sidebar {
background-color: var(--MENU-SECTIONS-BG-color);
}
#sidebar #header-wrapper {
background: var(--MENU-HEADER-BG-color);
color: var(--MENU-SEARCH-BOX-color);
border-color: var(--MENU-HEADER-BORDER-color);
}
#sidebar .searchbox {
border-color: var(--MENU-SEARCH-BOX-color);
background: var(--MENU-SEARCH-BG-color);
}
#sidebar ul.topics > li.parent, #sidebar ul.topics > li.active {
background: var(--MENU-SECTIONS-ACTIVE-BG-color);
}
#sidebar .searchbox * {
color: var(--MENU-SEARCH-BOX-ICONS-color);
}
#sidebar a {
color: var(--MENU-SECTIONS-LINK-color);
}
#sidebar a:hover {
color: var(--MENU-SECTIONS-LINK-HOVER-color);
}
#sidebar ul li.active > a {
background: var(--MENU-SECTION-ACTIVE-CATEGORY-BG-color);
color: var(--MENU-SECTION-ACTIVE-CATEGORY-color) !important;
}
#sidebar hr {
border-color: var(--MENU-SECTION-HR-color);
}

104
docs/css/theme-red.css Normal file
View File

@@ -0,0 +1,104 @@
:root{
--MAIN-TEXT-color:#323232; /* Color of text by default */
--MAIN-TITLES-TEXT-color: #5e5e5e; /* Color of titles h2-h3-h4-h5 */
--MAIN-LINK-color:#f31c1c; /* Color of links */
--MAIN-LINK-HOVER-color:#d01616; /* Color of hovered links */
--MAIN-ANCHOR-color: #f31c1c; /* color of anchors on titles */
--MENU-HEADER-BG-color:#dc1010; /* Background color of menu header */
--MENU-HEADER-BORDER-color:#e23131; /*Color of menu header border */
--MENU-SEARCH-BG-color:#b90000; /* Search field background color (by default borders + icons) */
--MENU-SEARCH-BOX-color: #ef2020; /* Override search field border color */
--MENU-SEARCH-BOX-ICONS-color: #fda1a1; /* Override search field icons color */
--MENU-SECTIONS-ACTIVE-BG-color:#2b2020; /* Background color of the active section and its childs */
--MENU-SECTIONS-BG-color:#312525; /* Background color of other sections */
--MENU-SECTIONS-LINK-color: #ccc; /* Color of links in menu */
--MENU-SECTIONS-LINK-HOVER-color: #e6e6e6; /* Color of links in menu, when hovered */
--MENU-SECTION-ACTIVE-CATEGORY-color: #777; /* Color of active category text */
--MENU-SECTION-ACTIVE-CATEGORY-BG-color: #fff; /* Color of background for the active category (only) */
--MENU-VISITED-color: #ff3333; /* Color of 'page visited' icons in menu */
--MENU-SECTION-HR-color: #2b2020; /* Color of <hr> separator in menu */
}
body {
color: var(--MAIN-TEXT-color) !important;
}
textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="url"]:focus, input[type="color"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, select[multiple=multiple]:focus {
border-color: none;
box-shadow: none;
}
h2, h3, h4, h5 {
color: var(--MAIN-TITLES-TEXT-color) !important;
}
a {
color: var(--MAIN-LINK-color);
}
.anchor {
color: var(--MAIN-ANCHOR-color);
}
a:hover {
color: var(--MAIN-LINK-HOVER-color);
}
#sidebar ul li.visited > a .read-icon {
color: var(--MENU-VISITED-color);
}
#body a.highlight:after {
display: block;
content: "";
height: 1px;
width: 0%;
-webkit-transition: width 0.5s ease;
-moz-transition: width 0.5s ease;
-ms-transition: width 0.5s ease;
transition: width 0.5s ease;
background-color: var(--MAIN-HOVER-color);
}
#sidebar {
background-color: var(--MENU-SECTIONS-BG-color);
}
#sidebar #header-wrapper {
background: var(--MENU-HEADER-BG-color);
color: var(--MENU-SEARCH-BOX-color);
border-color: var(--MENU-HEADER-BORDER-color);
}
#sidebar .searchbox {
border-color: var(--MENU-SEARCH-BOX-color);
background: var(--MENU-SEARCH-BG-color);
}
#sidebar ul.topics > li.parent, #sidebar ul.topics > li.active {
background: var(--MENU-SECTIONS-ACTIVE-BG-color);
}
#sidebar .searchbox * {
color: var(--MENU-SEARCH-BOX-ICONS-color);
}
#sidebar a {
color: var(--MENU-SECTIONS-LINK-color);
}
#sidebar a:hover {
color: var(--MENU-SECTIONS-LINK-HOVER-color);
}
#sidebar ul li.active > a {
background: var(--MENU-SECTION-ACTIVE-CATEGORY-BG-color);
color: var(--MENU-SECTION-ACTIVE-CATEGORY-color) !important;
}
#sidebar hr {
border-color: var(--MENU-SECTION-HR-color);
}

View File

@@ -59,6 +59,7 @@ a:hover {
} }
pre { pre {
position: relative; position: relative;
color: #ffffff;
} }
.bg { .bg {
background: #fff; background: #fff;
@@ -529,6 +530,94 @@ div.notices.tip p {
div.notices.tip p:first-child:after { div.notices.tip p:first-child:after {
content: 'Tip'; content: 'Tip';
} }
/* attachments shortcode */
section.attachments {
margin: 2rem 0;
position: relative;
}
section.attachments label {
font-weight: 400;
padding-left: 0.5em;
padding-top: 0.2em;
padding-bottom: 0.2em;
margin: 0;
}
section.attachments .attachments-files {
padding: 15px;
display: block;
font-size: 1rem;
margin-top: 0rem;
margin-bottom: 0rem;
color: #666;
}
section.attachments.orange label {
color: #fff;
background: #F0B37E;
}
section.attachments.orange .attachments-files {
background: #FFF2DB;
}
section.attachments.green label {
color: #fff;
background: rgba(92, 184, 92, 0.8);
}
section.attachments.green .attachments-files {
background: #E6F9E6;
}
section.attachments.blue label {
color: #fff;
background: #6AB0DE;
}
section.attachments.blue .attachments-files {
background: #E7F2FA;
}
section.attachments.grey label {
color: #fff;
background: #505d65;
}
section.attachments.grey .attachments-files {
background: #f4f4f4;
}
/* Children shortcode */
/* Children shortcode */
.children p {
font-size: small;
margin-top: 0px;
padding-top: 0px;
margin-bottom: 0px;
padding-bottom: 0px;
}
.children-li p {
font-size: small;
font-style: italic;
}
.children-h2 p, .children-h3 p {
font-size: small;
margin-top: 0px;
padding-top: 0px;
margin-bottom: 0px;
padding-bottom: 0px;
}
.children h3,.children h2 {
margin-bottom: 0px;
margin-top: 5px;
}
code, kbd, pre, samp { code, kbd, pre, samp {
font-family: "Consolas", menlo, monospace; font-family: "Consolas", menlo, monospace;
font-size: 92%; font-size: 92%;
@@ -812,6 +901,9 @@ td {
overflow: auto; overflow: auto;
position: relative; position: relative;
} }
.hljs::selection, .hljs span::selection {
background: #b7b7b7;
}
.lightbox-active #body { .lightbox-active #body {
overflow: visible; overflow: visible;
} }
@@ -999,6 +1091,18 @@ pre .copy-to-clipboard:hover {
#sidebar ul.topics > li > a .read-icon { #sidebar ul.topics > li > a .read-icon {
margin-top: 9px; margin-top: 9px;
} }
#sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
#sidebar #shortcuts li {
padding: 2px 0;
list-style: none;
}
#sidebar ul li .read-icon { #sidebar ul li .read-icon {
display: none; display: none;
float: right; float: right;
@@ -1012,6 +1116,13 @@ pre .copy-to-clipboard:hover {
display: inline; display: inline;
} }
#sidebar #shortcuts h3 {
font-family: "Novacento Sans Wide", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif;
color: white ;
margin-top:1rem;
padding-left: 1rem;
}
#searchResults { #searchResults {
text-align: left; text-align: left;
} }

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 357 KiB

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,56 +1,66 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="js csstransforms3d"> <html lang="en" class="js csstransforms3d">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Getting started :: Lettre site</title>
<title>Getting started</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/getting-started/"> <body class="" data-url="/getting-started/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -62,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
parent
active
">
<a href="/getting-started/">
<li class="dd-item active parent" data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -126,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -214,13 +309,29 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
<div id="top-github-link">
<a class="github-link" href="https://github.com/lettre/lettre/edit/master/website/content/getting-started/_index.md" target="blank">
<i class="fa fa-code-fork"></i>
Edit this page
</a>
</div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -228,6 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -235,17 +349,43 @@
<a href='/'>Lettre site</a> > Getting started
<span itemprop="title"> Getting started</span>
</span>
</div>
<div class="progress">
<div class="wrapper">
<nav id="TableOfContents">
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#getting-started">Getting started</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
</div>
</div> </div>
</div> </div>
</div>
<div id="chapter">
<div id="body-inner"> <div id="body-inner">
<h1>Getting started</h1>
@@ -254,55 +394,13 @@
<p>This section explains how to manipulate emails you have created.</p> <p>This section explains how to manipulate emails you have created.</p>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
</div>
<div id="navigation"> <div id="navigation">
@@ -314,7 +412,6 @@
<a class="nav nav-next" href="/lettre/getting-started/intro/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -338,26 +435,194 @@
<a class="nav nav-prev" href="/" title="Lettre site"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/getting-started/intro/" title="Introduction" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>
</html> </html>

View File

@@ -2,22 +2,22 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Getting started on Lettre site</title> <title>Getting started on Lettre site</title>
<link>https://lettre.github.io/lettre/getting-started/</link> <link>http://docs.lettre.at/getting-started/</link>
<description>Recent content in Getting started on Lettre site</description> <description>Recent content in Getting started on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate> <lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate>
<atom:link href="https://lettre.github.io/lettre/getting-started/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/getting-started/index.xml" rel="self" type="application/rss+xml" />
<item> <item>
<title>Introduction</title> <title>Introduction</title>
<link>https://lettre.github.io/lettre/getting-started/intro/</link> <link>http://docs.lettre.at/getting-started/intro/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/getting-started/intro/</guid> <guid>http://docs.lettre.at/getting-started/intro/</guid>
<description>This documentation is written for lettre 0.7. Please use https://docs.rs/lettre/0.6.2/lettre/ for lettre 0.6. <description>This documentation is written for lettre 0.8. Please use https://docs.rs/lettre/0.7.0/lettre/ for lettre 0.7.
Lettre is an email library that allows creating and sending messages. It provides: Lettre is an email library that allows creating and sending messages. It provides:
An easy to use email builder Pluggable email transports Unicode support (for emails and transports, including for sender et recipient addresses when compatible) Secure defaults (emails are only sent encrypted by default) </description> An easy to use email builder Pluggable email transports Unicode support (for emails and transports, including for sender et recipient addresses when compatible) Secure defaults (emails are only sent encrypted by default) </description>
</item> </item>

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Introduction :: Lettre site</title>
<title>Introduction</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/getting-started/intro/"> <body class="" data-url="/getting-started/intro/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
parent
">
<a href="/getting-started/">
<li class="dd-item parent" data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item active" data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item active">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/getting-started/" itemprop="url"><span itemprop="title">Getting started</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> Introduction</span>
<a href='/'>Lettre site</a> > <a href='/getting-started/'>Getting started</a> > Introduction
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -260,16 +370,21 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
<h1>Introduction</h1> <h1>Introduction</h1>
<div class="notices note" ><p>This documentation is written for lettre 0.7.
Please use <a href="https://docs.rs/lettre/0.6.2/lettre/">https://docs.rs/lettre/0.6.2/lettre/</a> for lettre 0.6.</p>
<div class="notices note" ><p>This documentation is written for lettre 0.8.
Please use <a href="https://docs.rs/lettre/0.7.0/lettre/">https://docs.rs/lettre/0.7.0/lettre/</a> for lettre 0.7.</p>
</div> </div>
@@ -283,54 +398,15 @@ Please use <a href="https://docs.rs/lettre/0.6.2/lettre/">https://docs.rs/lettre
</ul> </ul>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -340,12 +416,6 @@ Please use <a href="https://docs.rs/lettre/0.6.2/lettre/">https://docs.rs/lettre
<a class="nav nav-prev" href="/lettre/getting-started/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/creating-messages/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -367,23 +437,197 @@ Please use <a href="https://docs.rs/lettre/0.6.2/lettre/">https://docs.rs/lettre
<a class="nav nav-prev" href="/getting-started/" title="Getting started"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/creating-messages/" title="Creating messages" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -1 +1 @@
<meta http-equiv="refresh" content="0; url=https://lettre.github.io/lettre//getting-started/intro/"/> <meta http-equiv="refresh" content="0; url=http://docs.lettre.at//getting-started/intro/"/>

62
docs/index.json Normal file
View File

@@ -0,0 +1,62 @@
[
{
"uri": "/content/creating-messages/_index",
"title": "Creating messages",
"content": "\nCreating messages\n\nThis section explains how to create emails.",
"tags": []
},
{
"uri": "/content/creating-messages/email",
"title": "Email creation",
"content": "\nSimple example\n\nThe email part builds email messages. For now, it does not support attachments.\nAn email is built using an EmailBuilder. The simplest email could be:\n\nextern crate lettre_email;\n\nuse lettre_email::EmailBuilder;\n\nfn main() {\n // Create an email\n let email = EmailBuilder::new()\n // Addresses can be specified by the tuple (email, alias)\n .to((\"user@example.org\", \"Firstname Lastname\"))\n // ... or by an address only\n .from(\"user@example.com\")\n .subject(\"Hi, Hello world\")\n .text(\"Hello world.\")\n .build();\n \n assert!(email.is_ok());\n}\n\nWhen the build method is called, the EmailBuilder will add the missing headers (like\nMessage-ID or Date) and check for missing necessary ones (like From or To). It will\nthen generate an Email that can be sent.\n\nThe text() method will create a plain text email, while the html() method will create an\nHTML email. You can use the alternative() method to provide both versions, using plain text\nas fallback for the HTML version.\n\n Complete example\n\nBelow is a more complete example, not using method chaining:\n\nextern crate lettre_email;\n\nuse lettre_email::EmailBuilder;\n\nfn main() {\n let mut builder = EmailBuilder::new();\n builder.add_to((\"user@example.org\", \"Alias name\"));\n builder.add_cc((\"user@example.net\", \"Alias name\"));\n builder.add_from(\"no-reply@example.com\");\n builder.add_from(\"no-reply@example.eu\");\n builder.set_sender(\"no-reply@example.com\");\n builder.set_subject(\"Hello world\");\n builder.set_alternative(\"h2Hi, Hello world./h2\", \"Hi, Hello world.\");\n builder.addreplyto(\"contact@example.com\");\n builder.add_header((\"X-Custom-Header\", \"my header\"));\n \n let email = builder.build();\n assert!(email.is_ok());\n}\n\nSee the EmailBuilder documentation for a complete list of methods.\n\n",
"tags": []
},
{
"uri": "/content/getting-started/_index",
"title": "Getting started",
"content": "\nGetting started\n\nThis section explains how to manipulate emails you have created.",
"tags": []
},
{
"uri": "/content/getting-started/intro",
"title": "Introduction",
"content": "\n{{% notice note %}}\nThis documentation is written for lettre 0.8.\nPlease use https://docs.rs/lettre/0.7.0/lettre/ for lettre 0.7.\n{{% /notice%}}\n\nLettre is an email library that allows creating and sending messages. It provides:\n\nAn easy to use email builder\nPluggable email transports\nUnicode support (for emails and transports, including for sender et recipient addresses when compatible)\nSecure defaults (emails are only sent encrypted by default)\n",
"tags": []
},
{
"uri": "/content/sending-messages/_index",
"title": "Sending messages",
"content": "\nSending Messages\n\nThis section explains how to manipulate emails you have created.",
"tags": []
},
{
"uri": "/content/sending-messages/file",
"title": "File transport",
"content": "\nThe file transport writes the emails to the given directory. The name of the file will be\nmessage_id.txt.\nIt can be useful for testing purposes, or if you want to keep track of sent messages.\n\nextern crate lettre;\n\nuse std::env::temp_dir;\n\nuse lettre::file::FileEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nfn main() {\n // Write to the local temp directory\n let mut sender = FileEmailTransport::new(temp_dir());\n let email = SimpleSendableEmail::new(\n \"user@localhost\".to_string(),\n &[\"root@localhost\".to_string()],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n ).unwrap();\n \n let result = sender.send(&email);\n assert!(result.is_ok());\n}\n\nExample result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt:\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\nTo: root@localhost\nFrom: user@localhost\nSubject: Hello\nDate: Sat, 31 Oct 2015 13:42:19 +0100\nMessage-ID: b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost\n\nHello World!\n",
"tags": []
},
{
"uri": "/content/sending-messages/intro",
"title": "Introduction",
"content": "\nThis mailer contains several different transports for your emails. To be sendable, the\nemails have to implement SendableEmail, which is the case for emails created with lettre_email.\n\nThe following transports are available:\n\nThe SmtpTransport uses the SMTP protocol to send the message over the network. It is\n the preferred way of sending emails.\nThe SendmailTransport uses the sendmail command to send messages. It is an alternative to\n the SMTP transport.\nThe FileTransport creates a file containing the email content to be sent. It can be used\n for debugging or if you want to keep all sent emails.\nThe StubTransport is useful for debugging, and only prints the content of the email in the\n logs.\n",
"tags": []
},
{
"uri": "/content/sending-messages/sendmail",
"title": "Sendmail transport",
"content": "\nThe sendmail transport sends the email using the local sendmail command.\n\nextern crate lettre;\n\nuse lettre::sendmail::SendmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nfn main() {\n let email = SimpleSendableEmail::new(\n \"user@localhost\".to_string(),\n &[\"root@localhost\".to_string()],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n ).unwrap();\n \n let mut sender = SendmailTransport::new();\n let result = sender.send(&email);\n assert!(result.is_ok());\n}\n",
"tags": []
},
{
"uri": "/content/sending-messages/smtp",
"title": "SMTP transport",
"content": "\nThis transport uses the SMTP protocol to send emails over the network (locally or remotely).\n\nIt is designed to be:\n\nSecured: email are encrypted by default\nModern: Unicode support for email content and sender/recipient addresses when compatible\nFast: supports tcp connection reuse\n\nThis client is designed to send emails to a relay server, and should not be used to send\nemails directly to the destination.\n\nThe relay server can be the local email server, a specific host or a third-party service.\n\nSimple example\n\nThis is the most basic example of usage:\n\nextern crate lettre;\n\nuse lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};\n\nfn main() {\n let email = SimpleSendableEmail::new(\n \"user@localhost\".to_string(),\n &[\"root@localhost\".to_string()],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n ).unwrap();\n \n // Open a local connection on port 25\n let mut mailer =\n SmtpTransport::builderunencryptedlocalhost().unwrap().build();\n // Send the email\n let result = mailer.send(&email);\n \n assert!(result.is_ok());\n}\n\n Complete example\n\nextern crate lettre;\n\nuse lettre::smtp::authentication::{Credentials, Mechanism};\nuse lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};\nuse lettre::smtp::extension::ClientId;\nuse lettre::smtp::ConnectionReuseParameters;\n\nfn main() {\n let email = SimpleSendableEmail::new(\n \"user@localhost\".to_string(),\n &[\"root@localhost\".to_string()],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n ).unwrap();\n \n // Connect to a remote server on a custom port\n let mut mailer = SmtpTransport::simple_builder(\"server.tld\").unwrap()\n // Set the name sent during EHLO/HELO, default is localhost\n .helloname(ClientId::Domain(\"my.hostname.tld\".tostring()))\n // Add credentials for authentication\n .credentials(Credentials::new(\"username\".tostring(), \"password\".tostring()))\n // Enable SMTPUTF8 if the server supports it\n .smtp_utf8(true)\n // Configure expected authentication mechanism\n .authentication_mechanism(Mechanism::Plain)\n // Enable connection reuse\n .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build();\n \n let result_1 = mailer.send(&email);\n assert!(result1.isok());\n \n // The second email will use the same connection\n let result_2 = mailer.send(&email);\n assert!(result2.isok());\n \n // Explicitly close the SMTP transaction as we enabled connection reuse\n mailer.close();\n}\n\nLower level\n\nYou can also send commands, here is a simple email transaction without\nerror handling:\n\nextern crate lettre;\n\nuse lettre::EmailAddress;\nuse lettre::smtp::SMTP_PORT;\nuse lettre::smtp::client::Client;\nuse lettre::smtp::client::net::NetworkStream;\nuse lettre::smtp::extension::ClientId;\nuse lettre::smtp::commands::*;\n\nfn main() {\n let mut email_client: ClientNetworkStream = Client::new();\n let _ = emailclient.connect(&(\"localhost\", SMTPPORT), None);\n let _ = emailclient.command(EhloCommand::new(ClientId::new(\"myhostname\".to_string())));\n let _ = email_client.command(\n MailCommand::new(Some(EmailAddress::new(\"user@example.com\".to_string()).unwrap()), vec![])\n );\n let _ = email_client.command(\n RcptCommand::new(EmailAddress::new(\"user@example.org\".to_string()).unwrap(), vec![])\n );\n let _ = email_client.command(DataCommand);\n let _ = emailclient.message(Box::new(\"Test email\".asbytes()));\n let _ = email_client.command(QuitCommand);\n}\n\n",
"tags": []
},
{
"uri": "/content/sending-messages/stub",
"title": "Stub transport",
"content": "\nThe stub transport only logs message envelope and drops the content. It can be useful for\ntesting purposes.\n\nextern crate lettre;\n\nuse lettre::stub::StubEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nfn main() {\n let email = SimpleSendableEmail::new(\n \"user@localhost\".to_string(),\n &[\"root@localhost\".to_string()],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n ).unwrap();\n \n let mut sender = StubEmailTransport::new_positive();\n let result = sender.send(&email);\n assert!(result.is_ok());\n}\n\nWill log (when using a logger like env_logger):\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\n",
"tags": []
}
]

View File

@@ -2,78 +2,88 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Lettre site</title> <title>Lettre site</title>
<link>https://lettre.github.io/lettre/</link> <link>http://docs.lettre.at/</link>
<description>Recent content on Lettre site</description> <description>Recent content on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<lastBuildDate>Sun, 21 May 2017 23:46:17 +0200</lastBuildDate> <lastBuildDate>Sun, 21 May 2017 23:46:17 +0200</lastBuildDate>
<atom:link href="https://lettre.github.io/lettre/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/index.xml" rel="self" type="application/rss+xml" />
<item> <item>
<title>Introduction</title> <title>Introduction</title>
<link>https://lettre.github.io/lettre/getting-started/intro/</link> <link>http://docs.lettre.at/getting-started/intro/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/getting-started/intro/</guid> <guid>http://docs.lettre.at/getting-started/intro/</guid>
<description>This documentation is written for lettre 0.7. Please use https://docs.rs/lettre/0.6.2/lettre/ for lettre 0.6. <description>This documentation is written for lettre 0.8. Please use https://docs.rs/lettre/0.7.0/lettre/ for lettre 0.7.
Lettre is an email library that allows creating and sending messages. It provides: Lettre is an email library that allows creating and sending messages. It provides:
An easy to use email builder Pluggable email transports Unicode support (for emails and transports, including for sender et recipient addresses when compatible) Secure defaults (emails are only sent encrypted by default) </description> An easy to use email builder Pluggable email transports Unicode support (for emails and transports, including for sender et recipient addresses when compatible) Secure defaults (emails are only sent encrypted by default) </description>
</item> </item>
<item> <item>
<title>Introduction</title> <title>Introduction</title>
<link>https://lettre.github.io/lettre/sending-messages/intro/</link> <link>http://docs.lettre.at/sending-messages/intro/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/intro/</guid> <guid>http://docs.lettre.at/sending-messages/intro/</guid>
<description>This mailer contains several different transports for your emails. To be sendable, the emails have to implement SendableEmail, which is the case for emails created with lettre_email. <description>This mailer contains several different transports for your emails. To be sendable, the emails have to implement SendableEmail, which is the case for emails created with lettre_email.
The following transports are available: The following transports are available:
The SmtpTransport uses the SMTP protocol to send the message over the network. It is the prefered way of sending emails. The SendmailTransport uses the sendmail command to send messages. It is an alternative to the SMTP transport.</description> The SmtpTransport uses the SMTP protocol to send the message over the network. It is the preferred way of sending emails. The SendmailTransport uses the sendmail command to send messages. It is an alternative to the SMTP transport.</description>
</item> </item>
<item> <item>
<title>SMTP transport</title> <title>SMTP transport</title>
<link>https://lettre.github.io/lettre/sending-messages/smtp/</link> <link>http://docs.lettre.at/sending-messages/smtp/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/smtp/</guid> <guid>http://docs.lettre.at/sending-messages/smtp/</guid>
<description>This transport uses the SMTP protocol to send emails over the network (locally or remotely). <description>This transport uses the SMTP protocol to send emails over the network (locally or remotely).
It is desinged to be: It is designed to be:
Secured: email are encrypted by default Modern: Unicode support for email content and sender/recipient adresses when compatible Fast: supports tcp connection reuse This client is designed to send emails to a relay server, and should not be used to send emails directly to the destination. Secured: email are encrypted by default Modern: Unicode support for email content and sender/recipient addresses when compatible Fast: supports tcp connection reuse This client is designed to send emails to a relay server, and should not be used to send emails directly to the destination.
The relay server can be the local email server, a specific host or a third-party service.</description> The relay server can be the local email server, a specific host or a third-party service.</description>
</item> </item>
<item> <item>
<title>Sendmail transport</title> <title>Sendmail transport</title>
<link>https://lettre.github.io/lettre/sending-messages/sendmail/</link> <link>http://docs.lettre.at/sending-messages/sendmail/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/sendmail/</guid> <guid>http://docs.lettre.at/sending-messages/sendmail/</guid>
<description>The sendmail transport sends the email using the local sendmail command. <description>The sendmail transport sends the email using the local sendmail command.
use lettre::sendmail::SendmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let mut sender = SendmailTransport::new(); let result = sender.send(&amp;email); assert!(result.is_ok()); </description> extern crate lettre; use lettre::sendmail::SendmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).unwrap(); let mut sender = SendmailTransport::new(); let result = sender.send(&amp;amp;email); assert!(result.is_ok()); }</description>
</item>
<item>
<title>Email creation</title>
<link>http://docs.lettre.at/creating-messages/email/</link>
<pubDate>Sun, 21 Jan 2018 23:46:17 +0200</pubDate>
<guid>http://docs.lettre.at/creating-messages/email/</guid>
<description>Simple example The email part builds email messages. For now, it does not support attachments. An email is built using an EmailBuilder. The simplest email could be:
extern crate lettre_email; use lettre_email::EmailBuilder; fn main() { // Create an email let email = EmailBuilder::new() // Addresses can be specified by the tuple (email, alias) .to((&amp;#34;user@example.org&amp;#34;, &amp;#34;Firstname Lastname&amp;#34;)) // ... or by an address only .from(&amp;#34;user@example.com&amp;#34;) .subject(&amp;#34;Hi, Hello world&amp;#34;) .</description>
</item> </item>
<item> <item>
<title>File transport</title> <title>File transport</title>
<link>https://lettre.github.io/lettre/sending-messages/file/</link> <link>http://docs.lettre.at/sending-messages/file/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/file/</guid> <guid>http://docs.lettre.at/sending-messages/file/</guid>
<description>The file transport writes the emails to the given directory. The name of the file will be message_id.txt. It can be useful for testing purposes, or if you want to keep track of sent messages. <description>The file transport writes the emails to the given directory. The name of the file will be message_id.txt. It can be useful for testing purposes, or if you want to keep track of sent messages.
use std::env::temp_dir; use lettre::file::FileEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; // Write to the local temp directory let mut sender = FileEmailTransport::new(temp_dir()); let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let result = sender.send(&amp;email); assert!(result.is_ok()); Example result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.</description> extern crate lettre; use std::env::temp_dir; use lettre::file::FileEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { // Write to the local temp directory let mut sender = FileEmailTransport::new(temp_dir()); let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).</description>
</item> </item>
<item> <item>
<title>Stub transport</title> <title>Stub transport</title>
<link>https://lettre.github.io/lettre/sending-messages/stub/</link> <link>http://docs.lettre.at/sending-messages/stub/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/stub/</guid> <guid>http://docs.lettre.at/sending-messages/stub/</guid>
<description>The stub transport only logs message envelope and drops the content. It can be useful for testing purposes. <description>The stub transport only logs message envelope and drops the content. It can be useful for testing purposes.
use lettre::stub::StubEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let mut sender = StubEmailTransport::new_positive(); let result = sender.send(&amp;email); assert!(result.is_ok()); Will log (when using a logger like env_logger): extern crate lettre; use lettre::stub::StubEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).unwrap(); let mut sender = StubEmailTransport::new_positive(); let result = sender.send(&amp;amp;email); assert!(result.is_ok()); } Will log (when using a logger like env_logger):
b7c211bc-9811-45ce-8cd9-68eab575d695: from= to= </description> b7c211bc-9811-45ce-8cd9-68eab575d695: from=&amp;lt;user@localhost&amp;gt; to=&amp;lt;root@localhost&amp;gt;</description>
</item> </item>
</channel> </channel>

223
docs/js/auto-complete.js Normal file
View File

@@ -0,0 +1,223 @@
/*
JavaScript autoComplete v1.0.4
Copyright (c) 2014 Simon Steinberger / Pixabay
GitHub: https://github.com/Pixabay/JavaScript-autoComplete
License: http://www.opensource.org/licenses/mit-license.php
*/
var autoComplete = (function(){
// "use strict";
function autoComplete(options){
if (!document.querySelector) return;
// helpers
function hasClass(el, className){ return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className); }
function addEvent(el, type, handler){
if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
}
function removeEvent(el, type, handler){
// if (el.removeEventListener) not working in IE11
if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
}
function live(elClass, event, cb, context){
addEvent(context || document, event, function(e){
var found, el = e.target || e.srcElement;
while (el && !(found = hasClass(el, elClass))) el = el.parentElement;
if (found) cb.call(el, e);
});
}
var o = {
selector: 0,
source: 0,
minChars: 3,
delay: 150,
offsetLeft: 0,
offsetTop: 1,
cache: 1,
menuClass: '',
renderItem: function (item, search){
// escape special characters
search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi");
return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.replace(re, "<b>$1</b>") + '</div>';
},
onSelect: function(e, term, item){}
};
for (var k in options) { if (options.hasOwnProperty(k)) o[k] = options[k]; }
// init
var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector);
for (var i=0; i<elems.length; i++) {
var that = elems[i];
// create suggestions container "sc"
that.sc = document.createElement('div');
that.sc.className = 'autocomplete-suggestions '+o.menuClass;
that.autocompleteAttr = that.getAttribute('autocomplete');
that.setAttribute('autocomplete', 'off');
that.cache = {};
that.last_val = '';
that.updateSC = function(resize, next){
var rect = that.getBoundingClientRect();
that.sc.style.left = Math.round(rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + o.offsetLeft) + 'px';
that.sc.style.top = Math.round(rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + o.offsetTop) + 'px';
that.sc.style.width = Math.round(rect.right - rect.left) + 'px'; // outerWidth
if (!resize) {
that.sc.style.display = 'block';
if (!that.sc.maxHeight) { that.sc.maxHeight = parseInt((window.getComputedStyle ? getComputedStyle(that.sc, null) : that.sc.currentStyle).maxHeight); }
if (!that.sc.suggestionHeight) that.sc.suggestionHeight = that.sc.querySelector('.autocomplete-suggestion').offsetHeight;
if (that.sc.suggestionHeight)
if (!next) that.sc.scrollTop = 0;
else {
var scrTop = that.sc.scrollTop, selTop = next.getBoundingClientRect().top - that.sc.getBoundingClientRect().top;
if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0)
that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight;
else if (selTop < 0)
that.sc.scrollTop = selTop + scrTop;
}
}
}
addEvent(window, 'resize', that.updateSC);
document.body.appendChild(that.sc);
live('autocomplete-suggestion', 'mouseleave', function(e){
var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
if (sel) setTimeout(function(){ sel.className = sel.className.replace('selected', ''); }, 20);
}, that.sc);
live('autocomplete-suggestion', 'mouseover', function(e){
var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
if (sel) sel.className = sel.className.replace('selected', '');
this.className += ' selected';
}, that.sc);
live('autocomplete-suggestion', 'mousedown', function(e){
if (hasClass(this, 'autocomplete-suggestion')) { // else outside click
var v = this.getAttribute('data-val');
that.value = v;
o.onSelect(e, v, this);
that.sc.style.display = 'none';
}
}, that.sc);
that.blurHandler = function(){
try { var over_sb = document.querySelector('.autocomplete-suggestions:hover'); } catch(e){ var over_sb = 0; }
if (!over_sb) {
that.last_val = that.value;
that.sc.style.display = 'none';
setTimeout(function(){ that.sc.style.display = 'none'; }, 350); // hide suggestions on fast input
} else if (that !== document.activeElement) setTimeout(function(){ that.focus(); }, 20);
};
addEvent(that, 'blur', that.blurHandler);
var suggest = function(data){
var val = that.value;
that.cache[val] = data;
if (data.length && val.length >= o.minChars) {
var s = '';
for (var i=0;i<data.length;i++) s += o.renderItem(data[i], val);
that.sc.innerHTML = s;
that.updateSC(0);
}
else
that.sc.style.display = 'none';
}
that.keydownHandler = function(e){
var key = window.event ? e.keyCode : e.which;
// down (40), up (38)
if ((key == 40 || key == 38) && that.sc.innerHTML) {
var next, sel = that.sc.querySelector('.autocomplete-suggestion.selected');
if (!sel) {
next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last
next.className += ' selected';
console.log(next);
that.value = next.getAttribute('data-val');
} else {
next = (key == 40) ? sel.nextSibling : sel.previousSibling;
if (next) {
sel.className = sel.className.replace('selected', '');
next.className += ' selected';
that.value = next.getAttribute('data-val');
}
else { sel.className = sel.className.replace('selected', ''); that.value = that.last_val; next = 0; }
}
that.updateSC(0, next);
return false;
}
// esc
else if (key == 27) { that.value = that.last_val; that.sc.style.display = 'none'; }
// enter
else if (key == 13 || key == 9) {
var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
if (sel && that.sc.style.display != 'none') { o.onSelect(e, sel.getAttribute('data-val'), sel); setTimeout(function(){ that.sc.style.display = 'none'; }, 20); }
}
};
addEvent(that, 'keydown', that.keydownHandler);
that.keyupHandler = function(e){
var key = window.event ? e.keyCode : e.which;
if (!key || (key < 35 || key > 40) && key != 13 && key != 27) {
var val = that.value;
if (val.length >= o.minChars) {
if (val != that.last_val) {
that.last_val = val;
clearTimeout(that.timer);
if (o.cache) {
if (val in that.cache) { suggest(that.cache[val]); return; }
// no requests if previous suggestions were empty
for (var i=1; i<val.length-o.minChars; i++) {
var part = val.slice(0, val.length-i);
if (part in that.cache && !that.cache[part].length) { suggest([]); return; }
}
}
that.timer = setTimeout(function(){ o.source(val, suggest) }, o.delay);
}
} else {
that.last_val = val;
that.sc.style.display = 'none';
}
}
};
addEvent(that, 'keyup', that.keyupHandler);
that.focusHandler = function(e){
that.last_val = '\n';
that.keyupHandler(e)
};
if (!o.minChars) addEvent(that, 'focus', that.focusHandler);
}
// public destroy method
this.destroy = function(){
for (var i=0; i<elems.length; i++) {
var that = elems[i];
removeEvent(window, 'resize', that.updateSC);
removeEvent(that, 'blur', that.blurHandler);
removeEvent(that, 'focus', that.focusHandler);
removeEvent(that, 'keydown', that.keydownHandler);
removeEvent(that, 'keyup', that.keyupHandler);
if (that.autocompleteAttr)
that.setAttribute('autocomplete', that.autocompleteAttr);
else
that.removeAttribute('autocomplete');
document.body.removeChild(that.sc);
that = null;
}
};
}
return autoComplete;
})();
(function(){
if (typeof define === 'function' && define.amd)
define('autoComplete', function () { return autoComplete; });
else if (typeof module !== 'undefined' && module.exports)
module.exports = autoComplete;
else
window.autoComplete = autoComplete;
})();

View File

@@ -17,7 +17,7 @@ var getUrlParameter = function getUrlParameter(sPageURL) {
}; };
// Execute actions on images generated from Markdown pages // Execute actions on images generated from Markdown pages
var images = $("div#body-inner img"); var images = $("div#body-inner img").not(".inline");
// Wrap image inside a featherlight (to get a full size view in a popup) // Wrap image inside a featherlight (to get a full size view in a popup)
images.wrap(function(){ images.wrap(function(){
var image =$(this); var image =$(this);
@@ -56,7 +56,10 @@ images.each(function(index){
}); });
// Stick the top to the top of the screen when scrolling // Stick the top to the top of the screen when scrolling
$("#top-bar").stick_in_parent({spacer: false}); $("#top-bar").stick_in_parent( {
parent: ".sticky-parent",
spacer: ".sticky-spacer",
});
jQuery(document).ready(function() { jQuery(document).ready(function() {

View File

@@ -83,9 +83,14 @@ $(window).resize(function() {
jQuery(document).ready(function() { jQuery(document).ready(function() {
jQuery('#sidebar .category-icon').on('click', function() {
$( this ).toggleClass("fa-angle-down fa-angle-right") ;
$( this ).parent().parent().children('ul').toggle() ;
return false;
});
var sidebarStatus = searchStatus = 'open'; var sidebarStatus = searchStatus = 'open';
$('#sidebar .highlightable').perfectScrollbar(); $('#sidebar .highlightable').perfectScrollbar();
// set the menu height
setMenuHeight(); setMenuHeight();
jQuery('#overlay').on('click', function() { jQuery('#overlay').on('click', function() {
@@ -147,10 +152,25 @@ jQuery(document).ready(function() {
}); });
}); });
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
if (sessionStorage.getItem('search-value')) { if (sessionStorage.getItem('search-value')) {
jQuery(document.body).removeClass('searchbox-hidden'); var searchValue = sessionStorage.getItem('search-value')
jQuery('[data-search-input]').val(sessionStorage.getItem('search-value')); $(document.body).removeClass('searchbox-hidden');
jQuery('[data-search-input]').trigger('input'); $('[data-search-input]').val(searchValue);
$('[data-search-input]').trigger('input');
var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0);
if (searchedElem) {
searchedElem.scrollIntoView(true);
var scrolledY = window.scrollY;
if(scrolledY){
window.scroll(0, scrolledY - 125);
}
}
} }
// clipboard // clipboard
@@ -218,6 +238,8 @@ jQuery(document).ready(function() {
$('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight'); $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight');
$('#body-inner a:not(:has(img)):not(.btn)').addClass('highlight'); $('#body-inner a:not(:has(img)):not(.btn)').addClass('highlight');
var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
if (!touchsupport){ // browser doesn't support touch
$('#toc-menu').hover(function() { $('#toc-menu').hover(function() {
$('.progress').stop(true, false, true).fadeToggle(100); $('.progress').stop(true, false, true).fadeToggle(100);
}); });
@@ -225,6 +247,99 @@ jQuery(document).ready(function() {
$('.progress').hover(function() { $('.progress').hover(function() {
$('.progress').stop(true, false, true).fadeToggle(100); $('.progress').stop(true, false, true).fadeToggle(100);
}); });
}
if (touchsupport){ // browser does support touch
$('#toc-menu').click(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
$('.progress').click(function() {
$('.progress').stop(true, false, true).fadeToggle(100);
});
}
/**
* Fix anchor scrolling that hides behind top nav bar
* Courtesy of https://stackoverflow.com/a/13067009/28106
*
* We could use pure css for this if only heading anchors were
* involved, but this works for any anchor, including footnotes
**/
(function (document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function () {
this.scrollToCurrent();
$(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
$('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function () {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function (href, pushToHistory) {
var match, anchorOffset;
if (!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if (match) {
anchorOffset = $(match).offset().top - this.getFixedOffset();
$('html, body').animate({ scrollTop: anchorOffset });
// Add the state to history as-per normal anchor links
if (HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location's hash.
*/
scrollToCurrent: function (e) {
if (this.scrollIfAnchor(window.location.hash) && e) {
e.preventDefault();
}
},
/**
* If the click event's target was an anchor, fix the scroll position.
*/
delegateAnchors: function (e) {
var elem = e.target;
if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
e.preventDefault();
}
}
};
$(document).ready($.proxy(anchorScrolls, 'init'));
})(window.document, window.history, window.location);
}); });
jQuery(window).on('load', function() { jQuery(window).on('load', function() {

View File

@@ -1,9 +1,17 @@
var lunrIndex, pagesIndex; var lunrIndex, pagesIndex;
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
// Initialize lunrjs using our generated index file // Initialize lunrjs using our generated index file
function initLunr() { function initLunr() {
if (!endsWith(baseurl,"/")){
baseurl = baseurl+'/'
};
// First retrieve the index file // First retrieve the index file
$.getJSON(baseurl + "/json/search.json") $.getJSON(baseurl +"index.json")
.done(function(index) { .done(function(index) {
pagesIndex = index; pagesIndex = index;
// Set up lunrjs by declaring the fields we use // Set up lunrjs by declaring the fields we use
@@ -50,36 +58,33 @@ function search(query) {
// Let's get started // Let's get started
initLunr(); initLunr();
$( document ).ready(function() { $( document ).ready(function() {
var horseyList = horsey($("#search-by").get(0), { var searchList = new autoComplete({
suggestions: function (value, done) { /* selector for the search box element */
var query = $("#search-by").val(); selector: $("#search-by").get(0),
var results = search(query); /* source is the callback to perform the search */
done(results); source: function(term, response) {
response(search(term));
}, },
filter: function (q, suggestion) { /* renderItem displays individual search results */
return true; renderItem: function(item, term) {
var numContextWords = 2;
var text = item.content.match(
"(?:\\s?(?:[\\w]+)\\s?){0,"+numContextWords+"}" +
term+"(?:\\s?(?:[\\w]+)\\s?){0,"+numContextWords+"}");
item.context = text;
return '<div class="autocomplete-suggestion" ' +
'data-term="' + term + '" ' +
'data-title="' + item.title + '" ' +
'data-uri="'+ item.uri + '" ' +
'data-context="' + item.context + '">' +
'» ' + item.title +
'<div class="context">' +
(item.context || '') +'</div>' +
'</div>';
}, },
set: function (value) { /* onSelect callback fires when a search suggestion is chosen */
location.href=value.href; onSelect: function(e, term, item) {
}, location.href = item.getAttribute('data-uri');
render: function (li, suggestion) {
var uri = suggestion.uri.substring(1,suggestion.uri.length);
var indexOfIndex = uri.lastIndexOf("/index");
if (indexOfIndex == -1) {
indexOfIndex = uri.length;
} }
var href = uri.substring(uri.indexOf("/"), indexOfIndex);
suggestion.href = baseurl + href;
var query = $("#search-by").val();
var numWords = 2;
var text = suggestion.content.match("(?:\\s?(?:[\\w]+)\\s?){0,"+numWords+"}"+query+"(?:\\s?(?:[\\w]+)\\s?){0,"+numWords+"}");
suggestion.context = text;
var image = '<div>' + '» ' + suggestion.title + '</div><div style="font-size:12px">' + (suggestion.context || '') +'</div>';
li.innerHTML = image;
},
limit: 10
}); });
horseyList.refreshPosition();
}); });

View File

@@ -14,7 +14,7 @@
{ {
"uri": "/content/getting-started/intro", "uri": "/content/getting-started/intro",
"title": "Introduction", "title": "Introduction",
"content": "\n{{% notice note %}}\nThis documentation is written for lettre 0.7.\nPlease use https://docs.rs/lettre/0.6.2/lettre/ for lettre 0.6.\n{{% /notice%}}\n\nLettre is an email library that allows creating and sending messages. It provides:\n\nAn easy to use email builder\nPluggable email transports\nUnicode support (for emails and transports, including for sender et recipient addresses when compatible)\nSecure defaults (emails are only sent encrypted by default)\n", "content": "\nLettre is an email library that allows creating and sending messages. It provides:\n\nAn easy to use email builder\nPluggable email transports\nUnicode support (for emails and transports, including for sender et recipient addresses when compatible)\nSecure defaults (emails are only sent encrypted by default)\n",
"tags": [] "tags": []
}, },
{ {
@@ -26,7 +26,7 @@
{ {
"uri": "/content/sending-messages/file", "uri": "/content/sending-messages/file",
"title": "File transport", "title": "File transport",
"content": "\nThe file transport writes the emails to the given directory. The name of the file will be\nmessage_id.txt.\nIt can be useful for testing purposes, or if you want to keep track of sent messages.\n\nuse std::env::temp_dir;\n\nuse lettre::file::FileEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};\n\n// Write to the local temp directory\nlet mut sender = FileEmailTransport::new(temp_dir());\nlet email = SimpleSendableEmail::new(\n EmailAddress::new(\"user@localhost\".to_string()),\n vec![EmailAddress::new(\"root@localhost\".to_string())],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n );\n\nlet result = sender.send(&email);\nassert!(result.is_ok());\n\nExample result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt:\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\nTo: root@localhost\nFrom: user@localhost\nSubject: Hello\nDate: Sat, 31 Oct 2015 13:42:19 +0100\nMessage-ID: b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost\n\nHello World!\n", "content": "\nThe file transport writes the emails to the given directory. The name of the file will be\nmessage_id.txt.\nIt can be useful for testing purposes, or if you want to keep track of sent messages.\n\nuse std::env::temp_dir;\n\nuse lettre::file::FileEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\n// Write to the local temp directory\nlet mut sender = FileEmailTransport::new(temp_dir());\nlet email = SimpleSendableEmail::new(\n \"user@localhost\",\n vec![\"root@localhost\"],\n \"message_id\",\n \"Hello world\"\n );\n\nlet result = sender.send(email);\nassert!(result.is_ok());\n\nExample result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt:\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\nTo: root@localhost\nFrom: user@localhost\nSubject: Hello\nDate: Sat, 31 Oct 2015 13:42:19 +0100\nMessage-ID: b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost\n\nHello World!\n",
"tags": [] "tags": []
}, },
{ {
@@ -38,19 +38,19 @@
{ {
"uri": "/content/sending-messages/sendmail", "uri": "/content/sending-messages/sendmail",
"title": "Sendmail transport", "title": "Sendmail transport",
"content": "\nThe sendmail transport sends the email using the local sendmail command.\n\nuse lettre::sendmail::SendmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};\n\nlet email = SimpleSendableEmail::new(\n EmailAddress::new(\"user@localhost\".to_string()),\n vec![EmailAddress::new(\"root@localhost\".to_string())],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n );\n\nlet mut sender = SendmailTransport::new();\nlet result = sender.send(&email);\nassert!(result.is_ok());\n", "content": "\nThe sendmail transport sends the email using the local sendmail command.\n\nuse lettre::sendmail::SendmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nlet email = SimpleSendableEmail::new(\n \"user@localhost\",\n vec![\"root@localhost\"],\n \"message_id\",\n \"Hello world\"\n );\n\nlet mut sender = SendmailTransport::new();\nlet result = sender.send(email);\nassert!(result.is_ok());\n",
"tags": [] "tags": []
}, },
{ {
"uri": "/content/sending-messages/smtp", "uri": "/content/sending-messages/smtp",
"title": "SMTP transport", "title": "SMTP transport",
"content": "\nThis transport uses the SMTP protocol to send emails over the network (locally or remotely).\n\nIt is desinged to be:\n\nSecured: email are encrypted by default\nModern: Unicode support for email content and sender/recipient adresses when compatible\nFast: supports tcp connection reuse\n\nThis client is designed to send emails to a relay server, and should not be used to send\nemails directly to the destination.\n\nThe relay server can be the local email server, a specific host or a third-party service.\n\nSimple example\n\nThis is the most basic example of usage:\n\nuse lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};\n\nlet email = SimpleSendableEmail::new(\n EmailAddress::new(\"user@localhost\".to_string()),\n vec![EmailAddress::new(\"root@localhost\".to_string())],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n );\n\n// Open a local connection on port 25\nlet mut mailer =\nSmtpTransport::builderunencryptedlocalhost().unwrap().build();\n// Send the email\nlet result = mailer.send(&email);\n\nassert!(result.is_ok());\n\n Complete example\n\nuse lettre::smtp::authentication::{Credentials, Mechanism};\nuse lettre::smtp::SUBMISSION_PORT;\nuse lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};\nuse lettre::smtp::extension::ClientId;\nuse lettre::smtp::ConnectionReuseParameters;\n\nlet email = SimpleSendableEmail::new(\n EmailAddress::new(\"user@localhost\".to_string()),\n vec![EmailAddress::new(\"root@localhost\".to_string())],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n );\n\n// Connect to a remote server on a custom port\nlet mut mailer = SmtpTransport::simplebuilder(\"server.tld\".tostring()).unwrap()\n // Set the name sent during EHLO/HELO, default is localhost\n .helloname(ClientId::Domain(\"my.hostname.tld\".tostring()))\n // Add credentials for authentication\n .credentials(Credentials::new(\"username\".tostring(), \"password\".tostring()))\n // Enable SMTPUTF8 if the server supports it\n .smtp_utf8(true)\n // Configure expected authentication mechanism\n .authentication_mechanism(Mechanism::Plain)\n // Enable connection reuse\n .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build();\n\nlet result_1 = mailer.send(&email);\nassert!(result1.isok());\n\n// The second email will use the same connection\nlet result_2 = mailer.send(&email);\nassert!(result2.isok());\n\n// Explicitly close the SMTP transaction as we enabled connection reuse\nmailer.close();\n\nLower level\n\nYou can also send commands, here is a simple email transaction without\nerror handling:\n\nuse lettre::EmailAddress;\nuse lettre::smtp::SMTP_PORT;\nuse lettre::smtp::client::Client;\nuse lettre::smtp::client::net::NetworkStream;\nuse lettre::smtp::extension::ClientId;\nuse lettre::smtp::commands::*;\n\nlet mut email_client: ClientNetworkStream = Client::new();\nlet _ = emailclient.connect(&(\"localhost\", SMTPPORT), None);\nlet _ = emailclient.smtpcommand(EhloCommand::new(ClientId::new(\"myhostname\".tostring())));\nlet _ = emailclient.smtpcommand(\n MailCommand::new(Some(EmailAddress::new(\"user@example.com\".to_string())), vec![])\n );\nlet _ = emailclient.smtpcommand(\n RcptCommand::new(EmailAddress::new(\"user@example.org\".to_string()), vec![])\n );\nlet _ = emailclient.smtpcommand(DataCommand);\nlet _ = emailclient.message(Box::new(\"Test email\".asbytes()));\nlet _ = emailclient.smtpcommand(QuitCommand);\n\n", "content": "\nThis transport uses the SMTP protocol to send emails over the network (locally or remotely).\n\nIt is desinged to be:\n\nSecured: email are encrypted by default\nModern: Unicode support for email content and sender/recipient adresses when compatible\nFast: supports tcp connection reuse\n\nThis client is designed to send emails to a relay server, and should not be used to send\nemails directly to the destination.\n\nThe relay server can be the local email server, a specific host or a third-party service.\n\nSimple example\n\nThis is the most basic example of usage:\n\nuse lettre::{SimpleSendableEmail, EmailTransport};\nuse lettre::smtp::SmtpTransportBuilder;\nuse lettre::smtp::SecurityLevel;\n\nlet email = SimpleSendableEmail::new(\n \"user@localhost\",\n vec![\"root@localhost\"],\n \"message_id\",\n \"Hello world\"\n );\n\n// Open a local connection on port 25\nlet mut mailer =\nSmtpTransportBuilder::localhost().unwrap().security_level(SecurityLevel::Opportunistic).build();\n// Send the email\nlet result = mailer.send(email);\n\nassert!(result.is_ok());\n\n Complete example\n\nuse lettre::smtp::{SecurityLevel, SmtpTransport,\nSmtpTransportBuilder};\nuse lettre::smtp::authentication::Mechanism;\nuse lettre::smtp::SUBMISSION_PORT;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nlet email = SimpleSendableEmail::new(\n \"user@localhost\",\n vec![\"root@localhost\"],\n \"message_id\",\n \"Hello world\"\n );\n\n// Connect to a remote server on a custom port\nlet mut mailer = SmtpTransportBuilder::new((\"server.tld\",\nSUBMISSION_PORT)).unwrap()\n // Set the name sent during EHLO/HELO, default is localhost\n .hello_name(\"my.hostname.tld\")\n // Add credentials for authentication\n .credentials(\"username\", \"password\")\n // Specify a TLS security level. You can also specify an SslContext with\n // .ssl_context(SslContext::Ssl23)\n .security_level(SecurityLevel::AlwaysEncrypt)\n // Enable SMTPUTF8 if the server supports it\n .smtp_utf8(true)\n // Configure expected authentication mechanism\n .authentication_mechanism(Mechanism::CramMd5)\n // Enable connection reuse\n .connection_reuse(true).build();\n\nlet result_1 = mailer.send(email.clone());\nassert!(result1.isok());\n\n// The second email will use the same connection\nlet result_2 = mailer.send(email);\nassert!(result2.isok());\n\n// Explicitly close the SMTP transaction as we enabled connection reuse\nmailer.close();\n\nLower level\n\nYou can also send commands, here is a simple email transaction without\nerror handling:\n\nuse lettre::smtp::SMTP_PORT;\nuse lettre::smtp::client::Client;\nuse lettre::smtp::client::net::NetworkStream;\n\nlet mut email_client: ClientNetworkStream = Client::new();\nlet _ = emailclient.connect(&(\"localhost\", SMTPPORT), None);\nlet _ = emailclient.ehlo(\"myhostname\");\nlet _ = email_client.mail(\"user@example.com\", None);\nlet _ = email_client.rcpt(\"user@example.org\");\nlet _ = email_client.data();\nlet _ = email_client.message(\"Test email\");\nlet _ = email_client.quit();\n\n",
"tags": [] "tags": []
}, },
{ {
"uri": "/content/sending-messages/stub", "uri": "/content/sending-messages/stub",
"title": "Stub transport", "title": "Stub transport",
"content": "\nThe stub transport only logs message envelope and drops the content. It can be useful for\ntesting purposes.\n\nuse lettre::stub::StubEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};\n\nlet email = SimpleSendableEmail::new(\n EmailAddress::new(\"user@localhost\".to_string()),\n vec![EmailAddress::new(\"root@localhost\".to_string())],\n \"messageid\".tostring(),\n \"Hello world\".to_string(),\n );\n\nlet mut sender = StubEmailTransport::new_positive();\nlet result = sender.send(&email);\nassert!(result.is_ok());\n\nWill log (when using a logger like env_logger):\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\n", "content": "\nThe stub transport only logs message envelope and drops the content. It can be useful for\ntesting purposes.\n\nuse lettre::stub::StubEmailTransport;\nuse lettre::{SimpleSendableEmail, EmailTransport};\n\nlet email = SimpleSendableEmail::new(\n \"user@localhost\",\n vec![\"root@localhost\"],\n \"message_id\",\n \"Hello world\"\n );\n\nlet mut sender = StubEmailTransport;\nlet result = sender.send(email);\nassert!(result.is_ok());\n\nWill log the line:\n\nb7c211bc-9811-45ce-8cd9-68eab575d695: from=user@localhost to=root@localhost\n`",
"tags": [] "tags": []
} }
] ]

273
docs/mermaid/mermaid.css Normal file
View File

@@ -0,0 +1,273 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.mermaid .label {
color: #333;
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: #ECECFF;
stroke: #CCCCFF;
stroke-width: 1px;
}
.edgePath .path {
stroke: #333333;
}
.edgeLabel {
background-color: #e8e8e8;
}
.cluster rect {
fill: #ffffde !important;
rx: 4 !important;
stroke: #aaaa33 !important;
stroke-width: 1px !important;
}
.cluster text {
fill: #333;
}
.actor {
stroke: #CCCCFF;
fill: #ECECFF;
}
text.actor {
fill: black;
stroke: none;
}
.actor-line {
stroke: grey;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
#arrowhead {
fill: #333;
}
#crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
.messageText {
fill: #333;
stroke: none;
}
.labelBox {
stroke: #CCCCFF;
fill: #ECECFF;
}
.labelText {
fill: black;
stroke: none;
}
.loopText {
fill: black;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #CCCCFF;
}
.note {
stroke: #aaaa33;
fill: #fff5ad;
}
.noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.section {
stroke: none;
opacity: 0.2;
}
.section0 {
fill: rgba(102, 102, 255, 0.49);
}
.section2 {
fill: #fff400;
}
.section1,
.section3 {
fill: white;
opacity: 0.2;
}
.sectionTitle0 {
fill: #333;
}
.sectionTitle1 {
fill: #333;
}
.sectionTitle2 {
fill: #333;
}
.sectionTitle3 {
fill: #333;
}
.sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 2;
}
.taskText {
text-anchor: middle;
font-size: 11px;
}
.taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
.taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.taskText0,
.taskText1,
.taskText2,
.taskText3 {
fill: white;
}
.task0,
.task1,
.task2,
.task3 {
fill: #8a90dd;
stroke: #534fbc;
}
.taskTextOutside0,
.taskTextOutside2 {
fill: black;
}
.taskTextOutside1,
.taskTextOutside3 {
fill: black;
}
/* Active task */
.active0,
.active1,
.active2,
.active3 {
fill: #bfc7ff;
stroke: #534fbc;
}
.activeText0,
.activeText1,
.activeText2,
.activeText3 {
fill: black !important;
}
/* Completed task */
.done0,
.done1,
.done2,
.done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
.doneText0,
.doneText1,
.doneText2,
.doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
.crit0,
.crit1,
.crit2,
.crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
.activeCrit0,
.activeCrit1,
.activeCrit2,
.activeCrit3 {
stroke: #ff8888;
fill: #bfc7ff;
stroke-width: 2;
}
.doneCrit0,
.doneCrit1,
.doneCrit2,
.doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: black !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: black !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #ffffde;
border: 1px solid #aaaa33;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

View File

@@ -0,0 +1,275 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.mermaid .label {
color: #323D47;
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: #BDD5EA;
stroke: #81B1DB;
stroke-width: 1px;
}
.edgePath .path {
stroke: lightgrey;
}
.edgeLabel {
background-color: #e8e8e8;
}
.cluster rect {
fill: #6D6D65 !important;
rx: 4 !important;
stroke: rgba(255, 255, 255, 0.25) !important;
stroke-width: 1px !important;
}
.cluster text {
fill: #F9FFFE;
}
.actor {
stroke: #81B1DB;
fill: #BDD5EA;
}
text.actor {
fill: black;
stroke: none;
}
.actor-line {
stroke: lightgrey;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: lightgrey;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: lightgrey;
}
#arrowhead {
fill: lightgrey !important;
}
#crosshead path {
fill: lightgrey !important;
stroke: lightgrey !important;
}
.messageText {
fill: lightgrey;
stroke: none;
}
.labelBox {
stroke: #81B1DB;
fill: #BDD5EA;
}
.labelText {
fill: #323D47;
stroke: none;
}
.loopText {
fill: lightgrey;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #81B1DB;
}
.note {
stroke: rgba(255, 255, 255, 0.25);
fill: #fff5ad;
}
.noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.section {
stroke: none;
opacity: 0.2;
}
.section0 {
fill: rgba(255, 255, 255, 0.3);
}
.section2 {
fill: #EAE8B9;
}
.section1,
.section3 {
fill: white;
opacity: 0.2;
}
.sectionTitle0 {
fill: #F9FFFE;
}
.sectionTitle1 {
fill: #F9FFFE;
}
.sectionTitle2 {
fill: #F9FFFE;
}
.sectionTitle3 {
fill: #F9FFFE;
}
.sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.grid .tick {
stroke: rgba(255, 255, 255, 0.3);
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid .tick text {
fill: lightgrey;
opacity: 0.5;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: #DB5757;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 1;
}
.taskText {
text-anchor: middle;
font-size: 11px;
}
.taskTextOutsideRight {
fill: #323D47;
text-anchor: start;
font-size: 11px;
}
.taskTextOutsideLeft {
fill: #323D47;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.taskText0,
.taskText1,
.taskText2,
.taskText3 {
fill: #323D47;
}
.task0,
.task1,
.task2,
.task3 {
fill: #BDD5EA;
stroke: rgba(255, 255, 255, 0.5);
}
.taskTextOutside0,
.taskTextOutside2 {
fill: lightgrey;
}
.taskTextOutside1,
.taskTextOutside3 {
fill: lightgrey;
}
/* Active task */
.active0,
.active1,
.active2,
.active3 {
fill: #81B1DB;
stroke: rgba(255, 255, 255, 0.5);
}
.activeText0,
.activeText1,
.activeText2,
.activeText3 {
fill: #323D47 !important;
}
/* Completed task */
.done0,
.done1,
.done2,
.done3 {
fill: lightgrey;
}
.doneText0,
.doneText1,
.doneText2,
.doneText3 {
fill: #323D47 !important;
}
/* Tasks on the critical line */
.crit0,
.crit1,
.crit2,
.crit3 {
stroke: #E83737;
fill: #E83737;
stroke-width: 2;
}
.activeCrit0,
.activeCrit1,
.activeCrit2,
.activeCrit3 {
stroke: #E83737;
fill: #81B1DB;
stroke-width: 2;
}
.doneCrit0,
.doneCrit1,
.doneCrit2,
.doneCrit3 {
stroke: #E83737;
fill: lightgrey;
stroke-width: 1;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: lightgrey !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: #323D47 !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: lightgrey;
}
/*
*/
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #6D6D65;
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

View File

@@ -0,0 +1,353 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
.mermaid .label {
font-family: 'trebuchet ms', verdana, arial;
color: #333;
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: #cde498;
stroke: #13540c;
stroke-width: 1px;
}
.edgePath .path {
stroke: green;
stroke-width: 1.5px;
}
.edgeLabel {
background-color: #e8e8e8;
}
.cluster rect {
fill: #cdffb2 !important;
rx: 4 !important;
stroke: #6eaa49 !important;
stroke-width: 1px !important;
}
.cluster text {
fill: #333;
}
.actor {
stroke: #13540c;
fill: #cde498;
}
text.actor {
fill: black;
stroke: none;
}
.actor-line {
stroke: grey;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
#arrowhead {
fill: #333;
}
#crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
.messageText {
fill: #333;
stroke: none;
}
.labelBox {
stroke: #326932;
fill: #cde498;
}
.labelText {
fill: black;
stroke: none;
}
.loopText {
fill: black;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #326932;
}
.note {
stroke: #6eaa49;
fill: #fff5ad;
}
.noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
.section {
stroke: none;
opacity: 0.2;
}
.section0 {
fill: #6eaa49;
}
.section2 {
fill: #6eaa49;
}
.section1,
.section3 {
fill: white;
opacity: 0.2;
}
.sectionTitle0 {
fill: #333;
}
.sectionTitle1 {
fill: #333;
}
.sectionTitle2 {
fill: #333;
}
.sectionTitle3 {
fill: #333;
}
.sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
.grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 2;
}
.taskText {
text-anchor: middle;
font-size: 11px;
}
.taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
.taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
.taskText0,
.taskText1,
.taskText2,
.taskText3 {
fill: white;
}
.task0,
.task1,
.task2,
.task3 {
fill: #487e3a;
stroke: #13540c;
}
.taskTextOutside0,
.taskTextOutside2 {
fill: black;
}
.taskTextOutside1,
.taskTextOutside3 {
fill: black;
}
/* Active task */
.active0,
.active1,
.active2,
.active3 {
fill: #cde498;
stroke: #13540c;
}
.activeText0,
.activeText1,
.activeText2,
.activeText3 {
fill: black !important;
}
/* Completed task */
.done0,
.done1,
.done2,
.done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
.doneText0,
.doneText1,
.doneText2,
.doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
.crit0,
.crit1,
.crit2,
.crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
.activeCrit0,
.activeCrit1,
.activeCrit2,
.activeCrit3 {
stroke: #ff8888;
fill: #cde498;
stroke-width: 2;
}
.doneCrit0,
.doneCrit1,
.doneCrit2,
.doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: black !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: black !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
g.classGroup text {
fill: #13540c;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
g.classGroup rect {
fill: #cde498;
stroke: #13540c;
}
g.classGroup line {
stroke: #13540c;
stroke-width: 1;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: #cde498;
opacity: 0.5;
}
svg .classLabel .label {
fill: #13540c;
}
.relation {
stroke: #13540c;
stroke-width: 1;
fill: none;
}
.composition {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#compositionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#compositionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
.aggregation {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#aggregationStart {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#aggregationEnd {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
#dependencyStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#dependencyEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#extensionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
#extensionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #cdffb2;
border: 1px solid #6eaa49;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

30
docs/mermaid/mermaid.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>File transport :: Lettre site</title>
<title>File transport</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/file/"> <body class="" data-url="/sending-messages/file/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item active" data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item active">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/sending-messages/" itemprop="url"><span itemprop="title">Sending messages</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> File transport</span>
<a href='/'>Lettre site</a> > <a href='/sending-messages/'>Sending messages</a> > File transport
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -260,93 +370,61 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
<h1>File transport</h1> <h1>File transport</h1>
<p>The file transport writes the emails to the given directory. The name of the file will be <p>The file transport writes the emails to the given directory. The name of the file will be
<code>message_id.txt</code>. <code>message_id.txt</code>.
It can be useful for testing purposes, or if you want to keep track of sent messages.</p> It can be useful for testing purposes, or if you want to keep track of sent messages.</p>
use std::env::temp_dir; <div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">crate</span> lettre;
use lettre::file::FileEmailTransport; <span style="color:#66d9ef">use</span> std::env::temp_dir;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
// Write to the local temp directory <span style="color:#66d9ef">use</span> lettre::file::FileEmailTransport;
let mut sender = FileEmailTransport::new(temp_dir()); <span style="color:#66d9ef">use</span> lettre::{SimpleSendableEmail, EmailTransport};
let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()),
vec![EmailAddress::new("root@localhost".to_string())],
"message_id".to_string(),
"Hello world".to_string(),
);
let result = sender.send(&email); <span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">main</span>() {
assert!(result.is_ok()); <span style="color:#75715e">// Write to the local temp directory
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">let</span> <span style="color:#66d9ef">mut</span> sender <span style="color:#f92672">=</span> FileEmailTransport::new(temp_dir());
<span style="color:#66d9ef">let</span> email <span style="color:#f92672">=</span> SimpleSendableEmail::new(
<span style="color:#e6db74">&#34;user@localhost&#34;</span>.to_string(),
<span style="color:#f92672">&amp;</span>[<span style="color:#e6db74">&#34;root@localhost&#34;</span>.to_string()],
<span style="color:#e6db74">&#34;message_id&#34;</span>.to_string(),
<span style="color:#e6db74">&#34;Hello world&#34;</span>.to_string(),
).unwrap();
<span style="color:#66d9ef">let</span> result <span style="color:#f92672">=</span> sender.send(<span style="color:#f92672">&amp;</span>email);
assert<span style="color:#f92672">!</span>(result.is_ok());
}</code></pre></div>
<p>Example result in <code>/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt</code>:</p> <p>Example result in <code>/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt</code>:</p>
b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost> <div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">b7c211bc-9811-45ce-8cd9-68eab575d695: from=&lt;user@localhost&gt; to=&lt;root@localhost&gt;
To: <root@localhost> To: &lt;root@localhost&gt;
From: <user@localhost> From: &lt;user@localhost&gt;
Subject: Hello Subject: Hello
Date: Sat, 31 Oct 2015 13:42:19 +0100 Date: Sat, 31 Oct 2015 13:42:19 +0100
Message-ID: <b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost> Message-ID: &lt;b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost&gt;
Hello World! Hello World!</code></pre></div>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -368,12 +446,6 @@ Hello World!
<a class="nav nav-prev" href="/lettre/sending-messages/sendmail/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/stub/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -383,23 +455,203 @@ Hello World!
<a class="nav nav-prev" href="/sending-messages/sendmail/" title="Sendmail transport"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/stub/" title="Stub transport" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -1,56 +1,66 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="js csstransforms3d"> <html lang="en" class="js csstransforms3d">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Sending messages :: Lettre site</title>
<title>Sending messages</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/"> <body class="" data-url="/sending-messages/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -62,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -126,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item active parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
active
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -214,13 +309,29 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
<div id="top-github-link">
<a class="github-link" href="https://github.com/lettre/lettre/edit/master/website/content/sending-messages/_index.md" target="blank">
<i class="fa fa-code-fork"></i>
Edit this page
</a>
</div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -228,6 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -235,17 +349,43 @@
<a href='/'>Lettre site</a> > Sending messages
<span itemprop="title"> Sending messages</span>
</span>
</div>
<div class="progress">
<div class="wrapper">
<nav id="TableOfContents">
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#sending-messages">Sending Messages</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
</div>
</div> </div>
</div> </div>
</div>
<div id="chapter">
<div id="body-inner"> <div id="body-inner">
<h1>Sending messages</h1>
@@ -254,55 +394,13 @@
<p>This section explains how to manipulate emails you have created.</p> <p>This section explains how to manipulate emails you have created.</p>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
</div>
<div id="navigation"> <div id="navigation">
@@ -317,12 +415,6 @@
<a class="nav nav-prev" href="/lettre/creating-messages/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/intro/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -340,26 +432,200 @@
<a class="nav nav-prev" href="/creating-messages/email/" title="Email creation"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/intro/" title="Introduction" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>
</html> </html>

View File

@@ -2,67 +2,67 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Sending messages on Lettre site</title> <title>Sending messages on Lettre site</title>
<link>https://lettre.github.io/lettre/sending-messages/</link> <link>http://docs.lettre.at/sending-messages/</link>
<description>Recent content in Sending messages on Lettre site</description> <description>Recent content in Sending messages on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate> <lastBuildDate>Sun, 21 May 2017 23:46:01 +0200</lastBuildDate>
<atom:link href="https://lettre.github.io/lettre/sending-messages/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/sending-messages/index.xml" rel="self" type="application/rss+xml" />
<item> <item>
<title>Introduction</title> <title>Introduction</title>
<link>https://lettre.github.io/lettre/sending-messages/intro/</link> <link>http://docs.lettre.at/sending-messages/intro/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/intro/</guid> <guid>http://docs.lettre.at/sending-messages/intro/</guid>
<description>This mailer contains several different transports for your emails. To be sendable, the emails have to implement SendableEmail, which is the case for emails created with lettre_email. <description>This mailer contains several different transports for your emails. To be sendable, the emails have to implement SendableEmail, which is the case for emails created with lettre_email.
The following transports are available: The following transports are available:
The SmtpTransport uses the SMTP protocol to send the message over the network. It is the prefered way of sending emails. The SendmailTransport uses the sendmail command to send messages. It is an alternative to the SMTP transport.</description> The SmtpTransport uses the SMTP protocol to send the message over the network. It is the preferred way of sending emails. The SendmailTransport uses the sendmail command to send messages. It is an alternative to the SMTP transport.</description>
</item> </item>
<item> <item>
<title>SMTP transport</title> <title>SMTP transport</title>
<link>https://lettre.github.io/lettre/sending-messages/smtp/</link> <link>http://docs.lettre.at/sending-messages/smtp/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/smtp/</guid> <guid>http://docs.lettre.at/sending-messages/smtp/</guid>
<description>This transport uses the SMTP protocol to send emails over the network (locally or remotely). <description>This transport uses the SMTP protocol to send emails over the network (locally or remotely).
It is desinged to be: It is designed to be:
Secured: email are encrypted by default Modern: Unicode support for email content and sender/recipient adresses when compatible Fast: supports tcp connection reuse This client is designed to send emails to a relay server, and should not be used to send emails directly to the destination. Secured: email are encrypted by default Modern: Unicode support for email content and sender/recipient addresses when compatible Fast: supports tcp connection reuse This client is designed to send emails to a relay server, and should not be used to send emails directly to the destination.
The relay server can be the local email server, a specific host or a third-party service.</description> The relay server can be the local email server, a specific host or a third-party service.</description>
</item> </item>
<item> <item>
<title>Sendmail transport</title> <title>Sendmail transport</title>
<link>https://lettre.github.io/lettre/sending-messages/sendmail/</link> <link>http://docs.lettre.at/sending-messages/sendmail/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/sendmail/</guid> <guid>http://docs.lettre.at/sending-messages/sendmail/</guid>
<description>The sendmail transport sends the email using the local sendmail command. <description>The sendmail transport sends the email using the local sendmail command.
use lettre::sendmail::SendmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let mut sender = SendmailTransport::new(); let result = sender.send(&amp;email); assert!(result.is_ok()); </description> extern crate lettre; use lettre::sendmail::SendmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).unwrap(); let mut sender = SendmailTransport::new(); let result = sender.send(&amp;amp;email); assert!(result.is_ok()); }</description>
</item> </item>
<item> <item>
<title>File transport</title> <title>File transport</title>
<link>https://lettre.github.io/lettre/sending-messages/file/</link> <link>http://docs.lettre.at/sending-messages/file/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/file/</guid> <guid>http://docs.lettre.at/sending-messages/file/</guid>
<description>The file transport writes the emails to the given directory. The name of the file will be message_id.txt. It can be useful for testing purposes, or if you want to keep track of sent messages. <description>The file transport writes the emails to the given directory. The name of the file will be message_id.txt. It can be useful for testing purposes, or if you want to keep track of sent messages.
use std::env::temp_dir; use lettre::file::FileEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; // Write to the local temp directory let mut sender = FileEmailTransport::new(temp_dir()); let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let result = sender.send(&amp;email); assert!(result.is_ok()); Example result in /tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.</description> extern crate lettre; use std::env::temp_dir; use lettre::file::FileEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { // Write to the local temp directory let mut sender = FileEmailTransport::new(temp_dir()); let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).</description>
</item> </item>
<item> <item>
<title>Stub transport</title> <title>Stub transport</title>
<link>https://lettre.github.io/lettre/sending-messages/stub/</link> <link>http://docs.lettre.at/sending-messages/stub/</link>
<pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate> <pubDate>Sun, 21 May 2017 23:46:17 +0200</pubDate>
<guid>https://lettre.github.io/lettre/sending-messages/stub/</guid> <guid>http://docs.lettre.at/sending-messages/stub/</guid>
<description>The stub transport only logs message envelope and drops the content. It can be useful for testing purposes. <description>The stub transport only logs message envelope and drops the content. It can be useful for testing purposes.
use lettre::stub::StubEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; let email = SimpleSendableEmail::new( EmailAddress::new(&#34;user@localhost&#34;.to_string()), vec![EmailAddress::new(&#34;root@localhost&#34;.to_string())], &#34;message_id&#34;.to_string(), &#34;Hello world&#34;.to_string(), ); let mut sender = StubEmailTransport::new_positive(); let result = sender.send(&amp;email); assert!(result.is_ok()); Will log (when using a logger like env_logger): extern crate lettre; use lettre::stub::StubEmailTransport; use lettre::{SimpleSendableEmail, EmailTransport}; fn main() { let email = SimpleSendableEmail::new( &amp;#34;user@localhost&amp;#34;.to_string(), &amp;amp;[&amp;#34;root@localhost&amp;#34;.to_string()], &amp;#34;message_id&amp;#34;.to_string(), &amp;#34;Hello world&amp;#34;.to_string(), ).unwrap(); let mut sender = StubEmailTransport::new_positive(); let result = sender.send(&amp;amp;email); assert!(result.is_ok()); } Will log (when using a logger like env_logger):
b7c211bc-9811-45ce-8cd9-68eab575d695: from= to= </description> b7c211bc-9811-45ce-8cd9-68eab575d695: from=&amp;lt;user@localhost&amp;gt; to=&amp;lt;root@localhost&amp;gt;</description>
</item> </item>
</channel> </channel>

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Introduction :: Lettre site</title>
<title>Introduction</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/intro/"> <body class="" data-url="/sending-messages/intro/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item active" data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item active">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/sending-messages/" itemprop="url"><span itemprop="title">Sending messages</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> Introduction</span>
<a href='/'>Lettre site</a> > <a href='/sending-messages/'>Sending messages</a> > Introduction
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -260,14 +370,19 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
<h1>Introduction</h1> <h1>Introduction</h1>
<p>This mailer contains several different transports for your emails. To be sendable, the <p>This mailer contains several different transports for your emails. To be sendable, the
emails have to implement <code>SendableEmail</code>, which is the case for emails created with <code>lettre_email</code>.</p> emails have to implement <code>SendableEmail</code>, which is the case for emails created with <code>lettre_email</code>.</p>
@@ -275,7 +390,7 @@ emails have to implement <code>SendableEmail</code>, which is the case for email
<ul> <ul>
<li>The <code>SmtpTransport</code> uses the SMTP protocol to send the message over the network. It is <li>The <code>SmtpTransport</code> uses the SMTP protocol to send the message over the network. It is
the prefered way of sending emails.</li> the preferred way of sending emails.</li>
<li>The <code>SendmailTransport</code> uses the sendmail command to send messages. It is an alternative to <li>The <code>SendmailTransport</code> uses the sendmail command to send messages. It is an alternative to
the SMTP transport.</li> the SMTP transport.</li>
<li>The <code>FileTransport</code> creates a file containing the email content to be sent. It can be used <li>The <code>FileTransport</code> creates a file containing the email content to be sent. It can be used
@@ -285,54 +400,15 @@ logs.</li>
</ul> </ul>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -348,12 +424,6 @@ logs.</li>
<a class="nav nav-prev" href="/lettre/sending-messages/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/smtp/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -369,23 +439,200 @@ logs.</li>
<a class="nav nav-prev" href="/sending-messages/" title="Sending messages"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/smtp/" title="SMTP transport" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Sendmail transport :: Lettre site</title>
<title>Sendmail transport</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/sendmail/"> <body class="" data-url="/sending-messages/sendmail/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item active" data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item active">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/sending-messages/" itemprop="url"><span itemprop="title">Sending messages</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> Sendmail transport</span>
<a href='/'>Lettre site</a> > <a href='/sending-messages/'>Sending messages</a> > Sendmail transport
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -260,78 +370,47 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
<h1>Sendmail transport</h1> <h1>Sendmail transport</h1>
<p>The sendmail transport sends the email using the local sendmail command.</p>
use lettre::sendmail::SendmailTransport;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
<p>The sendmail transport sends the email using the local sendmail command.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust,no_run" data-lang="rust,no_run">extern crate lettre;
use lettre::sendmail::SendmailTransport;
use lettre::{SimpleSendableEmail, EmailTransport};
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), &#34;user@localhost&#34;.to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &amp;[&#34;root@localhost&#34;.to_string()],
"message_id".to_string(), &#34;message_id&#34;.to_string(),
"Hello world".to_string(), &#34;Hello world&#34;.to_string(),
); ).unwrap();
let mut sender = SendmailTransport::new(); let mut sender = SendmailTransport::new();
let result = sender.send(&email); let result = sender.send(&amp;email);
assert!(result.is_ok()); assert!(result.is_ok());
}</code></pre></div>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -351,12 +430,6 @@ assert!(result.is_ok());
<a class="nav nav-prev" href="/lettre/sending-messages/smtp/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/file/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -368,23 +441,203 @@ assert!(result.is_ok());
<a class="nav nav-prev" href="/sending-messages/smtp/" title="SMTP transport"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/file/" title="File transport" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>SMTP transport :: Lettre site</title>
<title>SMTP transport</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/smtp/"> <body class="" data-url="/sending-messages/smtp/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item active" data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item active">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/sending-messages/" itemprop="url"><span itemprop="title">Sending messages</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> SMTP transport</span>
<a href='/'>Lettre site</a> > <a href='/sending-messages/'>Sending messages</a> > SMTP transport
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -275,8 +385,10 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
@@ -285,13 +397,16 @@
<p>This transport uses the SMTP protocol to send emails over the network (locally or remotely).</p> <p>This transport uses the SMTP protocol to send emails over the network (locally or remotely).</p>
<p>It is desinged to be:</p> <p>It is designed to be:</p>
<ul> <ul>
<li>Secured: email are encrypted by default</li> <li>Secured: email are encrypted by default</li>
<li>Modern: Unicode support for email content and sender/recipient adresses when compatible</li> <li>Modern: Unicode support for email content and sender/recipient addresses when compatible</li>
<li>Fast: supports tcp connection reuse</li> <li>Fast: supports tcp connection reuse</li>
</ul> </ul>
@@ -303,44 +418,48 @@ emails directly to the destination.</p>
<h4 id="simple-example">Simple example</h4> <h4 id="simple-example">Simple example</h4>
<p>This is the most basic example of usage:</p> <p>This is the most basic example of usage:</p>
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport}; <div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust,no_run" data-lang="rust,no_run">extern crate lettre;
use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), &#34;user@localhost&#34;.to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &amp;[&#34;root@localhost&#34;.to_string()],
"message_id".to_string(), &#34;message_id&#34;.to_string(),
"Hello world".to_string(), &#34;Hello world&#34;.to_string(),
); ).unwrap();
// Open a local connection on port 25 // Open a local connection on port 25
let mut mailer = let mut mailer =
SmtpTransport::builder_unencrypted_localhost().unwrap().build(); SmtpTransport::builder_unencrypted_localhost().unwrap().build();
// Send the email // Send the email
let result = mailer.send(&email); let result = mailer.send(&amp;email);
assert!(result.is_ok()); assert!(result.is_ok());
}</code></pre></div>
<h4 id="complete-example">Complete example</h4> <h4 id="complete-example">Complete example</h4>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust,no_run" data-lang="rust,no_run">extern crate lettre;
use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::smtp::SUBMISSION_PORT; use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};
use lettre::smtp::extension::ClientId; use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters; use lettre::smtp::ConnectionReuseParameters;
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), &#34;user@localhost&#34;.to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &amp;[&#34;root@localhost&#34;.to_string()],
"message_id".to_string(), &#34;message_id&#34;.to_string(),
"Hello world".to_string(), &#34;Hello world&#34;.to_string(),
); ).unwrap();
// Connect to a remote server on a custom port // Connect to a remote server on a custom port
let mut mailer = SmtpTransport::simple_builder("server.tld".to_string()).unwrap() let mut mailer = SmtpTransport::simple_builder(&#34;server.tld&#34;).unwrap()
// Set the name sent during EHLO/HELO, default is `localhost` // Set the name sent during EHLO/HELO, default is `localhost`
.hello_name(ClientId::Domain("my.hostname.tld".to_string())) .hello_name(ClientId::Domain(&#34;my.hostname.tld&#34;.to_string()))
// Add credentials for authentication // Add credentials for authentication
.credentials(Credentials::new("username".to_string(), "password".to_string())) .credentials(Credentials::new(&#34;username&#34;.to_string(), &#34;password&#34;.to_string()))
// Enable SMTPUTF8 if the server supports it // Enable SMTPUTF8 if the server supports it
.smtp_utf8(true) .smtp_utf8(true)
// Configure expected authentication mechanism // Configure expected authentication mechanism
@@ -348,20 +467,22 @@ let mut mailer = SmtpTransport::simple_builder("server.tld".to_string()).unwrap(
// Enable connection reuse // Enable connection reuse
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build(); .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build();
let result_1 = mailer.send(&email); let result_1 = mailer.send(&amp;email);
assert!(result_1.is_ok()); assert!(result_1.is_ok());
// The second email will use the same connection // The second email will use the same connection
let result_2 = mailer.send(&email); let result_2 = mailer.send(&amp;email);
assert!(result_2.is_ok()); assert!(result_2.is_ok());
// Explicitly close the SMTP transaction as we enabled connection reuse // Explicitly close the SMTP transaction as we enabled connection reuse
mailer.close(); mailer.close();
}</code></pre></div>
<h4 id="lower-level">Lower level</h4> <h4 id="lower-level">Lower level</h4>
<p>You can also send commands, here is a simple email transaction without <p>You can also send commands, here is a simple email transaction without
error handling:</p> error handling:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust,no_run" data-lang="rust,no_run">extern crate lettre;
use lettre::EmailAddress; use lettre::EmailAddress;
use lettre::smtp::SMTP_PORT; use lettre::smtp::SMTP_PORT;
use lettre::smtp::client::Client; use lettre::smtp::client::Client;
@@ -369,68 +490,30 @@ use lettre::smtp::client::net::NetworkStream;
use lettre::smtp::extension::ClientId; use lettre::smtp::extension::ClientId;
use lettre::smtp::commands::*; use lettre::smtp::commands::*;
let mut email_client: Client<NetworkStream> = Client::new(); fn main() {
let _ = email_client.connect(&("localhost", SMTP_PORT), None); let mut email_client: Client&lt;NetworkStream&gt; = Client::new();
let _ = email_client.smtp_command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); let _ = email_client.connect(&amp;(&#34;localhost&#34;, SMTP_PORT), None);
let _ = email_client.smtp_command( let _ = email_client.command(EhloCommand::new(ClientId::new(&#34;my_hostname&#34;.to_string())));
MailCommand::new(Some(EmailAddress::new("user@example.com".to_string())), vec![]) let _ = email_client.command(
MailCommand::new(Some(EmailAddress::new(&#34;user@example.com&#34;.to_string()).unwrap()), vec![])
); );
let _ = email_client.smtp_command( let _ = email_client.command(
RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![]) RcptCommand::new(EmailAddress::new(&#34;user@example.org&#34;.to_string()).unwrap(), vec![])
); );
let _ = email_client.smtp_command(DataCommand); let _ = email_client.command(DataCommand);
let _ = email_client.message(Box::new("Test email".as_bytes())); let _ = email_client.message(Box::new(&#34;Test email&#34;.as_bytes()));
let _ = email_client.smtp_command(QuitCommand); let _ = email_client.command(QuitCommand);
}</code></pre></div>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -448,12 +531,6 @@ let _ = email_client.smtp_command(QuitCommand);
<a class="nav nav-prev" href="/lettre/sending-messages/intro/"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/lettre/sending-messages/sendmail/" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
@@ -467,23 +544,203 @@ let _ = email_client.smtp_command(QuitCommand);
<a class="nav nav-prev" href="/sending-messages/intro/" title="Introduction"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/sendmail/" title="Sendmail transport" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -3,53 +3,64 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.20.7" /> <meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust"> <meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset"> <meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="https://lettre.github.io/lettre//images/favicon.png" type="image/x-icon" /> <link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Stub transport :: Lettre site</title>
<title>Stub transport</title> <link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/nucleus.css" rel="stylesheet"> <link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/font-awesome.min.css" rel="stylesheet"> <link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hybrid.css" rel="stylesheet"> <link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/featherlight.min.css" rel="stylesheet"> <link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/perfect-scrollbar.min.css" rel="stylesheet"> <link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/horsey.css" rel="stylesheet"> <link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/theme.css" rel="stylesheet"> <link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<link href="https://lettre.github.io/lettre//css/hugo-theme.css" rel="stylesheet">
<script src="https://lettre.github.io/lettre//js/jquery-2.x.min.js"></script>
<style type="text/css">:root #header + #content > #left > #rlblock_left
{display:none !important;}</style>
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head> </head>
<body class="" data-url="/lettre/sending-messages/stub/"> <body class="" data-url="/sending-messages/stub/">
<nav id="sidebar" class="">
<nav id="sidebar">
<div id="header-wrapper"> <div id="header-wrapper">
<div id="header"> <div id="header">
<a href="https://lettre.github.io/lettre//getting-started/intro/"><img src="https://lettre.github.io/lettre//images/logo50.png" /></a> <a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label> <label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search"> <input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span> <span data-search-clear=""><i class="fa fa-close"></i></span>
</div> </div>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/lunr.min.js"></script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/horsey.js"></script> <script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript"> <script type="text/javascript">
var baseurl = "https:\/\/lettre.github.io\/lettre\/";
var baseurl = "http:\/\/docs.lettre.at\/";
</script> </script>
<script type="text/javascript" src="https://lettre.github.io/lettre//js/search.js"></script> <script type="text/javascript" src="/js/search.js?1522603726"></script>
</div> </div>
<div class="highlightable"> <div class="highlightable">
<ul class="topics"> <ul class="topics">
@@ -61,57 +72,44 @@
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
<li class="dd-item " data-nav-id="/lettre/getting-started/">
<a href="/lettre/getting-started/">
<span>
<b>1. </b>
Getting started Getting started
</span>
</a> </a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/getting-started/intro/">
<a href="/lettre/getting-started/intro/">
<span>Introduction </i></span>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a> </a>
</li> </li>
</ul> </ul>
</li> </li>
@@ -125,86 +123,184 @@
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<li class="dd-item " data-nav-id="/lettre/creating-messages/"> <a href="/creating-messages/">
<a href="/lettre/creating-messages/">
<span>
<b>2. </b>
Creating messages Creating messages
</span>
</a> </a>
</li>
<li class="dd-item parent" data-nav-id="/lettre/sending-messages/">
<a href="/lettre/sending-messages/">
<span>
<b>3. </b>
Sending messages
</span>
</a>
<ul> <ul>
<li class="dd-item " data-nav-id="/lettre/sending-messages/intro/">
<a href="/lettre/sending-messages/intro/">
<span>Introduction </i></span>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a> </a>
</li> </li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/smtp/">
<a href="/lettre/sending-messages/smtp/">
<span>SMTP transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/sendmail/">
<a href="/lettre/sending-messages/sendmail/">
<span>Sendmail transport </i></span>
</a>
</li>
<li class="dd-item " data-nav-id="/lettre/sending-messages/file/">
<a href="/lettre/sending-messages/file/">
<span>File transport </i></span>
</a>
</li>
<li class="dd-item active" data-nav-id="/lettre/sending-messages/stub/">
<a href="/lettre/sending-messages/stub/">
<span>Stub transport </i></span>
</a>
</li>
</ul> </ul>
</li> </li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
parent
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item active">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul> </ul>
<hr>
</li>
</ul>
<section id="footer"> <section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p> <p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
@@ -213,11 +309,15 @@
</div> </div>
</nav> </nav>
<section id="body"> <section id="body">
<div id="overlay"></div> <div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="padding highlightable"> <div class="sticky-spacer">
<div id="top-bar"> <div id="top-bar">
@@ -231,6 +331,7 @@
</div> </div>
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"> <div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span"> <span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle=""> <a href="#" id="sidebar-toggle" data-sidebar-toggle="">
@@ -238,7 +339,9 @@
</a> </a>
</span> </span>
<span id="toc-menu"><a href=""><i class="fa fa-list-alt"></i></a></span> <span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
@@ -246,11 +349,18 @@
<a href="/lettre/sending-messages/" itemprop="url"><span itemprop="title">Sending messages</span></a> <i class="fa fa-angle-right"></i>
<span itemprop="title"> Stub transport</span>
<a href='/'>Lettre site</a> > <a href='/sending-messages/'>Sending messages</a> > Stub transport
</span>
</div> </div>
<div class="progress"> <div class="progress">
@@ -260,82 +370,50 @@
</div> </div>
</div> </div>
</div>
<div id="body-inner"> <div id="body-inner">
<h1>Stub transport</h1> <h1>Stub transport</h1>
<p>The stub transport only logs message envelope and drops the content. It can be useful for <p>The stub transport only logs message envelope and drops the content. It can be useful for
testing purposes.</p> testing purposes.</p>
use lettre::stub::StubEmailTransport; <div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#66d9ef">extern</span> <span style="color:#66d9ef">crate</span> lettre;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
let email = SimpleSendableEmail::new( <span style="color:#66d9ef">use</span> lettre::stub::StubEmailTransport;
EmailAddress::new("user@localhost".to_string()), <span style="color:#66d9ef">use</span> lettre::{SimpleSendableEmail, EmailTransport};
vec![EmailAddress::new("root@localhost".to_string())],
"message_id".to_string(),
"Hello world".to_string(),
);
let mut sender = StubEmailTransport::new_positive(); <span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">main</span>() {
let result = sender.send(&email); <span style="color:#66d9ef">let</span> email <span style="color:#f92672">=</span> SimpleSendableEmail::new(
assert!(result.is_ok()); <span style="color:#e6db74">&#34;user@localhost&#34;</span>.to_string(),
<span style="color:#f92672">&amp;</span>[<span style="color:#e6db74">&#34;root@localhost&#34;</span>.to_string()],
<span style="color:#e6db74">&#34;message_id&#34;</span>.to_string(),
<span style="color:#e6db74">&#34;Hello world&#34;</span>.to_string(),
).unwrap();
<span style="color:#66d9ef">let</span> <span style="color:#66d9ef">mut</span> sender <span style="color:#f92672">=</span> StubEmailTransport::new_positive();
<span style="color:#66d9ef">let</span> result <span style="color:#f92672">=</span> sender.send(<span style="color:#f92672">&amp;</span>email);
assert<span style="color:#f92672">!</span>(result.is_ok());
}</code></pre></div>
<p>Will log (when using a logger like <code>env_logger</code>):</p> <p>Will log (when using a logger like <code>env_logger</code>):</p>
b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost> <div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">b7c211bc-9811-45ce-8cd9-68eab575d695: from=&lt;user@localhost&gt; to=&lt;root@localhost&gt;</code></pre></div>
<footer class=" footline" >
</footer>
</div> </div>
</div> </div>
<div id="navigation"> <div id="navigation">
@@ -359,7 +437,6 @@ b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>
<a class="nav nav-prev" href="/lettre/sending-messages/file/"> <i class="fa fa-chevron-left"></i></a>
@@ -370,23 +447,197 @@ b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>
<a class="nav nav-prev" href="/sending-messages/file/" title="File transport"> <i class="fa fa-chevron-left"></i></a>
<a class="nav nav-next" href="/sending-messages/intro/" title="Introduction" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div> </div>
</section> </section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"> <div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div> <div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div> </div>
<script src="https://lettre.github.io/lettre//js/clipboard.min.js"></script> <script src="/js/clipboard.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.min.js"></script> <script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/perfect-scrollbar.jquery.min.js"></script> <script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/jquery.sticky-kit.min.js"></script> <script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/featherlight.min.js"></script> <script src="/js/featherlight.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/html5shiv-printshiv.min.js"></script> <script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/highlight.pack.js"></script> <script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script> <script>hljs.initHighlightingOnLoad();</script>
<script src="https://lettre.github.io/lettre//js/modernizr.custom.71422.js"></script> <script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/learn.js"></script> <script src="/js/learn.js?1522603726"></script>
<script src="https://lettre.github.io/lettre//js/hugo-learn.js"></script> <script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body> </body>

View File

@@ -1,64 +1,70 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url> <url>
<loc>https://lettre.github.io/lettre/getting-started/intro/</loc> <loc>http://docs.lettre.at/getting-started/intro/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/intro/</loc> <loc>http://docs.lettre.at/sending-messages/intro/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/getting-started/</loc> <loc>http://docs.lettre.at/getting-started/</loc>
<lastmod>2017-05-21T23:46:01+02:00</lastmod> <lastmod>2017-05-21T23:46:01+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/smtp/</loc> <loc>http://docs.lettre.at/sending-messages/smtp/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/creating-messages/</loc> <loc>http://docs.lettre.at/creating-messages/</loc>
<lastmod>2017-05-21T23:46:01+02:00</lastmod> <lastmod>2017-05-21T23:46:01+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/sendmail/</loc> <loc>http://docs.lettre.at/sending-messages/sendmail/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/</loc> <loc>http://docs.lettre.at/sending-messages/</loc>
<lastmod>2017-05-21T23:46:01+02:00</lastmod> <lastmod>2017-05-21T23:46:01+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/file/</loc> <loc>http://docs.lettre.at/creating-messages/email/</loc>
<lastmod>2018-01-21T23:46:17+02:00</lastmod>
</url>
<url>
<loc>http://docs.lettre.at/sending-messages/file/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/sending-messages/stub/</loc> <loc>http://docs.lettre.at/sending-messages/stub/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/categories/</loc> <loc>http://docs.lettre.at/categories/</loc>
<priority>0</priority> <priority>0</priority>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/</loc> <loc>http://docs.lettre.at/</loc>
<lastmod>2017-05-21T23:46:17+02:00</lastmod> <lastmod>2017-05-21T23:46:17+02:00</lastmod>
<priority>0</priority> <priority>0</priority>
</url> </url>
<url> <url>
<loc>https://lettre.github.io/lettre/tags/</loc> <loc>http://docs.lettre.at/tags/</loc>
<priority>0</priority> <priority>0</priority>
</url> </url>

593
docs/tags/index.html Normal file
View File

@@ -0,0 +1,593 @@
<!DOCTYPE html>
<html lang="en" class="js csstransforms3d">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="generator" content="Hugo 0.37.1" />
<meta name="description" content="Documentation for the Lettre mailer in Rust">
<meta name="author" content="Alexis Mousset">
<link rel="shortcut icon" href="http://docs.lettre.at//images/favicon.png" type="image/x-icon" />
<title>Tags :: Lettre site</title>
<link href="/css/nucleus.css?1522603726" rel="stylesheet">
<link href="/css/font-awesome.min.css?1522603726" rel="stylesheet">
<link href="/css/hybrid.css?1522603726" rel="stylesheet">
<link href="/css/featherlight.min.css?1522603726" rel="stylesheet">
<link href="/css/perfect-scrollbar.min.css?1522603726" rel="stylesheet">
<link href="/css/auto-complete.css?1522603726" rel="stylesheet">
<link href="/css/theme.css?1522603726" rel="stylesheet">
<link href="/css/hugo-theme.css?1522603726" rel="stylesheet">
<script src="/js/jquery-2.x.min.js?1522603726"></script>
<style type="text/css">
:root #header + #content > #left > #rlblock_left{
display:none !important;
}
</style>
</head>
<body class="" data-url="/tags/">
<nav id="sidebar" class="">
<div id="header-wrapper">
<div id="header">
<a href="http://docs.lettre.at//getting-started/intro/"><img src="http://docs.lettre.at//images/logo50.png" /></a>
</div>
<div class="searchbox">
<label for="search-by"><i class="fa fa-search"></i></label>
<input data-search-input id="search-by" type="text" placeholder="Search...">
<span data-search-clear=""><i class="fa fa-close"></i></span>
</div>
<script type="text/javascript" src="/js/lunr.min.js?1522603726"></script>
<script type="text/javascript" src="/js/auto-complete.js?1522603726"></script>
<script type="text/javascript">
var baseurl = "http:\/\/docs.lettre.at\/";
</script>
<script type="text/javascript" src="/js/search.js?1522603726"></script>
</div>
<div class="highlightable">
<ul class="topics">
<li data-nav-id="/getting-started/" title="Getting started" class="dd-item
">
<a href="/getting-started/">
Getting started
</a>
<ul>
<li data-nav-id="/getting-started/intro/" title="Introduction" class="dd-item ">
<a href="/getting-started/intro/">
Introduction
</a>
</li>
</ul>
</li>
<li data-nav-id="/creating-messages/" title="Creating messages" class="dd-item
">
<a href="/creating-messages/">
Creating messages
</a>
<ul>
<li data-nav-id="/creating-messages/email/" title="Email creation" class="dd-item ">
<a href="/creating-messages/email/">
Email creation
</a>
</li>
</ul>
</li>
<li data-nav-id="/sending-messages/" title="Sending messages" class="dd-item
">
<a href="/sending-messages/">
Sending messages
</a>
<ul>
<li data-nav-id="/sending-messages/intro/" title="Introduction" class="dd-item ">
<a href="/sending-messages/intro/">
Introduction
</a>
</li>
<li data-nav-id="/sending-messages/smtp/" title="SMTP transport" class="dd-item ">
<a href="/sending-messages/smtp/">
SMTP transport
</a>
</li>
<li data-nav-id="/sending-messages/sendmail/" title="Sendmail transport" class="dd-item ">
<a href="/sending-messages/sendmail/">
Sendmail transport
</a>
</li>
<li data-nav-id="/sending-messages/file/" title="File transport" class="dd-item ">
<a href="/sending-messages/file/">
File transport
</a>
</li>
<li data-nav-id="/sending-messages/stub/" title="Stub transport" class="dd-item ">
<a href="/sending-messages/stub/">
Stub transport
</a>
</li>
</ul>
</li>
</ul>
<section id="footer">
<p>Built with <a href="https://github.com/matcornic/hugo-theme-learn"><i class="fa fa-heart"></i></a> from <a href="http://getgrav.org">Grav</a> and <a href="http://gohugo.io/">Hugo</a></p>
</section>
</div>
</nav>
<section id="body">
<div id="overlay"></div>
<div class="padding highlightable sticky-parent">
<div class="sticky-spacer">
<div id="top-bar">
<div id="breadcrumbs" itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span id="sidebar-toggle-span">
<a href="#" id="sidebar-toggle" data-sidebar-toggle="">
<i class="fa fa-bars"></i>
</a>
</span>
<span id="toc-menu"><i class="fa fa-list-alt"></i></span>
<span class="links">
Tags
</span>
</div>
<div class="progress">
<div class="wrapper">
</div>
</div>
</div>
</div>
<div id="body-inner">
<h1>Tags</h1>
<footer class=" footline" >
</footer>
</div>
</div>
<div id="navigation">
<a class="nav nav-next" href="/getting-started/" title="Getting started" style="margin-right: 0px;"><i class="fa fa-chevron-right"></i></a>
</div>
</section>
<div style="left: -1000px; overflow: scroll; position: absolute; top: -1000px; border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;">
<div style="border: none; box-sizing: content-box; height: 200px; margin: 0px; padding: 0px; width: 200px;"></div>
</div>
<script src="/js/clipboard.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.min.js?1522603726"></script>
<script src="/js/perfect-scrollbar.jquery.min.js?1522603726"></script>
<script src="/js/jquery.sticky-kit.min.js?1522603726"></script>
<script src="/js/featherlight.min.js?1522603726"></script>
<script src="/js/html5shiv-printshiv.min.js?1522603726"></script>
<script src="/js/highlight.pack.js?1522603726"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script src="/js/modernizr.custom.71422.js?1522603726"></script>
<script src="/js/learn.js?1522603726"></script>
<script src="/js/hugo-learn.js?1522603726"></script>
<link href="/mermaid/mermaid.css?1522603726" type="text/css" rel="stylesheet" />
<script src="/mermaid/mermaid.js?1522603726"></script>
<script>
mermaid.initialize({ startOnLoad: true });
</script>
</body>
</html>

View File

@@ -2,12 +2,12 @@
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel> <channel>
<title>Tags on Lettre site</title> <title>Tags on Lettre site</title>
<link>https://lettre.github.io/lettre/tags/</link> <link>http://docs.lettre.at/tags/</link>
<description>Recent content in Tags on Lettre site</description> <description>Recent content in Tags on Lettre site</description>
<generator>Hugo -- gohugo.io</generator> <generator>Hugo -- gohugo.io</generator>
<language>en-us</language> <language>en-us</language>
<atom:link href="https://lettre.github.io/lettre/tags/index.xml" rel="self" type="application/rss+xml" /> <atom:link href="http://docs.lettre.at/tags/index.xml" rel="self" type="application/rss+xml" />
</channel> </channel>

View File

@@ -1,37 +0,0 @@
### v0.7.0 (2017-10-08)
#### Features
* **all**
* Split into the *lettre* and *lettre_email* crates
* A lot of small improvements
* **smtp transport**
* Use *tls-native* instead of *openssl*
* Allow validating server certificate
### v0.6.2 (2017-02-18)
#### Features
* **all**
* Update env-logger crate to 0.4
* Update openssl crate to 0.9
### v0.6.1 (2016-10-19)
#### Features
* **documentation**
* #91: Build seperate docs for each release
* #96: Add complete documentation information to README
#### Bugfixes
* **tests**
* #93: Force building tests before coverage computing
### v0.6.0 (2016-05-05)
Nothing.

1
lettre/CHANGELOG.md Symbolic link
View File

@@ -0,0 +1 @@
../CHANGELOG.md

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre" name = "lettre"
version = "0.7.0" version = "0.8.1" # remember to update html_root_url
description = "Email client" description = "Email client"
readme = "README.md" readme = "README.md"
documentation = "https://docs.rs/lettre/" documentation = "https://docs.rs/lettre/"
@@ -15,18 +15,21 @@ keywords = ["email", "smtp", "mailer"]
travis-ci = { repository = "lettre/lettre" } travis-ci = { repository = "lettre/lettre" }
[dependencies] [dependencies]
log = "^0.3" log = "^0.4"
nom = { version = "^3.2", optional = true }
bufstream = { version = "^0.1", optional = true } bufstream = { version = "^0.1", optional = true }
native-tls = { version = "^0.1", optional = true } native-tls = { version = "^0.1", optional = true }
base64 = { version = "^0.7", optional = true } base64 = { version = "^0.9", optional = true }
hex = { version = "^0.2", optional = true } hex = { version = "^0.3", optional = true }
hostname = { version = "^0.1", optional = true }
rust-crypto = { version = "^0.2", optional = true } rust-crypto = { version = "^0.2", optional = true }
serde = { version = "^1.0", optional = true } serde = { version = "^1.0", optional = true }
serde_json = { version = "^1.0", optional = true } serde_json = { version = "^1.0", optional = true }
serde_derive = { version = "^1.0", optional = true } serde_derive = { version = "^1.0", optional = true }
[dev-dependencies] [dev-dependencies]
env_logger = "^0.4" env_logger = "^0.5"
glob = "0.2"
[features] [features]
default = ["file-transport", "crammd5-auth", "smtp-transport", "sendmail-transport"] default = ["file-transport", "crammd5-auth", "smtp-transport", "sendmail-transport"]
@@ -34,7 +37,7 @@ unstable = []
serde-impls = ["serde", "serde_derive"] serde-impls = ["serde", "serde_derive"]
file-transport = ["serde-impls", "serde_json"] file-transport = ["serde-impls", "serde_json"]
crammd5-auth = ["rust-crypto", "hex"] crammd5-auth = ["rust-crypto", "hex"]
smtp-transport = ["bufstream", "native-tls", "base64"] smtp-transport = ["bufstream", "native-tls", "base64", "nom", "hostname"]
sendmail-transport = [] sendmail-transport = []
[[example]] [[example]]

View File

@@ -1,17 +1,17 @@
extern crate lettre;
extern crate env_logger; extern crate env_logger;
extern crate lettre;
use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail, SmtpTransport}; use lettre::{EmailTransport, SimpleSendableEmail, SmtpTransport};
fn main() { fn main() {
env_logger::init().unwrap(); env_logger::init();
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"file_id".to_string(), "my-message-id".to_string(),
"Hello ß☺ example".to_string(), "Hello ß☺ example".to_string(),
); ).unwrap();
// Open a local connection on port 25 // Open a local connection on port 25
let mut mailer = SmtpTransport::builder_unencrypted_localhost() let mut mailer = SmtpTransport::builder_unencrypted_localhost()

View File

@@ -1 +0,0 @@
../rustfmt.toml

View File

@@ -34,8 +34,8 @@ impl StdError for Error {
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&StdError> {
match *self { match *self {
Io(ref err) => Some(&*err as &StdError), Io(ref err) => Some(&*err),
JsonSerialization(ref err) => Some(&*err as &StdError), JsonSerialization(ref err) => Some(&*err),
_ => None, _ => None,
} }
} }

View File

@@ -2,36 +2,6 @@
//! `message_id.txt`. //! `message_id.txt`.
//! It can be useful for testing purposes, or if you want to keep track of sent messages. //! It can be useful for testing purposes, or if you want to keep track of sent messages.
//! //!
//! ```rust
//! use std::env::temp_dir;
//!
//! use lettre::file::FileEmailTransport;
//! use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
//!
//! // Write to the local temp directory
//! let mut sender = FileEmailTransport::new(temp_dir());
//! let email = SimpleSendableEmail::new(
//! EmailAddress::new("user@localhost".to_string()),
//! vec![EmailAddress::new("root@localhost".to_string())],
//! "message_id".to_string(),
//! "Hello world".to_string(),
//! );
//!
//! let result = sender.send(&email);
//! assert!(result.is_ok());
//! ```
//! Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`:
//!
//! ```text
//! b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>
//! To: <root@localhost>
//! From: <user@localhost>
//! Subject: Hello
//! Date: Sat, 31 Oct 2015 13:42:19 +0100
//! Message-ID: <b7c211bc-9811-45ce-8cd9-68eab575d695.lettre@localhost>
//!
//! Hello World!
//! ```
use EmailTransport; use EmailTransport;
use SendableEmail; use SendableEmail;
@@ -47,6 +17,7 @@ pub mod error;
/// Writes the content and the envelope information to a file /// Writes the content and the envelope information to a file
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct FileEmailTransport { pub struct FileEmailTransport {
path: PathBuf, path: PathBuf,
} }
@@ -70,16 +41,13 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, FileResult> for FileEmailTransport
let mut message_content = String::new(); let mut message_content = String::new();
let _ = email.message().read_to_string(&mut message_content); let _ = email.message().read_to_string(&mut message_content);
let simple_email = SimpleSendableEmail::new( let simple_email = SimpleSendableEmail::new_with_envelope(
email.from().clone(), email.envelope().clone(),
email.to().clone(), email.message_id().to_string(),
email.message_id().clone(),
message_content, message_content,
); );
f.write_all( f.write_all(serde_json::to_string(&simple_email)?.as_bytes())?;
serde_json::to_string(&simple_email)?.as_bytes(),
)?;
Ok(()) Ok(())
} }

View File

@@ -4,25 +4,32 @@
//! emails have to implement `SendableEmail`. //! emails have to implement `SendableEmail`.
//! //!
#![deny(missing_docs, unsafe_code, unstable_features, warnings)] #![doc(html_root_url = "https://docs.rs/lettre/0.8.1")]
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
#[macro_use] trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
extern crate log; unused_qualifications)]
#[cfg(feature = "crammd5-auth")]
extern crate hex;
#[cfg(feature = "crammd5-auth")]
extern crate crypto;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
extern crate base64; extern crate base64;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
extern crate bufstream; extern crate bufstream;
#[cfg(feature = "crammd5-auth")]
extern crate crypto;
#[cfg(feature = "crammd5-auth")]
extern crate hex;
#[cfg(feature = "smtp-transport")]
extern crate hostname;
#[macro_use]
extern crate log;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
extern crate native_tls; extern crate native_tls;
#[cfg(feature = "file-transport")] #[cfg(feature = "smtp-transport")]
extern crate serde_json; #[macro_use]
extern crate nom;
#[cfg(feature = "serde-impls")] #[cfg(feature = "serde-impls")]
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[cfg(feature = "file-transport")]
extern crate serde_json;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
pub mod smtp; pub mod smtp;
@@ -37,16 +44,68 @@ pub use file::FileEmailTransport;
#[cfg(feature = "sendmail-transport")] #[cfg(feature = "sendmail-transport")]
pub use sendmail::SendmailTransport; pub use sendmail::SendmailTransport;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
pub use smtp::{SmtpTransport, ClientSecurity}; pub use smtp::{ClientSecurity, SmtpTransport};
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
pub use smtp::client::net::ClientTlsParameters; pub use smtp::client::net::ClientTlsParameters;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::io::Read; use std::io::Read;
use std::error::Error as StdError;
use std::str::FromStr;
/// Error type for email content
#[derive(Debug, Clone, Copy)]
pub enum Error {
/// Missing from in envelope
MissingFrom,
/// Missing to in envelope
MissingTo,
/// Invalid email
InvalidEmailAddress,
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::MissingFrom => "missing source address, invalid envelope",
Error::MissingTo => "missing destination address, invalid envelope",
Error::InvalidEmailAddress => "invalid email address",
}
}
fn cause(&self) -> Option<&StdError> {
None
}
}
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
fmt.write_str(self.description())
}
}
/// Email result type
pub type EmailResult<T> = Result<T, Error>;
/// Email address /// Email address
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct EmailAddress(pub String); pub struct EmailAddress(String);
impl EmailAddress {
/// Creates a new `EmailAddress`. For now it makes no validation.
pub fn new(address: String) -> EmailResult<EmailAddress> {
// TODO make some basic sanity checks
Ok(EmailAddress(address))
}
}
impl FromStr for EmailAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
EmailAddress::new(s.to_string())
}
}
impl Display for EmailAddress { impl Display for EmailAddress {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
@@ -54,19 +113,98 @@ impl Display for EmailAddress {
} }
} }
impl EmailAddress { /// Simple email envelope representation
/// Creates a new email address ///
pub fn new(address: String) -> EmailAddress { /// We only accept mailboxes, and do not support source routes (as per RFC).
EmailAddress(address) #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct Envelope {
/// The envelope recipients' addresses
///
/// This can not be empty.
forward_path: Vec<EmailAddress>,
/// The envelope sender address
reverse_path: Option<EmailAddress>,
}
impl Envelope {
/// Creates a new envelope, which may fail if `to` is empty.
pub fn new(from: Option<EmailAddress>, to: Vec<EmailAddress>) -> EmailResult<Envelope> {
if to.is_empty() {
return Err(Error::MissingTo);
}
Ok(Envelope {
forward_path: to,
reverse_path: from,
})
}
/// Destination addresses of the envelope
pub fn to(&self) -> &[EmailAddress] {
self.forward_path.as_slice()
}
/// Source address of the envelope
pub fn from(&self) -> Option<&EmailAddress> {
self.reverse_path.as_ref()
}
/// Creates a new builder
pub fn builder() -> EnvelopeBuilder {
EnvelopeBuilder::new()
}
}
/// Simple email envelope representation
#[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct EnvelopeBuilder {
/// The envelope recipients' addresses
to: Vec<EmailAddress>,
/// The envelope sender address
from: Option<EmailAddress>,
}
impl EnvelopeBuilder {
/// Constructs an envelope with no recipients and an empty sender
pub fn new() -> Self {
EnvelopeBuilder {
to: vec![],
from: None,
}
}
/// Adds a recipient
pub fn to<S: Into<EmailAddress>>(mut self, address: S) -> Self {
self.add_to(address);
self
}
/// Adds a recipient
pub fn add_to<S: Into<EmailAddress>>(&mut self, address: S) {
self.to.push(address.into());
}
/// Sets the sender
pub fn from<S: Into<EmailAddress>>(mut self, address: S) -> Self {
self.set_from(address);
self
}
/// Sets the sender
pub fn set_from<S: Into<EmailAddress>>(&mut self, address: S) {
self.from = Some(address.into());
}
/// Build the envelope
pub fn build(self) -> EmailResult<Envelope> {
Envelope::new(self.from, self.to)
} }
} }
/// Email sendable by an SMTP client /// Email sendable by an SMTP client
pub trait SendableEmail<'a, T: Read + 'a> { pub trait SendableEmail<'a, T: Read + 'a> {
/// To /// Envelope
fn to(&self) -> Vec<EmailAddress>; fn envelope(&self) -> Envelope;
/// From
fn from(&self) -> EmailAddress;
/// Message ID, used for logging /// Message ID, used for logging
fn message_id(&self) -> String; fn message_id(&self) -> String;
/// Message content /// Message content
@@ -83,10 +221,8 @@ pub trait EmailTransport<'a, U: Read + 'a, V> {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct SimpleSendableEmail { pub struct SimpleSendableEmail {
/// To /// Envelope
to: Vec<EmailAddress>, envelope: Envelope,
/// From
from: EmailAddress,
/// Message ID /// Message ID
message_id: String, message_id: String,
/// Message content /// Message content
@@ -96,27 +232,39 @@ pub struct SimpleSendableEmail {
impl SimpleSendableEmail { impl SimpleSendableEmail {
/// Returns a new email /// Returns a new email
pub fn new( pub fn new(
from_address: EmailAddress, from_address: String,
to_addresses: Vec<EmailAddress>, to_addresses: &[String],
message_id: String,
message: String,
) -> EmailResult<SimpleSendableEmail> {
let to: Result<Vec<EmailAddress>, Error> = to_addresses
.iter()
.map(|x| EmailAddress::new(x.clone()))
.collect();
Ok(SimpleSendableEmail::new_with_envelope(
Envelope::new(Some(EmailAddress::new(from_address)?), to?)?,
message_id,
message,
))
}
/// Returns a new email from a valid envelope
pub fn new_with_envelope(
envelope: Envelope,
message_id: String, message_id: String,
message: String, message: String,
) -> SimpleSendableEmail { ) -> SimpleSendableEmail {
SimpleSendableEmail { SimpleSendableEmail {
from: from_address, envelope,
to: to_addresses, message_id,
message_id: message_id,
message: message.into_bytes(), message: message.into_bytes(),
} }
} }
} }
impl<'a> SendableEmail<'a, &'a [u8]> for SimpleSendableEmail { impl<'a> SendableEmail<'a, &'a [u8]> for SimpleSendableEmail {
fn to(&self) -> Vec<EmailAddress> { fn envelope(&self) -> Envelope {
self.to.clone() self.envelope.clone()
}
fn from(&self) -> EmailAddress {
self.from.clone()
} }
fn message_id(&self) -> String { fn message_id(&self) -> String {

View File

@@ -30,7 +30,7 @@ impl StdError for Error {
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&StdError> {
match *self { match *self {
Io(ref err) => Some(&*err as &StdError), Io(ref err) => Some(&*err),
_ => None, _ => None,
} }
} }

View File

@@ -1,20 +1,5 @@
//! The sendmail transport sends the email using the local sendmail command. //! The sendmail transport sends the email using the local sendmail command.
//! //!
//! ```rust
//! use lettre::sendmail::SendmailTransport;
//! use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
//!
//! let email = SimpleSendableEmail::new(
//! EmailAddress::new("user@localhost".to_string()),
//! vec![EmailAddress::new("root@localhost".to_string())],
//! "message_id".to_string(),
//! "Hello world".to_string(),
//! );
//!
//! let mut sender = SendmailTransport::new();
//! let result = sender.send(&email);
//! assert!(result.is_ok());
//! ```
use {EmailTransport, SendableEmail}; use {EmailTransport, SendableEmail};
use sendmail::error::SendmailResult; use sendmail::error::SendmailResult;
@@ -26,6 +11,7 @@ pub mod error;
/// Sends an email using the `sendmail` command /// Sends an email using the `sendmail` command
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct SendmailTransport { pub struct SendmailTransport {
command: String, command: String,
} }
@@ -33,28 +19,35 @@ pub struct SendmailTransport {
impl SendmailTransport { impl SendmailTransport {
/// Creates a new transport with the default `/usr/sbin/sendmail` command /// Creates a new transport with the default `/usr/sbin/sendmail` command
pub fn new() -> SendmailTransport { pub fn new() -> SendmailTransport {
SendmailTransport { command: "/usr/sbin/sendmail".to_string() } SendmailTransport {
command: "/usr/sbin/sendmail".to_string(),
}
} }
/// Creates a new transport to the given sendmail command /// Creates a new transport to the given sendmail command
pub fn new_with_command<S: Into<String>>(command: S) -> SendmailTransport { pub fn new_with_command<S: Into<String>>(command: S) -> SendmailTransport {
SendmailTransport { command: command.into() } SendmailTransport {
command: command.into(),
}
} }
} }
impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTransport { impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTransport {
fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SendmailResult { fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SendmailResult {
let envelope = email.envelope();
// Spawn the sendmail command // Spawn the sendmail command
let to_addresses: Vec<String> = email.to().iter().map(|x| x.to_string()).collect(); let to_addresses: Vec<String> = envelope.to().iter().map(|x| x.to_string()).collect();
let mut process = Command::new(&self.command) let mut process = Command::new(&self.command)
.args( .args(&[
&[
"-i", "-i",
"-f", "-f",
&email.from().to_string(), &match envelope.from() {
Some(address) => address.to_string(),
None => "\"\"".to_string(),
},
&to_addresses.join(" "), &to_addresses.join(" "),
], ])
)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;
@@ -62,9 +55,12 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SendmailResult> for SendmailTranspo
let mut message_content = String::new(); let mut message_content = String::new();
let _ = email.message().read_to_string(&mut message_content); let _ = email.message().read_to_string(&mut message_content);
match process.stdin.as_mut().unwrap().write_all( match process
message_content.as_bytes(), .stdin
) { .as_mut()
.unwrap()
.write_all(message_content.as_bytes())
{
Ok(_) => (), Ok(_) => (),
Err(error) => return Err(From::from(error)), Err(error) => return Err(From::from(error)),
} }

View File

@@ -7,30 +7,28 @@ use crypto::mac::Mac;
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
use crypto::md5::Md5; use crypto::md5::Md5;
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
use hex::ToHex; use hex;
use smtp::NUL; use smtp::NUL;
use smtp::error::Error; use smtp::error::Error;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
/// Accepted authentication mecanisms on an encrypted connection /// Accepted authentication mechanisms on an encrypted connection
/// Trying LOGIN last as it is deprecated. /// Trying LOGIN last as it is deprecated.
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
pub const DEFAULT_ENCRYPTED_MECHANISMS: &'static [Mechanism] = pub const DEFAULT_ENCRYPTED_MECHANISMS: &[Mechanism] =
&[Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]; &[Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login];
/// Accepted authentication mecanisms on an encrypted connection /// Accepted authentication mechanisms on an encrypted connection
/// Trying LOGIN last as it is deprecated. /// Trying LOGIN last as it is deprecated.
#[cfg(not(feature = "crammd5-auth"))] #[cfg(not(feature = "crammd5-auth"))]
pub const DEFAULT_ENCRYPTED_MECHANISMS: &'static [Mechanism] = pub const DEFAULT_ENCRYPTED_MECHANISMS: &[Mechanism] = &[Mechanism::Plain, Mechanism::Login];
&[Mechanism::Plain, Mechanism::Login];
/// Accepted authentication mecanisms on an unencrypted connection /// Accepted authentication mechanisms on an unencrypted connection
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
pub const DEFAULT_UNENCRYPTED_MECHANISMS: &'static [Mechanism] = &[Mechanism::CramMd5]; pub const DEFAULT_UNENCRYPTED_MECHANISMS: &[Mechanism] = &[Mechanism::CramMd5];
/// Accepted authentication mecanisms on an unencrypted connection /// Accepted authentication mechanisms on an unencrypted connection
/// When CRAMMD5 support is not enabled, no mechanisms are allowed. /// When CRAMMD5 support is not enabled, no mechanisms are allowed.
#[cfg(not(feature = "crammd5-auth"))] #[cfg(not(feature = "crammd5-auth"))]
pub const DEFAULT_UNENCRYPTED_MECHANISMS: &'static [Mechanism] = &[]; pub const DEFAULT_UNENCRYPTED_MECHANISMS: &[Mechanism] = &[];
/// Convertable to user credentials /// Convertable to user credentials
pub trait IntoCredentials { pub trait IntoCredentials {
@@ -53,6 +51,7 @@ impl<S: Into<String>, T: Into<String>> IntoCredentials for (S, T) {
/// Contains user credentials /// Contains user credentials
#[derive(PartialEq, Eq, Clone, Hash, Debug)] #[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct Credentials { pub struct Credentials {
username: String, username: String,
password: String, password: String,
@@ -61,15 +60,13 @@ pub struct Credentials {
impl Credentials { impl Credentials {
/// Create a `Credentials` struct from username and password /// Create a `Credentials` struct from username and password
pub fn new(username: String, password: String) -> Credentials { pub fn new(username: String, password: String) -> Credentials {
Credentials { Credentials { username, password }
username: username,
password: password,
}
} }
} }
/// Represents authentication mechanisms /// Represents authentication mechanisms
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum Mechanism { pub enum Mechanism {
/// PLAIN authentication mechanism /// PLAIN authentication mechanism
/// RFC 4616: https://tools.ietf.org/html/rfc4616 /// RFC 4616: https://tools.ietf.org/html/rfc4616
@@ -119,18 +116,13 @@ impl Mechanism {
challenge: Option<&str>, challenge: Option<&str>,
) -> Result<String, Error> { ) -> Result<String, Error> {
match *self { match *self {
Mechanism::Plain => { Mechanism::Plain => match challenge {
match challenge {
Some(_) => Err(Error::Client("This mechanism does not expect a challenge")), Some(_) => Err(Error::Client("This mechanism does not expect a challenge")),
None => Ok(format!( None => Ok(format!(
"{}{}{}{}", "{}{}{}{}",
NUL, NUL, credentials.username, NUL, credentials.password
credentials.username,
NUL,
credentials.password
)), )),
} },
}
Mechanism::Login => { Mechanism::Login => {
let decoded_challenge = match challenge { let decoded_challenge = match challenge {
Some(challenge) => challenge, Some(challenge) => challenge,
@@ -160,7 +152,7 @@ impl Mechanism {
Ok(format!( Ok(format!(
"{} {}", "{} {}",
credentials.username, credentials.username,
hmac.result().code().to_hex() hex::encode(hmac.result().code())
)) ))
} }
} }
@@ -212,7 +204,7 @@ mod test {
mechanism mechanism
.response( .response(
&credentials, &credentials,
Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg=="), Some("PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==")
) )
.unwrap(), .unwrap(),
"alice a540ebe4ef2304070bbc3c456c1f64c0" "alice a540ebe4ef2304070bbc3c456c1f64c0"

View File

@@ -41,11 +41,11 @@ impl MockStream {
vec vec
} }
pub fn next_vec(&mut self, vec: Vec<u8>) { pub fn next_vec(&mut self, vec: &[u8]) {
let mut cursor = self.reader.lock().unwrap(); let mut cursor = self.reader.lock().unwrap();
cursor.set_position(0); cursor.set_position(0);
cursor.get_mut().clear(); cursor.get_mut().clear();
cursor.get_mut().extend_from_slice(vec.as_slice()); cursor.get_mut().extend_from_slice(vec);
} }
pub fn swap(&mut self) { pub fn swap(&mut self) {

View File

@@ -1,12 +1,13 @@
//! SMTP client //! SMTP client
use bufstream::BufStream; use bufstream::BufStream;
use nom::ErrorKind as NomErrorKind;
use smtp::{CRLF, MESSAGE_ENDING}; use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::{Credentials, Mechanism}; use smtp::authentication::{Credentials, Mechanism};
use smtp::client::net::{ClientTlsParameters, Connector, NetworkStream, Timeout}; use smtp::client::net::{ClientTlsParameters, Connector, NetworkStream, Timeout};
use smtp::commands::*; use smtp::commands::*;
use smtp::error::{Error, SmtpResult}; use smtp::error::{Error, SmtpResult};
use smtp::response::ResponseParser; use smtp::response::Response;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::io::{self, BufRead, BufReader, Read, Write}; use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
@@ -17,7 +18,8 @@ pub mod net;
pub mod mock; pub mod mock;
/// The codec used for transparency /// The codec used for transparency
#[derive(Default, Debug)] #[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct ClientCodec { pub struct ClientCodec {
escape_count: u8, escape_count: u8,
} }
@@ -60,9 +62,9 @@ impl ClientCodec {
start = idx; start = idx;
} }
} }
Ok(buf.write_all(&frame[start..])?) buf.write_all(&frame[start..])?;
Ok(())
} }
} }
} }
} }
@@ -73,12 +75,6 @@ fn escape_crlf(string: &str) -> String {
string.replace(CRLF, "<CRLF>") string.replace(CRLF, "<CRLF>")
} }
/// Returns the string removing all the CRLF
/// Used for debug displays
fn remove_crlf(string: &str) -> String {
string.replace(CRLF, "")
}
/// Structure that implements the SMTP client /// Structure that implements the SMTP client
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Client<S: Write + Read = NetworkStream> { pub struct Client<S: Write + Read = NetworkStream> {
@@ -106,7 +102,7 @@ impl<S: Write + Read> Client<S> {
impl<S: Connector + Write + Read + Timeout + Debug> Client<S> { impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
/// Closes the SMTP transaction if possible /// Closes the SMTP transaction if possible
pub fn close(&mut self) { pub fn close(&mut self) {
let _ = self.smtp_command(QuitCommand); let _ = self.command(QuitCommand);
self.stream = None; self.stream = None;
} }
@@ -166,29 +162,27 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
// Try to connect // Try to connect
self.set_stream(Connector::connect(&server_addr, tls_parameters)?); self.set_stream(Connector::connect(&server_addr, tls_parameters)?);
self.get_reply() self.read_response()
} }
/// Checks if the server is connected using the NOOP SMTP command /// Checks if the server is connected using the NOOP SMTP command
#[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
pub fn is_connected(&mut self) -> bool { pub fn is_connected(&mut self) -> bool {
self.smtp_command(NoopCommand).is_ok() self.command(NoopCommand).is_ok()
} }
/// Sends an AUTH command with the given mechanism, and handles challenge if needed /// Sends an AUTH command with the given mechanism, and handles challenge if needed
pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult { pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult {
// TODO // TODO
let mut challenges = 10; let mut challenges = 10;
let mut response = self.smtp_command( let mut response = self.command(AuthCommand::new(mechanism, credentials.clone(), None)?)?;
AuthCommand::new(mechanism, credentials.clone(), None)?,
)?;
while challenges > 0 && response.has_code(334) { while challenges > 0 && response.has_code(334) {
challenges -= 1; challenges -= 1;
response = self.smtp_command(AuthCommand::new_from_response( response = self.command(AuthCommand::new_from_response(
mechanism, mechanism,
credentials.clone(), credentials.clone(),
response, &response,
)?)?; )?)?;
} }
@@ -209,8 +203,11 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
out_buf.clear(); out_buf.clear();
let consumed = match message_reader.fill_buf() { let consumed = match message_reader.fill_buf() {
Ok(bytes) => { codec.encode(bytes, &mut out_buf)?; bytes.len() }, Ok(bytes) => {
Err(ref err) => panic!("Failed with: {}", err) codec.encode(bytes, &mut out_buf)?;
bytes.len()
}
Err(ref err) => panic!("Failed with: {}", err),
}; };
message_reader.consume(consumed); message_reader.consume(consumed);
@@ -218,21 +215,21 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
break; break;
} }
self.write_server(out_buf.as_slice())?; self.write(out_buf.as_slice())?;
} }
self.write_server(MESSAGE_ENDING.as_bytes())?; self.write(MESSAGE_ENDING.as_bytes())?;
self.get_reply() self.read_response()
} }
/// Sends an SMTP command /// Sends an SMTP command
pub fn smtp_command<C: Display>(&mut self, command: C) -> SmtpResult { pub fn command<C: Display>(&mut self, command: C) -> SmtpResult {
self.write_server(command.to_string().as_bytes())?; self.write(command.to_string().as_bytes())?;
self.get_reply() self.read_response()
} }
/// Writes a string to the server /// Writes a string to the server
fn write_server(&mut self, string: &[u8]) -> Result<(), Error> { fn write(&mut self, string: &[u8]) -> Result<(), Error> {
if self.stream.is_none() { if self.stream.is_none() {
return Err(From::from("Connection closed")); return Err(From::from("Connection closed"));
} }
@@ -248,33 +245,34 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
} }
/// Gets the SMTP response /// Gets the SMTP response
fn get_reply(&mut self) -> SmtpResult { fn read_response(&mut self) -> SmtpResult {
let mut raw_response = String::new();
let mut response = raw_response.parse::<Response>();
let mut parser = ResponseParser::default(); while response.is_err() {
if response.as_ref().err().unwrap() != &NomErrorKind::Complete {
let mut line = String::new(); break;
self.stream.as_mut().unwrap().read_line(&mut line)?; }
// TODO read more than one line
debug!("Read: {}", escape_crlf(line.as_ref())); self.stream.as_mut().unwrap().read_line(&mut raw_response)?;
response = raw_response.parse::<Response>();
while parser.read_line(remove_crlf(line.as_ref()).as_ref())? {
line.clear();
self.stream.as_mut().unwrap().read_line(&mut line)?;
} }
let response = parser.response()?; debug!("Read: {}", escape_crlf(raw_response.as_ref()));
if response.is_positive() { let final_response = response?;
Ok(response)
if final_response.is_positive() {
Ok(final_response)
} else { } else {
Err(From::from(response)) Err(From::from(final_response))
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{ClientCodec, escape_crlf, remove_crlf}; use super::{escape_crlf, ClientCodec};
#[test] #[test]
fn test_codec() { fn test_codec() {
@@ -296,16 +294,6 @@ mod test {
); );
} }
#[test]
fn test_remove_crlf() {
assert_eq!(remove_crlf("\r\n"), "");
assert_eq!(remove_crlf("EHLO my_name\r\n"), "EHLO my_name");
assert_eq!(
remove_crlf("EHLO my_name\r\nSIZE 42\r\n"),
"EHLO my_nameSIZE 42"
);
}
#[test] #[test]
fn test_escape_crlf() { fn test_escape_crlf() {
assert_eq!(escape_crlf("\r\n"), "<CRLF>"); assert_eq!(escape_crlf("\r\n"), "<CRLF>");

View File

@@ -1,6 +1,6 @@
//! A trait to represent a stream //! A trait to represent a stream
use native_tls::{TlsConnector, TlsStream, Protocol}; use native_tls::{Protocol, TlsConnector, TlsStream};
use smtp::client::mock::MockStream; use smtp::client::mock::MockStream;
use std::io::{self, ErrorKind, Read, Write}; use std::io::{self, ErrorKind, Read, Write};
use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, TcpStream}; use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, TcpStream};
@@ -8,6 +8,7 @@ use std::time::Duration;
/// Parameters to use for secure clients /// Parameters to use for secure clients
#[derive(Clone)] #[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct ClientTlsParameters { pub struct ClientTlsParameters {
/// A connector from `native-tls` /// A connector from `native-tls`
pub connector: TlsConnector, pub connector: TlsConnector,
@@ -18,16 +19,13 @@ pub struct ClientTlsParameters {
impl ClientTlsParameters { impl ClientTlsParameters {
/// Creates a `ClientTlsParameters` /// Creates a `ClientTlsParameters`
pub fn new(domain: String, connector: TlsConnector) -> ClientTlsParameters { pub fn new(domain: String, connector: TlsConnector) -> ClientTlsParameters {
ClientTlsParameters { ClientTlsParameters { connector, domain }
connector: connector,
domain: domain,
}
} }
} }
/// Accepted protocols by default. /// Accepted protocols by default.
/// This removes TLS 1.0 compared to tls-native defaults. /// This removes TLS 1.0 compared to tls-native defaults.
pub const DEFAULT_TLS_PROTOCOLS : &'static [Protocol] = &[Protocol::Tlsv11, Protocol::Tlsv12]; pub const DEFAULT_TLS_PROTOCOLS: &[Protocol] = &[Protocol::Tlsv11, Protocol::Tlsv12];
#[derive(Debug)] #[derive(Debug)]
/// Represents the different types of underlying network streams /// Represents the different types of underlying network streams
@@ -46,11 +44,10 @@ impl NetworkStream {
match *self { match *self {
NetworkStream::Tcp(ref s) => s.peer_addr(), NetworkStream::Tcp(ref s) => s.peer_addr(),
NetworkStream::Tls(ref s) => s.get_ref().peer_addr(), NetworkStream::Tls(ref s) => s.get_ref().peer_addr(),
NetworkStream::Mock(_) => { NetworkStream::Mock(_) => Ok(SocketAddr::V4(SocketAddrV4::new(
Ok(SocketAddr::V4( Ipv4Addr::new(127, 0, 0, 1),
SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80), 80,
)) ))),
}
} }
} }
@@ -111,13 +108,11 @@ impl Connector for NetworkStream {
let tcp_stream = TcpStream::connect(addr)?; let tcp_stream = TcpStream::connect(addr)?;
match tls_parameters { match tls_parameters {
Some(context) => { Some(context) => context
context
.connector .connector
.connect(context.domain.as_ref(), tcp_stream) .connect(context.domain.as_ref(), tcp_stream)
.map(NetworkStream::Tls) .map(NetworkStream::Tls)
.map_err(|e| io::Error::new(ErrorKind::Other, e)) .map_err(|e| io::Error::new(ErrorKind::Other, e)),
}
None => Ok(NetworkStream::Tcp(tcp_stream)), None => Ok(NetworkStream::Tcp(tcp_stream)),
} }
} }
@@ -125,15 +120,13 @@ impl Connector for NetworkStream {
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
fn upgrade_tls(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()> { fn upgrade_tls(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()> {
*self = match *self { *self = match *self {
NetworkStream::Tcp(ref mut stream) => { NetworkStream::Tcp(ref mut stream) => match tls_parameters
match tls_parameters.connector.connect( .connector
tls_parameters.domain.as_ref(), .connect(tls_parameters.domain.as_ref(), stream.try_clone().unwrap())
stream.try_clone().unwrap(), {
) {
Ok(tls_stream) => NetworkStream::Tls(tls_stream), Ok(tls_stream) => NetworkStream::Tls(tls_stream),
Err(err) => return Err(io::Error::new(ErrorKind::Other, err)), Err(err) => return Err(io::Error::new(ErrorKind::Other, err)),
} },
}
NetworkStream::Tls(_) => return Ok(()), NetworkStream::Tls(_) => return Ok(()),
NetworkStream::Mock(_) => return Ok(()), NetworkStream::Mock(_) => return Ok(()),
}; };

View File

@@ -12,6 +12,7 @@ use std::fmt::{self, Display, Formatter};
/// EHLO command /// EHLO command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct EhloCommand { pub struct EhloCommand {
client_id: ClientId, client_id: ClientId,
} }
@@ -26,12 +27,13 @@ impl Display for EhloCommand {
impl EhloCommand { impl EhloCommand {
/// Creates a EHLO command /// Creates a EHLO command
pub fn new(client_id: ClientId) -> EhloCommand { pub fn new(client_id: ClientId) -> EhloCommand {
EhloCommand { client_id: client_id } EhloCommand { client_id }
} }
} }
/// STARTTLS command /// STARTTLS command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct StarttlsCommand; pub struct StarttlsCommand;
impl Display for StarttlsCommand { impl Display for StarttlsCommand {
@@ -43,6 +45,7 @@ impl Display for StarttlsCommand {
/// MAIL command /// MAIL command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct MailCommand { pub struct MailCommand {
sender: Option<EmailAddress>, sender: Option<EmailAddress>,
parameters: Vec<MailParameter>, parameters: Vec<MailParameter>,
@@ -68,15 +71,13 @@ impl Display for MailCommand {
impl MailCommand { impl MailCommand {
/// Creates a MAIL command /// Creates a MAIL command
pub fn new(sender: Option<EmailAddress>, parameters: Vec<MailParameter>) -> MailCommand { pub fn new(sender: Option<EmailAddress>, parameters: Vec<MailParameter>) -> MailCommand {
MailCommand { MailCommand { sender, parameters }
sender: sender,
parameters: parameters,
}
} }
} }
/// RCPT command /// RCPT command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct RcptCommand { pub struct RcptCommand {
recipient: EmailAddress, recipient: EmailAddress,
parameters: Vec<RcptParameter>, parameters: Vec<RcptParameter>,
@@ -96,14 +97,15 @@ impl RcptCommand {
/// Creates an RCPT command /// Creates an RCPT command
pub fn new(recipient: EmailAddress, parameters: Vec<RcptParameter>) -> RcptCommand { pub fn new(recipient: EmailAddress, parameters: Vec<RcptParameter>) -> RcptCommand {
RcptCommand { RcptCommand {
recipient: recipient, recipient,
parameters: parameters, parameters,
} }
} }
} }
/// DATA command /// DATA command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct DataCommand; pub struct DataCommand;
impl Display for DataCommand { impl Display for DataCommand {
@@ -114,7 +116,8 @@ impl Display for DataCommand {
} }
/// QUIT command /// QUIT command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct QuitCommand; pub struct QuitCommand;
impl Display for QuitCommand { impl Display for QuitCommand {
@@ -125,7 +128,8 @@ impl Display for QuitCommand {
} }
/// NOOP command /// NOOP command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct NoopCommand; pub struct NoopCommand;
impl Display for NoopCommand { impl Display for NoopCommand {
@@ -137,6 +141,7 @@ impl Display for NoopCommand {
/// HELP command /// HELP command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct HelpCommand { pub struct HelpCommand {
argument: Option<String>, argument: Option<String>,
} }
@@ -154,12 +159,13 @@ impl Display for HelpCommand {
impl HelpCommand { impl HelpCommand {
/// Creates an HELP command /// Creates an HELP command
pub fn new(argument: Option<String>) -> HelpCommand { pub fn new(argument: Option<String>) -> HelpCommand {
HelpCommand { argument: argument } HelpCommand { argument }
} }
} }
/// VRFY command /// VRFY command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct VrfyCommand { pub struct VrfyCommand {
argument: String, argument: String,
} }
@@ -174,12 +180,13 @@ impl Display for VrfyCommand {
impl VrfyCommand { impl VrfyCommand {
/// Creates a VRFY command /// Creates a VRFY command
pub fn new(argument: String) -> VrfyCommand { pub fn new(argument: String) -> VrfyCommand {
VrfyCommand { argument: argument } VrfyCommand { argument }
} }
} }
/// EXPN command /// EXPN command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct ExpnCommand { pub struct ExpnCommand {
argument: String, argument: String,
} }
@@ -194,12 +201,13 @@ impl Display for ExpnCommand {
impl ExpnCommand { impl ExpnCommand {
/// Creates an EXPN command /// Creates an EXPN command
pub fn new(argument: String) -> ExpnCommand { pub fn new(argument: String) -> ExpnCommand {
ExpnCommand { argument: argument } ExpnCommand { argument }
} }
} }
/// RSET command /// RSET command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct RsetCommand; pub struct RsetCommand;
impl Display for RsetCommand { impl Display for RsetCommand {
@@ -211,6 +219,7 @@ impl Display for RsetCommand {
/// AUTH command /// AUTH command
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct AuthCommand { pub struct AuthCommand {
mechanism: Mechanism, mechanism: Mechanism,
credentials: Credentials, credentials: Credentials,
@@ -249,18 +258,15 @@ impl AuthCommand {
challenge: Option<String>, challenge: Option<String>,
) -> Result<AuthCommand, Error> { ) -> Result<AuthCommand, Error> {
let response = if mechanism.supports_initial_response() || challenge.is_some() { let response = if mechanism.supports_initial_response() || challenge.is_some() {
Some(mechanism.response( Some(mechanism.response(&credentials, challenge.as_ref().map(String::as_str))?)
&credentials,
challenge.as_ref().map(String::as_str),
)?)
} else { } else {
None None
}; };
Ok(AuthCommand { Ok(AuthCommand {
mechanism: mechanism, mechanism,
credentials: credentials, credentials,
challenge: challenge, challenge,
response: response, response,
}) })
} }
@@ -269,7 +275,7 @@ impl AuthCommand {
pub fn new_from_response( pub fn new_from_response(
mechanism: Mechanism, mechanism: Mechanism,
credentials: Credentials, credentials: Credentials,
response: Response, response: &Response,
) -> Result<AuthCommand, Error> { ) -> Result<AuthCommand, Error> {
if !response.has_code(334) { if !response.has_code(334) {
return Err(Error::ResponseParsing("Expecting a challenge")); return Err(Error::ResponseParsing("Expecting a challenge"));
@@ -283,27 +289,22 @@ impl AuthCommand {
debug!("auth encoded challenge: {}", encoded_challenge); debug!("auth encoded challenge: {}", encoded_challenge);
let decoded_challenge = match base64::decode(&encoded_challenge) { let decoded_challenge = match base64::decode(&encoded_challenge) {
Ok(challenge) => { Ok(challenge) => match String::from_utf8(challenge) {
match String::from_utf8(challenge) {
Ok(value) => value, Ok(value) => value,
Err(error) => return Err(Error::Utf8Parsing(error)), Err(error) => return Err(Error::Utf8Parsing(error)),
} },
}
Err(error) => return Err(Error::ChallengeParsing(error)), Err(error) => return Err(Error::ChallengeParsing(error)),
}; };
debug!("auth decoded challenge: {}", decoded_challenge); debug!("auth decoded challenge: {}", decoded_challenge);
let response = Some(mechanism.response( let response = Some(mechanism.response(&credentials, Some(decoded_challenge.as_ref()))?);
&credentials,
Some(decoded_challenge.as_ref()),
)?);
Ok(AuthCommand { Ok(AuthCommand {
mechanism: mechanism, mechanism,
credentials: credentials, credentials,
challenge: Some(decoded_challenge), challenge: Some(decoded_challenge),
response: response, response,
}) })
} }
} }
@@ -313,14 +314,12 @@ mod test {
use super::*; use super::*;
use smtp::extension::MailBodyParameter; use smtp::extension::MailBodyParameter;
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
use smtp::response::Code; use smtp::response::{Category, Code, Detail, Severity};
#[cfg(feature = "crammd5-auth")]
use std::str::FromStr;
#[test] #[test]
fn test_display() { fn test_display() {
let id = ClientId::Domain("localhost".to_string()); let id = ClientId::Domain("localhost".to_string());
let email = EmailAddress::new("test@example.com".to_string()); let email = EmailAddress::new("test@example.com".to_string()).unwrap();
let mail_parameter = MailParameter::Other { let mail_parameter = MailParameter::Other {
keyword: "TEST".to_string(), keyword: "TEST".to_string(),
value: Some("value".to_string()), value: Some("value".to_string()),
@@ -418,7 +417,14 @@ mod test {
AuthCommand::new_from_response( AuthCommand::new_from_response(
Mechanism::CramMd5, Mechanism::CramMd5,
credentials.clone(), credentials.clone(),
Response::new(Code::from_str("334").unwrap(), vec!["dGVzdAo=".to_string()]), &Response::new(
Code::new(
Severity::PositiveIntermediate,
Category::Unspecified3,
Detail::Four,
),
vec!["dGVzdAo=".to_string()],
),
).unwrap() ).unwrap()
), ),
"dXNlciA1NTIzNThiMzExOWFjOWNkYzM2YWRiN2MxNWRmMWJkNw==\r\n" "dXNlciA1NTIzNThiMzExOWFjOWNkYzM2YWRiN2MxNWRmMWJkNw==\r\n"

View File

@@ -2,13 +2,14 @@
use self::Error::*; use self::Error::*;
use base64::DecodeError; use base64::DecodeError;
use native_tls;
use nom;
use smtp::response::{Response, Severity}; use smtp::response::{Response, Severity};
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::io; use std::io;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use native_tls;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Debug)]
@@ -35,6 +36,8 @@ pub enum Error {
Io(io::Error), Io(io::Error),
/// TLS error /// TLS error
Tls(native_tls::Error), Tls(native_tls::Error),
/// Parsing error
Parsing(nom::simple_errors::Err),
} }
impl Display for Error { impl Display for Error {
@@ -49,18 +52,14 @@ impl StdError for Error {
match *self { match *self {
// Try to display the first line of the server's response that usually // Try to display the first line of the server's response that usually
// contains a short humanly readable error message // contains a short humanly readable error message
Transient(ref err) => { Transient(ref err) => match err.first_line() {
match err.first_line() {
Some(line) => line, Some(line) => line,
None => "undetailed transient error during SMTP transaction", None => "undetailed transient error during SMTP transaction",
} },
} Permanent(ref err) => match err.first_line() {
Permanent(ref err) => {
match err.first_line() {
Some(line) => line, Some(line) => line,
None => "undetailed permanent error during SMTP transaction", None => "undetailed permanent error during SMTP transaction",
} },
}
ResponseParsing(err) => err, ResponseParsing(err) => err,
ChallengeParsing(ref err) => err.description(), ChallengeParsing(ref err) => err.description(),
Utf8Parsing(ref err) => err.description(), Utf8Parsing(ref err) => err.description(),
@@ -68,15 +67,17 @@ impl StdError for Error {
Client(err) => err, Client(err) => err,
Io(ref err) => err.description(), Io(ref err) => err.description(),
Tls(ref err) => err.description(), Tls(ref err) => err.description(),
Parsing(ref err) => err.description(),
} }
} }
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&StdError> {
match *self { match *self {
ChallengeParsing(ref err) => Some(&*err as &StdError), ChallengeParsing(ref err) => Some(&*err),
Utf8Parsing(ref err) => Some(&*err as &StdError), Utf8Parsing(ref err) => Some(&*err),
Io(ref err) => Some(&*err as &StdError), Io(ref err) => Some(&*err),
Tls(ref err) => Some(&*err as &StdError), Tls(ref err) => Some(&*err),
Parsing(ref err) => Some(&*err),
_ => None, _ => None,
} }
} }
@@ -94,6 +95,12 @@ impl From<native_tls::Error> for Error {
} }
} }
impl From<nom::simple_errors::Err> for Error {
fn from(err: nom::simple_errors::Err) -> Error {
Parsing(err)
}
}
impl From<Response> for Error { impl From<Response> for Error {
fn from(response: Response) -> Error { fn from(response: Response) -> Error {
match response.code.severity { match response.code.severity {

View File

@@ -1,5 +1,6 @@
//! ESMTP features //! ESMTP features
use hostname::get_hostname;
use smtp::authentication::Mechanism; use smtp::authentication::Mechanism;
use smtp::error::Error; use smtp::error::Error;
use smtp::response::Response; use smtp::response::Response;
@@ -9,8 +10,12 @@ use std::fmt::{self, Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use std::result::Result; use std::result::Result;
/// Default ehlo clinet id
pub const DEFAULT_EHLO_HOSTNAME: &str = "localhost";
/// Client identifier, the parameter to `EHLO` /// Client identifier, the parameter to `EHLO`
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum ClientId { pub enum ClientId {
/// A fully-qualified domain name /// A fully-qualified domain name
Domain(String), Domain(String),
@@ -35,10 +40,20 @@ impl ClientId {
pub fn new(domain: String) -> ClientId { pub fn new(domain: String) -> ClientId {
ClientId::Domain(domain) ClientId::Domain(domain)
} }
/// Defines a `ClientId` with the current hostname, of `localhost` if hostname could not be
/// found
pub fn hostname() -> ClientId {
ClientId::Domain(match get_hostname() {
Some(name) => name,
None => DEFAULT_EHLO_HOSTNAME.to_string(),
})
}
} }
/// Supported ESMTP keywords /// Supported ESMTP keywords
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum Extension { pub enum Extension {
/// 8BITMIME keyword /// 8BITMIME keyword
/// ///
@@ -59,16 +74,17 @@ pub enum Extension {
impl Display for Extension { impl Display for Extension {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self { match *self {
Extension::EightBitMime => write!(f, "{}", "8BITMIME"), Extension::EightBitMime => write!(f, "8BITMIME"),
Extension::SmtpUtfEight => write!(f, "{}", "SMTPUTF8"), Extension::SmtpUtfEight => write!(f, "SMTPUTF8"),
Extension::StartTls => write!(f, "{}", "STARTTLS"), Extension::StartTls => write!(f, "STARTTLS"),
Extension::Authentication(ref mechanism) => write!(f, "{} {}", "AUTH", mechanism), Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
} }
} }
} }
/// Contains information about an SMTP server /// Contains information about an SMTP server
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct ServerInfo { pub struct ServerInfo {
/// Server name /// Server name
/// ///
@@ -106,6 +122,10 @@ impl ServerInfo {
let mut features: HashSet<Extension> = HashSet::new(); let mut features: HashSet<Extension> = HashSet::new();
for line in response.message.as_slice() { for line in response.message.as_slice() {
if line.is_empty() {
continue;
}
let splitted: Vec<&str> = line.split_whitespace().collect(); let splitted: Vec<&str> = line.split_whitespace().collect();
match splitted[0] { match splitted[0] {
"8BITMIME" => { "8BITMIME" => {
@@ -117,8 +137,7 @@ impl ServerInfo {
"STARTTLS" => { "STARTTLS" => {
features.insert(Extension::StartTls); features.insert(Extension::StartTls);
} }
"AUTH" => { "AUTH" => for &mechanism in &splitted[1..] {
for &mechanism in &splitted[1..] {
match mechanism { match mechanism {
"PLAIN" => { "PLAIN" => {
features.insert(Extension::Authentication(Mechanism::Plain)); features.insert(Extension::Authentication(Mechanism::Plain));
@@ -132,15 +151,14 @@ impl ServerInfo {
} }
_ => (), _ => (),
} }
} },
}
_ => (), _ => (),
}; };
} }
Ok(ServerInfo { Ok(ServerInfo {
name: name.to_string(), name: name.to_string(),
features: features, features,
}) })
} }
@@ -151,14 +169,14 @@ impl ServerInfo {
/// Checks if the server supports an ESMTP feature /// Checks if the server supports an ESMTP feature
pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool { pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool {
self.features.contains( self.features
&Extension::Authentication(mechanism), .contains(&Extension::Authentication(mechanism))
)
} }
} }
/// A `MAIL FROM` extension parameter /// A `MAIL FROM` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum MailParameter { pub enum MailParameter {
/// `BODY` parameter /// `BODY` parameter
Body(MailBodyParameter), Body(MailBodyParameter),
@@ -194,7 +212,8 @@ impl Display for MailParameter {
} }
/// Values for the `BODY` parameter to `MAIL FROM` /// Values for the `BODY` parameter to `MAIL FROM`
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum MailBodyParameter { pub enum MailBodyParameter {
/// `7BIT` /// `7BIT`
SevenBit, SevenBit,
@@ -213,6 +232,7 @@ impl Display for MailBodyParameter {
/// A `RCPT TO` extension parameter /// A `RCPT TO` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum RcptParameter { pub enum RcptParameter {
/// Custom parameter /// Custom parameter
Other { Other {
@@ -241,11 +261,19 @@ impl Display for RcptParameter {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Extension, ServerInfo}; use super::{ClientId, Extension, ServerInfo};
use smtp::authentication::Mechanism; use smtp::authentication::Mechanism;
use smtp::response::{Category, Code, Detail, Response, Severity}; use smtp::response::{Category, Code, Detail, Response, Severity};
use std::collections::HashSet; use std::collections::HashSet;
#[test]
fn test_clientid_fmt() {
assert_eq!(
format!("{}", ClientId::new("test".to_string())),
"test".to_string()
);
}
#[test] #[test]
fn test_extension_fmt() { fn test_extension_fmt() {
assert_eq!( assert_eq!(
@@ -308,7 +336,7 @@ mod test {
Code::new( Code::new(
Severity::PositiveCompletion, Severity::PositiveCompletion,
Category::Unspecified4, Category::Unspecified4,
Detail(1), Detail::One,
), ),
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -336,7 +364,7 @@ mod test {
Code::new( Code::new(
Severity::PositiveCompletion, Severity::PositiveCompletion,
Category::Unspecified4, Category::Unspecified4,
Detail(1), Detail::One,
), ),
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -348,13 +376,9 @@ mod test {
let mut features2 = HashSet::new(); let mut features2 = HashSet::new();
assert!(features2.insert(Extension::EightBitMime)); assert!(features2.insert(Extension::EightBitMime));
assert!(features2.insert( assert!(features2.insert(Extension::Authentication(Mechanism::Plain),));
Extension::Authentication(Mechanism::Plain),
));
#[cfg(feature = "crammd5-auth")] #[cfg(feature = "crammd5-auth")]
assert!(features2.insert( assert!(features2.insert(Extension::Authentication(Mechanism::CramMd5),));
Extension::Authentication(Mechanism::CramMd5),
));
let server_info2 = ServerInfo { let server_info2 = ServerInfo {
name: "me".to_string(), name: "me".to_string(),

View File

@@ -13,107 +13,17 @@
//! * STARTTLS ([RFC 2487](http://tools.ietf.org/html/rfc2487)) //! * STARTTLS ([RFC 2487](http://tools.ietf.org/html/rfc2487))
//! * SMTPUTF8 ([RFC 6531](http://tools.ietf.org/html/rfc6531)) //! * SMTPUTF8 ([RFC 6531](http://tools.ietf.org/html/rfc6531))
//! //!
//! #### Simple example
//!
//! This is the most basic example of usage:
//!
//! ```rust,no_run
//! use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};
//!
//! let email = SimpleSendableEmail::new(
//! EmailAddress::new("user@localhost".to_string()),
//! vec![EmailAddress::new("root@localhost".to_string())],
//! "message_id".to_string(),
//! "Hello world".to_string(),
//! );
//!
//! // Open a local connection on port 25
//! let mut mailer =
//! SmtpTransport::builder_unencrypted_localhost().unwrap().build();
//! // Send the email
//! let result = mailer.send(&email);
//!
//! assert!(result.is_ok());
//! ```
//!
//! #### Complete example
//!
//! ```rust,no_run
//! use lettre::smtp::authentication::{Credentials, Mechanism};
//! use lettre::smtp::SUBMISSION_PORT;
//! use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};
//! use lettre::smtp::extension::ClientId;
//! use lettre::smtp::ConnectionReuseParameters;
//!
//!
//! let email = SimpleSendableEmail::new(
//! EmailAddress::new("user@localhost".to_string()),
//! vec![EmailAddress::new("root@localhost".to_string())],
//! "message_id".to_string(),
//! "Hello world".to_string(),
//! );
//!
//! // Connect to a remote server on a custom port
//! let mut mailer = SmtpTransport::simple_builder("server.tld".to_string()).unwrap()
//! // Set the name sent during EHLO/HELO, default is `localhost`
//! .hello_name(ClientId::Domain("my.hostname.tld".to_string()))
//! // Add credentials for authentication
//! .credentials(Credentials::new("username".to_string(), "password".to_string()))
//! // Enable SMTPUTF8 if the server supports it
//! .smtp_utf8(true)
//! // Configure expected authentication mechanism
//! .authentication_mechanism(Mechanism::Plain)
//! // Enable connection reuse
//! .connection_reuse(ConnectionReuseParameters::ReuseUnlimited).build();
//!
//! let result_1 = mailer.send(&email);
//! assert!(result_1.is_ok());
//!
//! // The second email will use the same connection
//! let result_2 = mailer.send(&email);
//! assert!(result_2.is_ok());
//!
//! // Explicitly close the SMTP transaction as we enabled connection reuse
//! mailer.close();
//! ```
//!
//! #### Lower level
//!
//! You can also send commands, here is a simple email transaction without
//! error handling:
//!
//! ```rust
//! use lettre::EmailAddress;
//! use lettre::smtp::SMTP_PORT;
//! use lettre::smtp::client::Client;
//! use lettre::smtp::client::net::NetworkStream;
//! use lettre::smtp::extension::ClientId;
//! use lettre::smtp::commands::*;
//!
//! let mut email_client: Client<NetworkStream> = Client::new();
//! let _ = email_client.connect(&("localhost", SMTP_PORT), None);
//! let _ = email_client.smtp_command(EhloCommand::new(ClientId::new("my_hostname".to_string())));
//! let _ = email_client.smtp_command(
//! MailCommand::new(Some(EmailAddress::new("user@example.com".to_string())), vec![])
//! );
//! let _ = email_client.smtp_command(
//! RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![])
//! );
//! let _ = email_client.smtp_command(DataCommand);
//! let _ = email_client.message(Box::new("Test email".as_bytes()));
//! let _ = email_client.smtp_command(QuitCommand);
//! ```
use EmailTransport; use EmailTransport;
use SendableEmail; use SendableEmail;
use native_tls::TlsConnector; use native_tls::TlsConnector;
use smtp::authentication::{Credentials, DEFAULT_ENCRYPTED_MECHANISMS, use smtp::authentication::{Credentials, Mechanism, DEFAULT_ENCRYPTED_MECHANISMS,
DEFAULT_UNENCRYPTED_MECHANISMS, Mechanism}; DEFAULT_UNENCRYPTED_MECHANISMS};
use smtp::client::Client; use smtp::client::Client;
use smtp::client::net::ClientTlsParameters; use smtp::client::net::ClientTlsParameters;
use smtp::client::net::DEFAULT_TLS_PROTOCOLS;
use smtp::commands::*; use smtp::commands::*;
use smtp::error::{Error, SmtpResult}; use smtp::error::{Error, SmtpResult};
use smtp::client::net::DEFAULT_TLS_PROTOCOLS;
use smtp::extension::{ClientId, Extension, MailBodyParameter, MailParameter, ServerInfo}; use smtp::extension::{ClientId, Extension, MailBodyParameter, MailParameter, ServerInfo};
use std::io::Read; use std::io::Read;
use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{SocketAddr, ToSocketAddrs};
@@ -140,22 +50,23 @@ pub const SUBMISSION_PORT: u16 = 587;
// Useful strings and characters // Useful strings and characters
/// The word separator for SMTP transactions /// The word separator for SMTP transactions
pub const SP: &'static str = " "; pub const SP: &str = " ";
/// The line ending for SMTP transactions (carriage return + line feed) /// The line ending for SMTP transactions (carriage return + line feed)
pub const CRLF: &'static str = "\r\n"; pub const CRLF: &str = "\r\n";
/// Colon /// Colon
pub const COLON: &'static str = ":"; pub const COLON: &str = ":";
/// The ending of message content /// The ending of message content
pub const MESSAGE_ENDING: &'static str = "\r\n.\r\n"; pub const MESSAGE_ENDING: &str = "\r\n.\r\n";
/// NUL unicode character /// NUL unicode character
pub const NUL: &'static str = "\0"; pub const NUL: &str = "\0";
/// How to apply TLS to a client connection /// How to apply TLS to a client connection
#[derive(Clone)] #[derive(Clone)]
#[allow(missing_debug_implementations)]
pub enum ClientSecurity { pub enum ClientSecurity {
/// Insecure connection /// Insecure connection
None, None,
@@ -169,7 +80,8 @@ pub enum ClientSecurity {
} }
/// Configures connection reuse behavior /// Configures connection reuse behavior
#[derive(Clone, Debug)] #[derive(Clone, Debug, Copy)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum ConnectionReuseParameters { pub enum ConnectionReuseParameters {
/// Unlimitied connection reuse /// Unlimitied connection reuse
ReuseUnlimited, ReuseUnlimited,
@@ -180,6 +92,7 @@ pub enum ConnectionReuseParameters {
} }
/// Contains client configuration /// Contains client configuration
#[allow(missing_debug_implementations)]
pub struct SmtpTransportBuilder { pub struct SmtpTransportBuilder {
/// Enable connection reuse /// Enable connection reuse
connection_reuse: ConnectionReuseParameters, connection_reuse: ConnectionReuseParameters,
@@ -207,7 +120,6 @@ impl SmtpTransportBuilder {
/// Defaults are: /// Defaults are:
/// ///
/// * No connection reuse /// * No connection reuse
/// * "localhost" as EHLO name
/// * No authentication /// * No authentication
/// * No SMTPUTF8 support /// * No SMTPUTF8 support
/// * A 60 seconds timeout for smtp commands /// * A 60 seconds timeout for smtp commands
@@ -218,18 +130,16 @@ impl SmtpTransportBuilder {
let mut addresses = addr.to_socket_addrs()?; let mut addresses = addr.to_socket_addrs()?;
match addresses.next() { match addresses.next() {
Some(addr) => { Some(addr) => Ok(SmtpTransportBuilder {
Ok(SmtpTransportBuilder {
server_addr: addr, server_addr: addr,
security: security, security,
smtp_utf8: false, smtp_utf8: false,
credentials: None, credentials: None,
connection_reuse: ConnectionReuseParameters::NoReuse, connection_reuse: ConnectionReuseParameters::NoReuse,
hello_name: ClientId::Domain("localhost".to_string()), hello_name: ClientId::hostname(),
authentication_mechanism: None, authentication_mechanism: None,
timeout: Some(Duration::new(60, 0)), timeout: Some(Duration::new(60, 0)),
}) }),
}
None => Err(Error::Resolution), None => Err(Error::Resolution),
} }
} }
@@ -291,6 +201,7 @@ struct State {
} }
/// Structure that implements the high level SMTP client /// Structure that implements the high level SMTP client
#[allow(missing_debug_implementations)]
pub struct SmtpTransport { pub struct SmtpTransport {
/// Information about the server /// Information about the server
/// Value is None before HELO/EHLO /// Value is None before HELO/EHLO
@@ -310,7 +221,7 @@ macro_rules! try_smtp (
Err(err) => { Err(err) => {
if !$client.state.panic { if !$client.state.panic {
$client.state.panic = true; $client.state.panic = true;
$client.reset(); $client.close();
} }
return Err(From::from(err)) return Err(From::from(err))
}, },
@@ -322,18 +233,15 @@ impl<'a> SmtpTransport {
/// Simple and secure transport, should be used when possible. /// Simple and secure transport, should be used when possible.
/// Creates an encrypted transport over submission port, using the provided domain /// Creates an encrypted transport over submission port, using the provided domain
/// to validate TLS certificates. /// to validate TLS certificates.
pub fn simple_builder(domain: String) -> Result<SmtpTransportBuilder, Error> { pub fn simple_builder(domain: &str) -> Result<SmtpTransportBuilder, Error> {
let mut tls_builder = TlsConnector::builder()?; let mut tls_builder = TlsConnector::builder()?;
tls_builder.supported_protocols(DEFAULT_TLS_PROTOCOLS)?; tls_builder.supported_protocols(DEFAULT_TLS_PROTOCOLS)?;
let tls_parameters = ClientTlsParameters::new( let tls_parameters =
domain.clone(), ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap());
tls_builder.build().unwrap(),
);
SmtpTransportBuilder::new( SmtpTransportBuilder::new(
(domain.as_ref(), SUBMISSION_PORT), (domain, SUBMISSION_PORT),
ClientSecurity::Required(tls_parameters), ClientSecurity::Required(tls_parameters),
) )
} }
@@ -355,11 +263,10 @@ impl<'a> SmtpTransport {
/// ///
/// It does not connect to the server, but only creates the `SmtpTransport` /// It does not connect to the server, but only creates the `SmtpTransport`
pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport { pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport {
let client = Client::new(); let client = Client::new();
SmtpTransport { SmtpTransport {
client: client, client,
server_info: None, server_info: None,
client_info: builder, client_info: builder,
state: State { state: State {
@@ -370,12 +277,12 @@ impl<'a> SmtpTransport {
} }
/// Gets the EHLO response and updates server information /// Gets the EHLO response and updates server information
pub fn get_ehlo(&mut self) -> SmtpResult { fn ehlo(&mut self) -> SmtpResult {
// Extended Hello // Extended Hello
let ehlo_response = try_smtp!( let ehlo_response = try_smtp!(
self.client.smtp_command(EhloCommand::new( self.client.command(EhloCommand::new(ClientId::new(
ClientId::new(self.client_info.hello_name.to_string()), self.client_info.hello_name.to_string()
)), ),)),
self self
); );
@@ -387,15 +294,10 @@ impl<'a> SmtpTransport {
Ok(ehlo_response) Ok(ehlo_response)
} }
/// Closes the inner connection
pub fn close(&mut self) {
self.client.close();
}
/// Reset the client state /// Reset the client state
pub fn reset(&mut self) { pub fn close(&mut self) {
// Close the SMTP transaction if needed // Close the SMTP transaction if needed
self.close(); self.client.close();
// Reset the client state // Reset the client state
self.server_info = None; self.server_info = None;
@@ -408,13 +310,13 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
/// Sends an email /// Sends an email
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))] #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms, cyclomatic_complexity))]
fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SmtpResult { fn send<U: SendableEmail<'a, T> + 'a>(&mut self, email: &'a U) -> SmtpResult {
// Extract email information // Extract email information
let message_id = email.message_id(); let message_id = email.message_id();
let envelope = email.envelope();
// Check if the connection is still available // Check if the connection is still available
if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) { if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) {
self.reset(); self.close();
} }
if self.state.connection_reuse_count == 0 { if self.state.connection_reuse_count == 0 {
@@ -431,13 +333,14 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
// Log the connection // Log the connection
info!("connection established to {}", self.client_info.server_addr); info!("connection established to {}", self.client_info.server_addr);
self.get_ehlo()?; self.ehlo()?;
match ( match (
&self.client_info.security.clone(), &self.client_info.security.clone(),
self.server_info.as_ref().unwrap().supports_feature( self.server_info
Extension::StartTls, .as_ref()
), .unwrap()
.supports_feature(Extension::StartTls),
) { ) {
(&ClientSecurity::Required(_), false) => { (&ClientSecurity::Required(_), false) => {
return Err(From::from("Could not encrypt connection, aborting")) return Err(From::from("Could not encrypt connection, aborting"))
@@ -445,15 +348,15 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
(&ClientSecurity::Opportunistic(_), false) => (), (&ClientSecurity::Opportunistic(_), false) => (),
(&ClientSecurity::None, _) => (), (&ClientSecurity::None, _) => (),
(&ClientSecurity::Wrapper(_), _) => (), (&ClientSecurity::Wrapper(_), _) => (),
(&ClientSecurity::Opportunistic(ref tls_parameters), true) | (&ClientSecurity::Opportunistic(ref tls_parameters), true)
(&ClientSecurity::Required(ref tls_parameters), true) => { | (&ClientSecurity::Required(ref tls_parameters), true) => {
try_smtp!(self.client.smtp_command(StarttlsCommand), self); try_smtp!(self.client.command(StarttlsCommand), self);
try_smtp!(self.client.upgrade_tls_stream(tls_parameters), self); try_smtp!(self.client.upgrade_tls_stream(tls_parameters), self);
debug!("connection encrypted"); debug!("connection encrypted");
// Send EHLO again // Send EHLO again
self.get_ehlo()?; self.ehlo()?;
} }
} }
@@ -473,16 +376,15 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
}; };
for mechanism in accepted_mechanisms { for mechanism in accepted_mechanisms {
if self.server_info.as_ref().unwrap().supports_auth_mechanism( if self.server_info
mechanism, .as_ref()
) .unwrap()
.supports_auth_mechanism(mechanism)
{ {
found = true; found = true;
try_smtp!( try_smtp!(
self.client.auth( self.client
mechanism, .auth(mechanism, self.client_info.credentials.as_ref().unwrap(),),
self.client_info.credentials.as_ref().unwrap(),
),
self self
); );
break; break;
@@ -498,37 +400,43 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
// Mail // Mail
let mut mail_options = vec![]; let mut mail_options = vec![];
if self.server_info.as_ref().unwrap().supports_feature( if self.server_info
Extension::EightBitMime, .as_ref()
) .unwrap()
.supports_feature(Extension::EightBitMime)
{ {
mail_options.push(MailParameter::Body(MailBodyParameter::EightBitMime)); mail_options.push(MailParameter::Body(MailBodyParameter::EightBitMime));
} }
if self.server_info.as_ref().unwrap().supports_feature( if self.server_info
Extension::SmtpUtfEight, .as_ref()
) && self.client_info.smtp_utf8 .unwrap()
.supports_feature(Extension::SmtpUtfEight) && self.client_info.smtp_utf8
{ {
mail_options.push(MailParameter::SmtpUtfEight); mail_options.push(MailParameter::SmtpUtfEight);
} }
try_smtp!( try_smtp!(
self.client.smtp_command(MailCommand::new( self.client
Some(email.from().clone()), .command(MailCommand::new(envelope.from().cloned(), mail_options,)),
mail_options,
)),
self self
); );
// Log the mail command // Log the mail command
info!("{}: from=<{}>", message_id, email.from()); info!(
"{}: from=<{}>",
message_id,
match envelope.from() {
Some(address) => address.to_string(),
None => "".to_string(),
}
);
// Recipient // Recipient
for to_address in &email.to() { for to_address in envelope.to() {
try_smtp!( try_smtp!(
self.client.smtp_command( self.client
RcptCommand::new(to_address.clone(), vec![]), .command(RcptCommand::new(to_address.clone(), vec![]),),
),
self self
); );
// Log the rcpt command // Log the rcpt command
@@ -536,7 +444,7 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
} }
// Data // Data
try_smtp!(self.client.smtp_command(DataCommand), self); try_smtp!(self.client.command(DataCommand), self);
// Message content // Message content
let result = self.client.message(email.message()); let result = self.client.message(email.message());
@@ -564,8 +472,11 @@ impl<'a, T: Read + 'a> EmailTransport<'a, T, SmtpResult> for SmtpTransport {
// Test if we can reuse the existing connection // Test if we can reuse the existing connection
match self.client_info.connection_reuse { match self.client_info.connection_reuse {
ConnectionReuseParameters::ReuseLimited(limit) ConnectionReuseParameters::ReuseLimited(limit)
if self.state.connection_reuse_count >= limit => self.reset(), if self.state.connection_reuse_count >= limit =>
ConnectionReuseParameters::NoReuse => self.reset(), {
self.close()
}
ConnectionReuseParameters::NoReuse => self.close(),
_ => (), _ => (),
} }

View File

@@ -1,131 +1,91 @@
//! SMTP response, containing a mandatory return code and an optional text //! SMTP response, containing a mandatory return code and an optional text
//! message //! message
use self::Category::*; use nom::{crlf, ErrorKind as NomErrorKind, IResult as NomResult};
use self::Severity::*; use nom::simple_errors::Err as NomError;
use smtp::error::{Error, SmtpResult};
use std::fmt::{Display, Formatter, Result}; use std::fmt::{Display, Formatter, Result};
use std::result; use std::result;
use std::str::FromStr; use std::str::{FromStr, from_utf8};
/// First digit indicates severity /// First digit indicates severity
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum Severity { pub enum Severity {
/// 2yx /// 2yx
PositiveCompletion, PositiveCompletion = 2,
/// 3yz /// 3yz
PositiveIntermediate, PositiveIntermediate = 3,
/// 4yz /// 4yz
TransientNegativeCompletion, TransientNegativeCompletion = 4,
/// 5yz /// 5yz
PermanentNegativeCompletion, PermanentNegativeCompletion = 5,
}
impl FromStr for Severity {
type Err = Error;
fn from_str(s: &str) -> result::Result<Severity, Error> {
match s {
"2" => Ok(PositiveCompletion),
"3" => Ok(PositiveIntermediate),
"4" => Ok(TransientNegativeCompletion),
"5" => Ok(PermanentNegativeCompletion),
_ => Err(Error::ResponseParsing(
"First digit must be between 2 and 5",
)),
}
}
} }
impl Display for Severity { impl Display for Severity {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!( write!(f, "{}", *self as u8)
f,
"{}",
match *self {
PositiveCompletion => 2,
PositiveIntermediate => 3,
TransientNegativeCompletion => 4,
PermanentNegativeCompletion => 5,
}
)
} }
} }
/// Second digit /// Second digit
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum Category { pub enum Category {
/// x0z /// x0z
Syntax, Syntax = 0,
/// x1z /// x1z
Information, Information = 1,
/// x2z /// x2z
Connections, Connections = 2,
/// x3z /// x3z
Unspecified3, Unspecified3 = 3,
/// x4z /// x4z
Unspecified4, Unspecified4 = 4,
/// x5z /// x5z
MailSystem, MailSystem = 5,
}
impl FromStr for Category {
type Err = Error;
fn from_str(s: &str) -> result::Result<Category, Error> {
match s {
"0" => Ok(Syntax),
"1" => Ok(Information),
"2" => Ok(Connections),
"3" => Ok(Unspecified3),
"4" => Ok(Unspecified4),
"5" => Ok(MailSystem),
_ => Err(Error::ResponseParsing(
"Second digit must be between 0 and 5",
)),
}
}
} }
impl Display for Category { impl Display for Category {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!( write!(f, "{}", *self as u8)
f,
"{}",
match *self {
Syntax => 0,
Information => 1,
Connections => 2,
Unspecified3 => 3,
Unspecified4 => 4,
MailSystem => 5,
}
)
} }
} }
/// The detail digit of a response code (third digit) /// The detail digit of a response code (third digit)
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct Detail(pub u8); #[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub enum Detail {
impl FromStr for Detail { #[allow(missing_docs)]
type Err = Error; Zero = 0,
fn from_str(s: &str) -> result::Result<Detail, Error> { #[allow(missing_docs)]
match s.parse::<u8>() { One = 1,
Ok(d) if d < 10 => Ok(Detail(d)), #[allow(missing_docs)]
_ => Err(Error::ResponseParsing( Two = 2,
"Third digit must be between 0 and 9", #[allow(missing_docs)]
)), Three = 3,
} #[allow(missing_docs)]
} Four = 4,
#[allow(missing_docs)]
Five = 5,
#[allow(missing_docs)]
Six = 6,
#[allow(missing_docs)]
Seven = 7,
#[allow(missing_docs)]
Eight = 8,
#[allow(missing_docs)]
Nine = 9,
} }
impl Display for Detail { impl Display for Detail {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.0) write!(f, "{}", *self as u8)
} }
} }
/// Represents a 3 digit SMTP response code /// Represents a 3 digit SMTP response code
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct Code { pub struct Code {
/// First digit of the response code /// First digit of the response code
pub severity: Severity, pub severity: Severity,
@@ -141,99 +101,13 @@ impl Display for Code {
} }
} }
impl FromStr for Code {
type Err = Error;
#[inline]
fn from_str(s: &str) -> result::Result<Code, Error> {
if s.len() == 3 {
match (
s[0..1].parse::<Severity>(),
s[1..2].parse::<Category>(),
s[2..3].parse::<Detail>(),
) {
(Ok(severity), Ok(category), Ok(detail)) => {
Ok(Code {
severity: severity,
category: category,
detail: detail,
})
}
_ => Err(Error::ResponseParsing("Could not parse response code")),
}
} else {
Err(Error::ResponseParsing(
"Wrong code length (should be 3 digit)",
))
}
}
}
impl Code { impl Code {
/// Creates a new `Code` structure /// Creates a new `Code` structure
pub fn new(severity: Severity, category: Category, detail: Detail) -> Code { pub fn new(severity: Severity, category: Category, detail: Detail) -> Code {
if detail.0 > 9 {
panic!("The detail code must be between 0 and 9");
}
Code { Code {
severity: severity, severity,
category: category, category,
detail: detail, detail,
}
}
}
/// Parses an SMTP response
#[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct ResponseParser {
/// Response code
code: Option<Code>,
/// Server response string (optional)
/// Handle multiline responses
message: Vec<String>,
}
impl ResponseParser {
/// Parses a line and return a `bool` indicating if there are more lines to come
pub fn read_line(&mut self, line: &str) -> result::Result<bool, Error> {
if line.len() < 3 {
return Err(Error::ResponseParsing(
"Incorrect response code (should be 3 digits)",
));
}
match self.code {
Some(ref code) => {
if code.to_string() != line[0..3] {
return Err(Error::ResponseParsing(
"Response code has changed during a \
reponse",
));
}
}
None => self.code = Some(line[0..3].parse::<Code>()?),
}
if line.len() > 4 {
self.message.push(line[4..].to_string());
Ok(line.as_bytes()[3] == b'-')
} else {
Ok(false)
}
}
/// Builds a response from a `ResponseParser`
pub fn response(self) -> SmtpResult {
match self.code {
Some(code) => Ok(Response::new(code, self.message)),
None => {
Err(Error::ResponseParsing(
"Incomplete response, could not read response \
code",
))
}
} }
} }
} }
@@ -242,6 +116,7 @@ impl ResponseParser {
/// ///
/// The text message is optional, only the code is mandatory /// The text message is optional, only the code is mandatory
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct Response { pub struct Response {
/// Response code /// Response code
pub code: Code, pub code: Code,
@@ -250,33 +125,42 @@ pub struct Response {
pub message: Vec<String>, pub message: Vec<String>,
} }
impl FromStr for Response {
type Err = NomError;
fn from_str(s: &str) -> result::Result<Response, NomError> {
match parse_response(s.as_bytes()) {
NomResult::Done(_, res) => Ok(res),
NomResult::Error(e) => Err(e),
NomResult::Incomplete(_) => Err(NomErrorKind::Complete),
}
}
}
impl Response { impl Response {
/// Creates a new `Response` /// Creates a new `Response`
pub fn new(code: Code, message: Vec<String>) -> Response { pub fn new(code: Code, message: Vec<String>) -> Response {
Response { Response { code, message }
code: code,
message: message,
}
} }
/// Tells if the response is positive /// Tells if the response is positive
pub fn is_positive(&self) -> bool { pub fn is_positive(&self) -> bool {
match self.code.severity { match self.code.severity {
PositiveCompletion | PositiveIntermediate => true, Severity::PositiveCompletion | Severity::PositiveIntermediate => true,
_ => false, _ => false,
} }
} }
/// Tests code equality /// Tests code equality
pub fn has_code(&self, code: u16) -> bool { pub fn has_code(&self, code: u16) -> bool {
self.code.to_string() == format!("{}", code) self.code.to_string() == code.to_string()
} }
/// Returns only the first word of the message if possible /// Returns only the first word of the message if possible
pub fn first_word(&self) -> Option<&str> { pub fn first_word(&self) -> Option<&str> {
self.message.get(0).and_then( self.message
|line| line.split_whitespace().next(), .get(0)
) .and_then(|line| line.split_whitespace().next())
} }
/// Returns only the line of the message if possible /// Returns only the line of the message if possible
@@ -285,36 +169,109 @@ impl Response {
} }
} }
// Parsers (originaly from tokio-smtp)
named!(
parse_code<Code>,
map!(
tuple!(parse_severity, parse_category, parse_detail),
|(severity, category, detail)| Code {
severity,
category,
detail,
}
)
);
named!(
parse_severity<Severity>,
alt!(
tag!("2") => { |_| Severity::PositiveCompletion } |
tag!("3") => { |_| Severity::PositiveIntermediate } |
tag!("4") => { |_| Severity::TransientNegativeCompletion } |
tag!("5") => { |_| Severity::PermanentNegativeCompletion }
)
);
named!(
parse_category<Category>,
alt!(
tag!("0") => { |_| Category::Syntax } |
tag!("1") => { |_| Category::Information } |
tag!("2") => { |_| Category::Connections } |
tag!("3") => { |_| Category::Unspecified3 } |
tag!("4") => { |_| Category::Unspecified4 } |
tag!("5") => { |_| Category::MailSystem }
)
);
named!(
parse_detail<Detail>,
alt!(
tag!("0") => { |_| Detail::Zero } |
tag!("1") => { |_| Detail::One } |
tag!("2") => { |_| Detail::Two } |
tag!("3") => { |_| Detail::Three } |
tag!("4") => { |_| Detail::Four} |
tag!("5") => { |_| Detail::Five } |
tag!("6") => { |_| Detail::Six} |
tag!("7") => { |_| Detail::Seven } |
tag!("8") => { |_| Detail::Eight } |
tag!("9") => { |_| Detail::Nine }
)
);
named!(
parse_response<Response>,
map_res!(
tuple!(
// Parse any number of continuation lines.
many0!(tuple!(
parse_code,
preceded!(char!('-'), take_until_and_consume!(b"\r\n".as_ref()))
)),
// Parse the final line.
tuple!(
parse_code,
terminated!(
opt!(preceded!(char!(' '), take_until!(b"\r\n".as_ref()))),
crlf
)
)
),
|(lines, (last_code, last_line)): (Vec<_>, _)| {
// Check that all codes are equal.
if !lines.iter().all(|&(ref code, _)| *code == last_code) {
return Err(());
}
// Extract text from lines, and append last line.
let mut lines = lines.into_iter().map(|(_, text)| text).collect::<Vec<_>>();
if let Some(text) = last_line {
lines.push(text);
}
Ok(Response {
code: last_code,
message: lines
.into_iter()
.map(|line| from_utf8(line).map(|s| s.to_string()))
.collect::<result::Result<Vec<_>, _>>()
.map_err(|_| ())?,
})
}
)
);
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Category, Code, Detail, Response, ResponseParser, Severity}; use super::{Category, Code, Detail, Response, Severity};
#[test]
fn test_severity_from_str() {
assert_eq!(
"2".parse::<Severity>().unwrap(),
Severity::PositiveCompletion
);
assert_eq!(
"4".parse::<Severity>().unwrap(),
Severity::TransientNegativeCompletion
);
assert!("1".parse::<Severity>().is_err());
assert!("51".parse::<Severity>().is_err());
}
#[test] #[test]
fn test_severity_fmt() { fn test_severity_fmt() {
assert_eq!(format!("{}", Severity::PositiveCompletion), "2"); assert_eq!(format!("{}", Severity::PositiveCompletion), "2");
} }
#[test]
fn test_category_from_str() {
assert_eq!("2".parse::<Category>().unwrap(), Category::Connections);
assert_eq!("4".parse::<Category>().unwrap(), Category::Unspecified4);
assert!("6".parse::<Category>().is_err());
}
#[test] #[test]
fn test_category_fmt() { fn test_category_fmt() {
assert_eq!(format!("{}", Category::Unspecified4), "4"); assert_eq!(format!("{}", Category::Unspecified4), "4");
@@ -326,121 +283,37 @@ mod test {
Code::new( Code::new(
Severity::TransientNegativeCompletion, Severity::TransientNegativeCompletion,
Category::Connections, Category::Connections,
Detail(0), Detail::Zero,
), ),
Code { Code {
severity: Severity::TransientNegativeCompletion, severity: Severity::TransientNegativeCompletion,
category: Category::Connections, category: Category::Connections,
detail: Detail(0), detail: Detail::Zero,
} }
); );
} }
#[test]
#[should_panic]
fn test_code_new_panic() {
let _ = Code::new(
Severity::TransientNegativeCompletion,
Category::Connections,
Detail(11),
);
}
#[test]
fn test_code_from_str() {
assert_eq!(
"421".parse::<Code>().unwrap(),
Code {
severity: Severity::TransientNegativeCompletion,
category: Category::Connections,
detail: "1".parse::<Detail>().unwrap(),
}
);
assert!("2222".parse::<Code>().is_err());
assert!("aaa".parse::<Code>().is_err());
assert!("-32".parse::<Code>().is_err());
assert!("-333".parse::<Code>().is_err());
assert!("".parse::<Code>().is_err());
assert!("292".parse::<Code>().is_err());
}
#[test] #[test]
fn test_code_display() { fn test_code_display() {
let code = Code { let code = Code {
severity: Severity::TransientNegativeCompletion, severity: Severity::TransientNegativeCompletion,
category: Category::Connections, category: Category::Connections,
detail: Detail(1), detail: Detail::One,
}; };
assert_eq!(code.to_string(), "421"); assert_eq!(code.to_string(), "421");
} }
#[test] #[test]
fn test_response_new() { fn test_response_from_str() {
let raw_response = "250-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250 AUTH PLAIN CRAM-MD5\r\n";
assert_eq!( assert_eq!(
Response::new( raw_response.parse::<Response>().unwrap(),
Code {
severity: "2".parse::<Severity>().unwrap(),
category: "4".parse::<Category>().unwrap(),
detail: "1".parse::<Detail>().unwrap(),
},
vec![
"me".to_string(),
"8BITMIME".to_string(),
"SIZE 42".to_string(),
],
),
Response {
code: Code {
severity: Severity::PositiveCompletion,
category: Category::Unspecified4,
detail: "1".parse::<Detail>().unwrap(),
},
message: vec![
"me".to_string(),
"8BITMIME".to_string(),
"SIZE 42".to_string(),
],
}
);
assert_eq!(
Response::new(
Code {
severity: "2".parse::<Severity>().unwrap(),
category: "4".parse::<Category>().unwrap(),
detail: "1".parse::<Detail>().unwrap(),
},
vec![],
),
Response {
code: Code {
severity: Severity::PositiveCompletion,
category: Category::Unspecified4,
detail: "1".parse::<Detail>().unwrap(),
},
message: vec![],
}
);
}
#[test]
fn test_response_parser() {
let mut parser = ResponseParser::default();
assert!(parser.read_line("250-me").unwrap());
assert!(parser.read_line("250-8BITMIME").unwrap());
assert!(parser.read_line("250-SIZE 42").unwrap());
assert!(!parser.read_line("250 AUTH PLAIN CRAM-MD5").unwrap());
let response = parser.response().unwrap();
assert_eq!(
response,
Response { Response {
code: Code { code: Code {
severity: Severity::PositiveCompletion, severity: Severity::PositiveCompletion,
category: Category::MailSystem, category: Category::MailSystem,
detail: Detail(0), detail: Detail::Zero,
}, },
message: vec![ message: vec![
"me".to_string(), "me".to_string(),
@@ -450,6 +323,12 @@ mod test {
], ],
} }
); );
let wrong_code = "2506-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250 AUTH PLAIN CRAM-MD5\r\n";
assert!(wrong_code.parse::<Response>().is_err());
let wrong_end = "250-me\r\n250-8BITMIME\r\n250-SIZE 42\r\n250-AUTH PLAIN CRAM-MD5\r\n";
assert!(wrong_end.parse::<Response>().is_err());
} }
#[test] #[test]
@@ -457,9 +336,9 @@ mod test {
assert!( assert!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::PositiveCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::Zero,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -470,9 +349,9 @@ mod test {
); );
assert!(!Response::new( assert!(!Response::new(
Code { Code {
severity: "5".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::Zero,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -487,29 +366,29 @@ mod test {
assert!( assert!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "4".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
"8BITMIME".to_string(), "8BITMIME".to_string(),
"SIZE 42".to_string(), "SIZE 42".to_string(),
], ],
).has_code(241) ).has_code(451)
); );
assert!(!Response::new( assert!(!Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "5".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
"8BITMIME".to_string(), "8BITMIME".to_string(),
"SIZE 42".to_string(), "SIZE 42".to_string(),
], ],
).has_code(241)); ).has_code(251));
} }
#[test] #[test]
@@ -517,9 +396,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -532,9 +411,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me mo".to_string(), "me mo".to_string(),
@@ -547,9 +426,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![], vec![],
).first_word(), ).first_word(),
@@ -558,9 +437,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![" ".to_string()], vec![" ".to_string()],
).first_word(), ).first_word(),
@@ -569,9 +448,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![" ".to_string()], vec![" ".to_string()],
).first_word(), ).first_word(),
@@ -580,9 +459,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec!["".to_string()], vec!["".to_string()],
).first_word(), ).first_word(),
@@ -595,9 +474,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me".to_string(), "me".to_string(),
@@ -610,9 +489,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![ vec![
"me mo".to_string(), "me mo".to_string(),
@@ -625,9 +504,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![], vec![],
).first_line(), ).first_line(),
@@ -636,9 +515,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![" ".to_string()], vec![" ".to_string()],
).first_line(), ).first_line(),
@@ -647,9 +526,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec![" ".to_string()], vec![" ".to_string()],
).first_line(), ).first_line(),
@@ -658,9 +537,9 @@ mod test {
assert_eq!( assert_eq!(
Response::new( Response::new(
Code { Code {
severity: "2".parse::<Severity>().unwrap(), severity: Severity::TransientNegativeCompletion,
category: "3".parse::<Category>().unwrap(), category: Category::MailSystem,
detail: "1".parse::<Detail>().unwrap(), detail: Detail::One,
}, },
vec!["".to_string()], vec!["".to_string()],
).first_line(), ).first_line(),

View File

@@ -4,6 +4,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
/// Encode a string as xtext /// Encode a string as xtext
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct XText<'a>(pub &'a str); pub struct XText<'a>(pub &'a str);
impl<'a> Display for XText<'a> { impl<'a> Display for XText<'a> {
@@ -27,7 +28,6 @@ impl<'a> Display for XText<'a> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::XText; use super::XText;
@@ -39,8 +39,7 @@ mod tests {
("bjørn", "bjørn"), ("bjørn", "bjørn"),
("Ø+= ❤️‰", "Ø+2B+3D+20❤"), ("Ø+= ❤️‰", "Ø+2B+3D+20❤"),
("+", "+2B"), ("+", "+2B"),
] ] {
{
assert_eq!(format!("{}", XText(input)), expect); assert_eq!(format!("{}", XText(input)), expect);
} }
} }

View File

@@ -1,34 +1,13 @@
//! The stub transport only logs message envelope and drops the content. It can be useful for //! The stub transport only logs message envelope and drops the content. It can be useful for
//! testing purposes. //! testing purposes.
//! //!
//! ```rust
//! use lettre::stub::StubEmailTransport;
//! use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
//!
//! let email = SimpleSendableEmail::new(
//! EmailAddress::new("user@localhost".to_string()),
//! vec![EmailAddress::new("root@localhost".to_string())],
//! "message_id".to_string(),
//! "Hello world".to_string(),
//! );
//!
//! let mut sender = StubEmailTransport::new_positive();
//! let result = sender.send(&email);
//! assert!(result.is_ok());
//! ```
//!
//! Will log (when using a logger like `env_logger`):
//!
//! ```text
//! b7c211bc-9811-45ce-8cd9-68eab575d695: from=<user@localhost> to=<root@localhost>
//! ```
use EmailTransport; use EmailTransport;
use SendableEmail; use SendableEmail;
use std::io::Read; use std::io::Read;
/// This transport logs the message envelope and returns the given response /// This transport logs the message envelope and returns the given response
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct StubEmailTransport { pub struct StubEmailTransport {
response: StubResult, response: StubResult,
} }
@@ -36,14 +15,12 @@ pub struct StubEmailTransport {
impl StubEmailTransport { impl StubEmailTransport {
/// Creates a new transport that always returns the given response /// Creates a new transport that always returns the given response
pub fn new(response: StubResult) -> StubEmailTransport { pub fn new(response: StubResult) -> StubEmailTransport {
StubEmailTransport { response: response } StubEmailTransport { response }
} }
/// Creates a new transport that always returns a success response /// Creates a new transport that always returns a success response
pub fn new_positive() -> StubEmailTransport { pub fn new_positive() -> StubEmailTransport {
StubEmailTransport { StubEmailTransport { response: Ok(()) }
response: Ok(()),
}
} }
} }
@@ -52,12 +29,15 @@ pub type StubResult = Result<(), ()>;
impl<'a, T: Read + 'a> EmailTransport<'a, T, StubResult> for StubEmailTransport { impl<'a, T: Read + 'a> EmailTransport<'a, T, StubResult> for StubEmailTransport {
fn send<U: SendableEmail<'a, T>>(&mut self, email: &'a U) -> StubResult { fn send<U: SendableEmail<'a, T>>(&mut self, email: &'a U) -> StubResult {
let envelope = email.envelope();
info!( info!(
"{}: from=<{}> to=<{:?}>", "{}: from=<{}> to=<{:?}>",
email.message_id(), email.message_id(),
email.from(), match envelope.from() {
email.to() Some(address) => address.to_string(),
None => "".to_string(),
},
envelope.to()
); );
self.response self.response
} }

62
lettre/tests/skeptic.rs Normal file
View File

@@ -0,0 +1,62 @@
extern crate glob;
use self::glob::glob;
use std::env::consts::EXE_EXTENSION;
use std::env;
use std::path::Path;
use std::process::Command;
#[test]
fn test_readme() {
let readme = Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("README.md");
skeptic_test(&readme);
}
#[test]
fn book_test() {
let mut book_path = env::current_dir().unwrap();
book_path.push(
Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("../website/content/sending-messages"),
); // For some reasons, calling .parent() once more gives us None...
for md in glob(&format!("{}/*.md", book_path.to_str().unwrap())).unwrap() {
skeptic_test(&md.unwrap());
}
}
fn skeptic_test(path: &Path) {
let rustdoc = Path::new("rustdoc").with_extension(EXE_EXTENSION);
let exe = env::current_exe().unwrap();
let depdir = exe.parent().unwrap();
let mut cmd = Command::new(rustdoc);
cmd.args(&["--verbose", "--test"])
.arg("-L")
.arg(&depdir)
.arg(path);
let result = cmd.spawn()
.expect("Failed to spawn process")
.wait()
.expect("Failed to run process");
assert!(
result.success(),
format!("Failed to run rustdoc tests on {:?}", path)
);
}

View File

@@ -4,7 +4,7 @@ extern crate lettre;
#[cfg(feature = "file-transport")] #[cfg(feature = "file-transport")]
mod test { mod test {
use lettre::{EmailAddress, EmailTransport, SendableEmail, SimpleSendableEmail}; use lettre::{EmailTransport, SendableEmail, SimpleSendableEmail};
use lettre::file::FileEmailTransport; use lettre::file::FileEmailTransport;
use std::env::temp_dir; use std::env::temp_dir;
use std::fs::File; use std::fs::File;
@@ -15,11 +15,11 @@ mod test {
fn file_transport() { fn file_transport() {
let mut sender = FileEmailTransport::new(temp_dir()); let mut sender = FileEmailTransport::new(temp_dir());
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"file_id".to_string(), "file_id".to_string(),
"Hello file".to_string(), "Hello file".to_string(),
); ).unwrap();
let result = sender.send(&email); let result = sender.send(&email);
assert!(result.is_ok()); assert!(result.is_ok());
@@ -31,8 +31,7 @@ mod test {
assert_eq!( assert_eq!(
buffer, buffer,
"{\"to\":[\"root@localhost\"],\"from\":\"user@localhost\",\"message_id\":\ "{\"envelope\":{\"forward_path\":[\"root@localhost\"],\"reverse_path\":\"user@localhost\"},\"message_id\":\"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}"
\"file_id\",\"message\":[72,101,108,108,111,32,102,105,108,101]}"
); );
remove_file(file).unwrap(); remove_file(file).unwrap();

View File

@@ -4,18 +4,18 @@ extern crate lettre;
#[cfg(feature = "sendmail-transport")] #[cfg(feature = "sendmail-transport")]
mod test { mod test {
use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail}; use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::sendmail::SendmailTransport; use lettre::sendmail::SendmailTransport;
#[test] #[test]
fn sendmail_transport_simple() { fn sendmail_transport_simple() {
let mut sender = SendmailTransport::new(); let mut sender = SendmailTransport::new();
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"sendmail_id".to_string(), "sendmail_id".to_string(),
"Hello sendmail".to_string(), "Hello sendmail".to_string(),
); ).unwrap();
let result = sender.send(&email); let result = sender.send(&email);
println!("{:?}", result); println!("{:?}", result);

View File

@@ -4,7 +4,7 @@ extern crate lettre;
#[cfg(feature = "smtp-transport")] #[cfg(feature = "smtp-transport")]
mod test { mod test {
use lettre::{ClientSecurity, EmailAddress, EmailTransport, SimpleSendableEmail, SmtpTransport}; use lettre::{ClientSecurity, EmailTransport, SimpleSendableEmail, SmtpTransport};
#[test] #[test]
fn smtp_transport_simple() { fn smtp_transport_simple() {
@@ -12,11 +12,11 @@ mod test {
.unwrap() .unwrap()
.build(); .build();
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"smtp_id".to_string(), "smtp_id".to_string(),
"Hello smtp".to_string(), "Hello smtp".to_string(),
); ).unwrap();
sender.send(&email).unwrap(); sender.send(&email).unwrap();
} }

View File

@@ -1,19 +1,18 @@
extern crate lettre; extern crate lettre;
use lettre::{EmailAddress, EmailTransport, SimpleSendableEmail}; use lettre::{EmailTransport, SimpleSendableEmail};
use lettre::stub::StubEmailTransport; use lettre::stub::StubEmailTransport;
#[test] #[test]
fn stub_transport() { fn stub_transport() {
let mut sender_ok = StubEmailTransport::new_positive(); let mut sender_ok = StubEmailTransport::new_positive();
let mut sender_ko = StubEmailTransport::new(Err(())); let mut sender_ko = StubEmailTransport::new(Err(()));
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"stub_id".to_string(), "stub_id".to_string(),
"Hello stub".to_string(), "Hello stub".to_string(),
); ).unwrap();
sender_ok.send(&email).unwrap(); sender_ok.send(&email).unwrap();
sender_ko.send(&email).unwrap_err(); sender_ko.send(&email).unwrap_err();

View File

@@ -1,48 +0,0 @@
### v0.7.0 (2017-10-08)
#### Features
* **all**
* Split into the *lettre* and *lettre_email* crates
* A lot of small improvements
* **email**
* Initial (incomplete) attachments support
### v0.6.2 (2017-02-18)
#### Features
* **all**
* Update uuid crate to 0.4
### v0.6.1 (2016-10-19)
#### Features
* **documentation**
* #91: Build seperate docs for each release
* #96: Add complete documentation information to README
#### Bugfixes
* **email**
* #85: Use address-list for "To", "From" etc.
* **tests**
* #93: Force building tests before coverage computing
### v0.6.0 (2016-05-05)
#### Features
* **email**
* multipart support
* add non-consuming methods for Email builders
#### Beaking Change
* **email**
* `add_header` does not return the builder anymore,
for consistency with other methods. Use the `header`
method instead

1
lettre_email/CHANGELOG.md Symbolic link
View File

@@ -0,0 +1 @@
../CHANGELOG.md

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre_email" name = "lettre_email"
version = "0.7.0" version = "0.8.1" # remember to update html_root_url
description = "Email builder" description = "Email builder"
readme = "README.md" readme = "README.md"
documentation = "https://docs.rs/lettre_email/" documentation = "https://docs.rs/lettre_email/"
@@ -15,12 +15,13 @@ keywords = ["email", "mailer"]
travis-ci = { repository = "lettre/lettre_email" } travis-ci = { repository = "lettre/lettre_email" }
[dev-dependencies] [dev-dependencies]
env_logger = "*" lettre = { version = "^0.8", path = "../lettre", features = ["smtp-transport"] }
lettre = { path = "../lettre", features = ["smtp-transport"] } glob = "0.2"
[dependencies] [dependencies]
email = "^0.0" email = "^0.0"
mime = "^0.3" mime = "^0.3"
time = "^0.1" time = "^0.1"
uuid = { version = ">=0.4, <0.6", features = ["v4"] } uuid = { version = "^0.6", features = ["v4"] }
lettre = { path = "../lettre", default-features = false } lettre = { version = "^0.8", path = "../lettre", default-features = false }
base64 = "^0.9"

View File

@@ -14,7 +14,7 @@ fn main() {
.from("user@example.com") .from("user@example.com")
.subject("Hi, Hello world") .subject("Hi, Hello world")
.text("Hello world.") .text("Hello world.")
.attachment(Path::new("Cargo.toml"), None, mime::TEXT_PLAIN).unwrap() .attachment(Path::new("Cargo.toml"), None, &mime::TEXT_PLAIN).unwrap()
.build() .build()
.unwrap(); .unwrap();

View File

@@ -1 +0,0 @@
../rustfmt.toml

View File

@@ -2,16 +2,16 @@
use self::Error::*; use self::Error::*;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::io;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::io;
use lettre;
/// An enum of all error kinds. /// An enum of all error kinds.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Missing sender /// Envelope error
MissingFrom, Email(lettre::Error),
/// Missing recipient
MissingTo,
/// Unparseable filename for attachment /// Unparseable filename for attachment
CannotParseFilename, CannotParseFilename,
/// IO error /// IO error
@@ -27,8 +27,7 @@ impl Display for Error {
impl StdError for Error { impl StdError for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
MissingFrom => "the sender is missing", Email(ref err) => err.description(),
MissingTo => "the recipient is missing",
CannotParseFilename => "the attachment filename could not be parsed", CannotParseFilename => "the attachment filename could not be parsed",
Io(ref err) => err.description(), Io(ref err) => err.description(),
} }
@@ -41,3 +40,8 @@ impl From<io::Error> for Error {
} }
} }
impl From<lettre::Error> for Error {
fn from(err: lettre::Error) -> Error {
Email(err)
}
}

View File

@@ -1,76 +1,30 @@
//! Lettre is a mailer written in Rust. It provides a simple email builder and several transports. //! Lettre is a mailer written in Rust. lettre_email provides a simple email builder.
//!
//! ## Overview
//!
//! The `email` part builds email messages. For now, it does not support attachments.
//! An email is built using an `EmailBuilder`. The simplest email could be:
//!
//! ```rust
//! use lettre_email::EmailBuilder;
//!
//! // Create an email
//! let email = EmailBuilder::new()
//! // Addresses can be specified by the tuple (email, alias)
//! .to(("user@example.org", "Firstname Lastname"))
//! // ... or by an address only
//! .from("user@example.com")
//! .subject("Hi, Hello world")
//! .text("Hello world.")
//! .build();
//!
//! assert!(email.is_ok());
//! ```
//!
//! When the `build` method is called, the `EmailBuilder` will add the missing headers (like
//! `Message-ID` or `Date`) and check for missing necessary ones (like `From` or `To`). It will
//! then generate an `Email` that can be sent.
//!
//! The `text()` method will create a plain text email, while the `html()` method will create an
//! HTML email. You can use the `alternative()` method to provide both versions, using plain text
//! as fallback for the HTML version.
//!
//! Below is a more complete example, not using method chaining:
//!
//! ```rust
//! use lettre_email::EmailBuilder;
//!
//! let mut builder = EmailBuilder::new();
//! builder.add_to(("user@example.org", "Alias name"));
//! builder.add_cc(("user@example.net", "Alias name"));
//! builder.add_from("no-reply@example.com");
//! builder.add_from("no-reply@example.eu");
//! builder.set_sender("no-reply@example.com");
//! builder.set_subject("Hello world");
//! builder.set_alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.");
//! builder.add_reply_to("contact@example.com");
//! builder.add_header(("X-Custom-Header", "my header"));
//!
//! let email = builder.build();
//! assert!(email.is_ok());
//! ```
//!
//! See the `EmailBuilder` documentation for a complete list of methods.
//! //!
#![deny(missing_docs, unsafe_code, unstable_features, warnings, missing_debug_implementations)] #![doc(html_root_url = "https://docs.rs/lettre_email/0.8.1")]
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
unused_qualifications)]
extern crate base64;
extern crate email as email_format;
extern crate lettre;
extern crate mime; extern crate mime;
extern crate time; extern crate time;
extern crate uuid; extern crate uuid;
extern crate email as email_format;
extern crate lettre;
pub mod error; pub mod error;
pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType}; pub use email_format::{Address, Header, Mailbox, MimeMessage, MimeMultipartType};
use error::Error; use error::Error;
use lettre::{EmailAddress, SendableEmail}; use lettre::{EmailAddress, Envelope, Error as EmailError, SendableEmail};
use mime::Mime; use mime::Mime;
use time::{Tm, now};
use uuid::Uuid;
use std::fs::File; use std::fs::File;
use std::path::Path;
use std::io::Read; use std::io::Read;
use std::path::Path;
use time::{now, Tm};
use uuid::Uuid;
use std::str::FromStr;
/// Converts an address or an address with an alias to a `Header` /// Converts an address or an address with an alias to a `Header`
pub trait IntoHeader { pub trait IntoHeader {
@@ -169,7 +123,6 @@ impl IntoEmail for SimpleEmail {
} }
} }
/// Simple representation of an email, useful for some transports /// Simple representation of an email, useful for some transports
#[derive(PartialEq, Eq, Clone, Debug, Default)] #[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct SimpleEmail { pub struct SimpleEmail {
@@ -322,7 +275,6 @@ impl Default for PartBuilder {
} }
} }
/// Builds an `Email` structure /// Builds an `Email` structure
#[derive(PartialEq, Eq, Clone, Debug, Default)] #[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct EmailBuilder { pub struct EmailBuilder {
@@ -346,43 +298,6 @@ pub struct EmailBuilder {
date_issued: bool, date_issued: bool,
} }
/// Simple email enveloppe representation
#[derive(PartialEq, Eq, Clone, Debug, Default)]
pub struct Envelope {
/// The envelope recipients' addresses
pub to: Vec<String>,
/// The envelope sender address
pub from: String,
}
impl Envelope {
/// Constructs an envelope with no receivers and an empty sender
pub fn new() -> Self {
Envelope {
to: vec![],
from: String::new(),
}
}
/// Adds a receiver
pub fn to<S: Into<String>>(mut self, address: S) -> Self {
self.add_to(address);
self
}
/// Adds a receiver
pub fn add_to<S: Into<String>>(&mut self, address: S) {
self.to.push(address.into());
}
/// Sets the sender
pub fn from<S: Into<String>>(mut self, address: S) -> Self {
self.set_from(address);
self
}
/// Sets the sender
pub fn set_from<S: Into<String>>(&mut self, address: S) {
self.from = address.into();
}
}
/// Simple email representation /// Simple email representation
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct Email { pub struct Email {
@@ -397,7 +312,9 @@ pub struct Email {
impl PartBuilder { impl PartBuilder {
/// Creates a new empty part /// Creates a new empty part
pub fn new() -> PartBuilder { pub fn new() -> PartBuilder {
PartBuilder { message: MimeMessage::new_blank_message() } PartBuilder {
message: MimeMessage::new_blank_message(),
}
} }
/// Adds a generic header /// Adds a generic header
@@ -434,13 +351,13 @@ impl PartBuilder {
} }
/// Adds a `ContentType` header with the given MIME type /// Adds a `ContentType` header with the given MIME type
pub fn content_type(mut self, content_type: Mime) -> PartBuilder { pub fn content_type(mut self, content_type: &Mime) -> PartBuilder {
self.set_content_type(content_type); self.set_content_type(content_type);
self self
} }
/// Adds a `ContentType` header with the given MIME type /// Adds a `ContentType` header with the given MIME type
pub fn set_content_type(&mut self, content_type: Mime) { pub fn set_content_type(&mut self, content_type: &Mime) {
self.add_header(("Content-Type", format!("{}", content_type).as_ref())); self.add_header(("Content-Type", format!("{}", content_type).as_ref()));
} }
@@ -580,9 +497,8 @@ impl EmailBuilder {
/// Adds a `Subject` header /// Adds a `Subject` header
pub fn set_subject<S: Into<String>>(&mut self, subject: S) { pub fn set_subject<S: Into<String>>(&mut self, subject: S) {
self.message.add_header( self.message
("Subject".to_string(), subject.into()), .add_header(("Subject".to_string(), subject.into()));
);
} }
/// Adds a `Date` header with the given date /// Adds a `Date` header with the given date
@@ -593,26 +509,35 @@ impl EmailBuilder {
/// Adds a `Date` header with the given date /// Adds a `Date` header with the given date
pub fn set_date(&mut self, date: &Tm) { pub fn set_date(&mut self, date: &Tm) {
self.message.add_header( self.message
("Date", Tm::rfc822z(date).to_string()), .add_header(("Date", Tm::rfc822z(date).to_string()));
);
self.date_issued = true; self.date_issued = true;
} }
/// Adds an attachment to the email /// Adds an attachment to the email
pub fn attachment(mut self, path: &Path, filename: Option<&str>, content_type: Mime) -> Result<EmailBuilder, Error> { pub fn attachment(
mut self,
path: &Path,
filename: Option<&str>,
content_type: &Mime,
) -> Result<EmailBuilder, Error> {
self.set_attachment(path, filename, content_type)?; self.set_attachment(path, filename, content_type)?;
Ok(self) Ok(self)
} }
/// Adds an attachment to the email /// Adds an attachment to the email
/// If filename is not provided, the name of the file will be used. /// If filename is not provided, the name of the file will be used.
pub fn set_attachment(&mut self, path: &Path, filename: Option<&str>, content_type: Mime) -> Result<(), Error> { pub fn set_attachment(
&mut self,
path: &Path,
filename: Option<&str>,
content_type: &Mime,
) -> Result<(), Error> {
let file = File::open(path); let file = File::open(path);
let body = match file { let body = match file {
Ok(mut f) => { Ok(mut f) => {
let mut data = String::new(); let mut data = Vec::new();
let read = f.read_to_string(&mut data); let read = f.read_to_end(&mut data);
match read { match read {
Ok(_) => data, Ok(_) => data,
Err(e) => { Err(e) => {
@@ -636,10 +561,15 @@ impl EmailBuilder {
}, },
}; };
let encoded_body = base64::encode(&body);
let content = PartBuilder::new() let content = PartBuilder::new()
.body(body) .body(encoded_body)
.header(("Content-Disposition", format!("attachment; filename=\"{}\"", actual_filename))) .header((
"Content-Disposition",
format!("attachment; filename=\"{}\"", actual_filename),
))
.header(("Content-Type", content_type.to_string())) .header(("Content-Type", content_type.to_string()))
.header(("Content-Transfer-Encoding", "base64"))
.build(); .build();
self.set_message_type(MimeMultipartType::Mixed); self.set_message_type(MimeMultipartType::Mixed);
@@ -678,11 +608,14 @@ impl EmailBuilder {
/// Sets the email body to plain text content /// Sets the email body to plain text content
pub fn set_text<S: Into<String>>(&mut self, body: S) { pub fn set_text<S: Into<String>>(&mut self, body: S) {
self.message.set_body(body); let text = PartBuilder::new()
self.message.add_header(( .body(body)
.header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(), format!("{}", mime::TEXT_PLAIN_UTF_8).as_ref(),
)); ))
.build();
self.add_child(text);
} }
/// Sets the email body to HTML content /// Sets the email body to HTML content
@@ -693,11 +626,14 @@ impl EmailBuilder {
/// Sets the email body to HTML content /// Sets the email body to HTML content
pub fn set_html<S: Into<String>>(&mut self, body: S) { pub fn set_html<S: Into<String>>(&mut self, body: S) {
self.message.set_body(body); let html = PartBuilder::new()
self.message.add_header(( .body(body)
.header((
"Content-Type", "Content-Type",
format!("{}", mime::TEXT_HTML).as_ref(), format!("{}", mime::TEXT_HTML_UTF_8).as_ref(),
)); ))
.build();
self.add_child(html);
} }
/// Sets the email content /// Sets the email content
@@ -729,7 +665,10 @@ impl EmailBuilder {
let html = PartBuilder::new() let html = PartBuilder::new()
.body(body_html) .body(body_html)
.header(("Content-Type", format!("{}", mime::TEXT_HTML).as_ref())) .header((
"Content-Type",
format!("{}", mime::TEXT_HTML_UTF_8).as_ref(),
))
.build(); .build();
alternate.add_child(text); alternate.add_child(text);
@@ -780,25 +719,21 @@ impl EmailBuilder {
Some(e) => e, Some(e) => e,
None => { None => {
// we need to generate the envelope // we need to generate the envelope
let mut e = Envelope::new(); let mut e = Envelope::builder();
// add all receivers in to_header and cc_header // add all receivers in to_header and cc_header
for receiver in self.to_header.iter().chain(self.cc_header.iter()).chain( for receiver in self.to_header
self.bcc_header.iter(), .iter()
) .chain(self.cc_header.iter())
.chain(self.bcc_header.iter())
{ {
match *receiver { match *receiver {
Address::Mailbox(ref m) => e.add_to(m.address.clone()), Address::Mailbox(ref m) => e.add_to(EmailAddress::from_str(&m.address)?),
Address::Group(_, ref ms) => { Address::Group(_, ref ms) => for m in ms.iter() {
for m in ms.iter() { e.add_to(EmailAddress::from_str(&m.address.clone())?);
e.add_to(m.address.clone()); },
} }
} }
} e.set_from(EmailAddress::from_str(&match self.sender_header {
}
if e.to.is_empty() {
return Err(Error::MissingTo);
}
e.set_from(match self.sender_header {
Some(x) => x.address.clone(), // if we have a sender_header, use it Some(x) => x.address.clone(), // if we have a sender_header, use it
None => { None => {
// use a from header // use a from header
@@ -811,41 +746,36 @@ impl EmailBuilder {
// if it's an author group, use the first author // if it's an author group, use the first author
Some(mailbox) => mailbox.address.clone(), Some(mailbox) => mailbox.address.clone(),
// for an empty author group (the rarest of the rare cases) // for an empty author group (the rarest of the rare cases)
None => return Err(Error::MissingFrom), // empty envelope sender None => return Err(Error::Email(EmailError::MissingFrom)), // empty envelope sender
}, },
}, },
// if we don't have a from header // if we don't have a from header
None => return Err(Error::MissingFrom), // empty envelope sender None => return Err(Error::Email(EmailError::MissingFrom)), // empty envelope sender
} }
} }
}); })?);
e e.build()?
} }
}; };
// Add the collected addresses as mailbox-list all at once. // Add the collected addresses as mailbox-list all at once.
// The unwraps are fine because the conversions for Vec<Address> never errs. // The unwraps are fine because the conversions for Vec<Address> never errs.
if !self.to_header.is_empty() { if !self.to_header.is_empty() {
self.message.add_header( self.message
Header::new_with_value( .add_header(Header::new_with_value("To".into(), self.to_header).unwrap());
"To".into(),
self.to_header,
).unwrap(),
);
} }
if !self.from_header.is_empty() { if !self.from_header.is_empty() {
self.message.add_header( self.message
Header::new_with_value("From".into(), self.from_header).unwrap(), .add_header(Header::new_with_value("From".into(), self.from_header).unwrap());
);
} else { } else {
return Err(Error::MissingFrom); return Err(Error::Email(EmailError::MissingFrom));
} }
if !self.cc_header.is_empty() { if !self.cc_header.is_empty() {
self.message.add_header( self.message
Header::new_with_value( .add_header(Header::new_with_value("Cc".into(), self.cc_header).unwrap());
"Cc".into(), }
self.cc_header, if !self.bcc_header.is_empty() {
).unwrap(), self.message
); .add_header(Header::new_with_value("Bcc".into(), self.bcc_header).unwrap());
} }
if !self.reply_to_header.is_empty() { if !self.reply_to_header.is_empty() {
self.message.add_header( self.message.add_header(
@@ -854,10 +784,8 @@ impl EmailBuilder {
} }
if !self.date_issued { if !self.date_issued {
self.message.add_header(( self.message
"Date", .add_header(("Date", Tm::rfc822z(&now()).to_string().as_ref()));
Tm::rfc822z(&now()).to_string().as_ref(),
));
} }
self.message.add_header(("MIME-Version", "1.0")); self.message.add_header(("MIME-Version", "1.0"));
@@ -867,34 +795,25 @@ impl EmailBuilder {
if let Ok(header) = Header::new_with_value( if let Ok(header) = Header::new_with_value(
"Message-ID".to_string(), "Message-ID".to_string(),
format!("<{}.lettre@localhost>", message_id), format!("<{}.lettre@localhost>", message_id),
) ) {
{
self.message.add_header(header) self.message.add_header(header)
} }
Ok(Email { Ok(Email {
message: self.message.build().as_string().into_bytes(), message: self.message.build().as_string().into_bytes(),
envelope: envelope, envelope,
message_id: message_id, message_id,
}) })
} }
} }
impl<'a> SendableEmail<'a, &'a [u8]> for Email { impl<'a> SendableEmail<'a, &'a [u8]> for Email {
fn to(&self) -> Vec<EmailAddress> { fn envelope(&self) -> Envelope {
self.envelope self.envelope.clone()
.to
.iter()
.map(|x| EmailAddress::new(x.clone()))
.collect()
}
fn from(&self) -> EmailAddress {
EmailAddress::new(self.envelope.from.clone())
} }
fn message_id(&self) -> String { fn message_id(&self) -> String {
format!("{}", self.message_id) self.message_id.to_string()
} }
fn message(&'a self) -> Box<&[u8]> { fn message(&'a self) -> Box<&[u8]> {
@@ -926,45 +845,12 @@ pub trait ExtractableEmail {
fn text(self) -> String; fn text(self) -> String;
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::EmailBuilder;
use super::{EmailBuilder, IntoEmail, SimpleEmail};
use lettre::{EmailAddress, SendableEmail}; use lettre::{EmailAddress, SendableEmail};
use time::now; use time::now;
#[test]
fn test_simple_email_builder() {
let email_builder = SimpleEmail::default();
let date_now = now();
let email = email_builder
.to("user@localhost")
.from("user@localhost")
.cc(("cc@localhost", "Alias"))
.reply_to("reply@localhost")
.text("Hello World!")
.date(date_now.clone())
.subject("Hello")
.header(("X-test", "value"))
.into_email()
.unwrap();
assert_eq!(
format!("{}", String::from_utf8_lossy(email.message().as_ref())),
format!(
"Subject: Hello\r\nContent-Type: text/plain; \
charset=utf-8\r\nX-test: value\r\nTo: <user@localhost>\r\nFrom: \
<user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\nReply-To: \
<reply@localhost>\r\nDate: {}\r\nMIME-Version: 1.0\r\nMessage-ID: \
<{}.lettre@localhost>\r\n\r\nHello World!\r\n",
date_now.rfc822z(),
email.message_id()
)
);
}
#[test] #[test]
fn test_multiple_from() { fn test_multiple_from() {
let email_builder = EmailBuilder::new(); let email_builder = EmailBuilder::new();
@@ -1000,6 +886,7 @@ mod test {
.to("user@localhost") .to("user@localhost")
.from("user@localhost") .from("user@localhost")
.cc(("cc@localhost", "Alias")) .cc(("cc@localhost", "Alias"))
.bcc("bcc@localhost")
.reply_to("reply@localhost") .reply_to("reply@localhost")
.sender("sender@localhost") .sender("sender@localhost")
.body("Hello World!") .body("Hello World!")
@@ -1014,8 +901,9 @@ mod test {
format!( format!(
"Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \ "Date: {}\r\nSubject: Hello\r\nX-test: value\r\nSender: \
<sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \ <sender@localhost>\r\nTo: <user@localhost>\r\nFrom: \
<user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\nReply-To: \ <user@localhost>\r\nCc: \"Alias\" <cc@localhost>\r\n\
<reply@localhost>\r\nMIME-Version: 1.0\r\nMessage-ID: \ Bcc: <bcc@localhost>\r\nReply-To: <reply@localhost>\r\n\
MIME-Version: 1.0\r\nMessage-ID: \
<{}.lettre@localhost>\r\n\r\nHello World!\r\n", <{}.lettre@localhost>\r\n\r\nHello World!\r\n",
date_now.rfc822z(), date_now.rfc822z(),
email.message_id() email.message_id()
@@ -1042,14 +930,17 @@ mod test {
.build() .build()
.unwrap(); .unwrap();
assert_eq!(email.from().to_string(), "sender@localhost".to_string());
assert_eq!( assert_eq!(
email.to(), email.envelope().from().unwrap().to_string(),
"sender@localhost".to_string()
);
assert_eq!(
email.envelope().to(),
vec![ vec![
EmailAddress::new("user@localhost".to_string()), EmailAddress::new("user@localhost".to_string()).unwrap(),
EmailAddress::new("cc@localhost".to_string()), EmailAddress::new("cc@localhost".to_string()).unwrap(),
EmailAddress::new("bcc@localhost".to_string()), EmailAddress::new("bcc@localhost".to_string()).unwrap(),
] ].as_slice()
); );
} }

View File

@@ -0,0 +1,48 @@
extern crate glob;
use self::glob::glob;
use std::env::consts::EXE_EXTENSION;
use std::env;
use std::path::Path;
use std::process::Command;
#[test]
fn book_test() {
let mut book_path = env::current_dir().unwrap();
book_path.push(
Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("../website/content/creating-messages"),
); // For some reasons, calling .parent() once more gives us None...
for md in glob(&format!("{}/*.md", book_path.to_str().unwrap())).unwrap() {
skeptic_test(&md.unwrap());
}
}
fn skeptic_test(path: &Path) {
let rustdoc = Path::new("rustdoc").with_extension(EXE_EXTENSION);
let exe = env::current_exe().unwrap();
let depdir = exe.parent().unwrap();
let mut cmd = Command::new(rustdoc);
cmd.args(&["--verbose", "--test"])
.arg("-L")
.arg(&depdir)
.arg(path);
let result = cmd.spawn()
.expect("Failed to spawn process")
.wait()
.expect("Failed to run process");
assert!(
result.success(),
format!("Failed to run rustdoc tests on {:?}!", path)
);
}

View File

@@ -1,3 +0,0 @@
write_mode = "Overwrite"
reorder_imports = true
reorder_imported_names = true

View File

@@ -1,4 +1,3 @@
#!/bin/sh #!/bin/sh
hugo hugo
lunr-hugo -i "content/**/*.md" -o ../docs/json/search.json -l toml lunr-hugo -i "content/**/*.md" -o ../docs/index.json -l toml

View File

@@ -1,4 +1,4 @@
baseURL = "https://lettre.github.io/lettre/" baseURL = "http://docs.lettre.at/"
languageCode = "en-us" languageCode = "en-us"
title = "Lettre site" title = "Lettre site"
theme = "hugo-theme-learn" theme = "hugo-theme-learn"

View File

@@ -7,3 +7,5 @@ weight = 2
+++ +++
### Creating messages ### Creating messages
This section explains how to create emails.

View File

@@ -0,0 +1,69 @@
+++
date = "2018-01-21T23:46:17+02:00"
title = "Email creation"
toc = true
weight = 4
+++
#### Simple example
The `email` part builds email messages. For now, it does not support attachments.
An email is built using an `EmailBuilder`. The simplest email could be:
```rust
extern crate lettre_email;
use lettre_email::EmailBuilder;
fn main() {
// Create an email
let email = EmailBuilder::new()
// Addresses can be specified by the tuple (email, alias)
.to(("user@example.org", "Firstname Lastname"))
// ... or by an address only
.from("user@example.com")
.subject("Hi, Hello world")
.text("Hello world.")
.build();
assert!(email.is_ok());
}
```
When the `build` method is called, the `EmailBuilder` will add the missing headers (like
`Message-ID` or `Date`) and check for missing necessary ones (like `From` or `To`). It will
then generate an `Email` that can be sent.
The `text()` method will create a plain text email, while the `html()` method will create an
HTML email. You can use the `alternative()` method to provide both versions, using plain text
as fallback for the HTML version.
#### Complete example
Below is a more complete example, not using method chaining:
```rust
extern crate lettre_email;
use lettre_email::EmailBuilder;
fn main() {
let mut builder = EmailBuilder::new();
builder.add_to(("user@example.org", "Alias name"));
builder.add_cc(("user@example.net", "Alias name"));
builder.add_from("no-reply@example.com");
builder.add_from("no-reply@example.eu");
builder.set_sender("no-reply@example.com");
builder.set_subject("Hello world");
builder.set_alternative("<h2>Hi, Hello world.</h2>", "Hi, Hello world.");
builder.add_reply_to("contact@example.com");
builder.add_header(("X-Custom-Header", "my header"));
let email = builder.build();
assert!(email.is_ok());
}
```
See the `EmailBuilder` documentation for a complete list of methods.

View File

@@ -7,8 +7,8 @@ weight = 1
+++ +++
{{% notice note %}} {{% notice note %}}
This documentation is written for lettre 0.7. This documentation is written for lettre 0.8.
Please use https://docs.rs/lettre/0.6.2/lettre/ for lettre 0.6. Please use https://docs.rs/lettre/0.7.0/lettre/ for lettre 0.7.
{{% /notice%}} {{% /notice%}}
Lettre is an email library that allows creating and sending messages. It provides: Lettre is an email library that allows creating and sending messages. It provides:

View File

@@ -11,22 +11,26 @@ The file transport writes the emails to the given directory. The name of the fil
It can be useful for testing purposes, or if you want to keep track of sent messages. It can be useful for testing purposes, or if you want to keep track of sent messages.
```rust ```rust
extern crate lettre;
use std::env::temp_dir; use std::env::temp_dir;
use lettre::file::FileEmailTransport; use lettre::file::FileEmailTransport;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress}; use lettre::{SimpleSendableEmail, EmailTransport};
fn main() {
// Write to the local temp directory // Write to the local temp directory
let mut sender = FileEmailTransport::new(temp_dir()); let mut sender = FileEmailTransport::new(temp_dir());
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"message_id".to_string(), "message_id".to_string(),
"Hello world".to_string(), "Hello world".to_string(),
); ).unwrap();
let result = sender.send(&email); let result = sender.send(&email);
assert!(result.is_ok()); assert!(result.is_ok());
}
``` ```
Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`: Example result in `/tmp/b7c211bc-9811-45ce-8cd9-68eab575d695.txt`:

View File

@@ -12,7 +12,7 @@ emails have to implement `SendableEmail`, which is the case for emails created w
The following transports are available: The following transports are available:
* The `SmtpTransport` uses the SMTP protocol to send the message over the network. It is * The `SmtpTransport` uses the SMTP protocol to send the message over the network. It is
the prefered way of sending emails. the preferred way of sending emails.
* The `SendmailTransport` uses the sendmail command to send messages. It is an alternative to * The `SendmailTransport` uses the sendmail command to send messages. It is an alternative to
the SMTP transport. the SMTP transport.
* The `FileTransport` creates a file containing the email content to be sent. It can be used * The `FileTransport` creates a file containing the email content to be sent. It can be used

View File

@@ -8,18 +8,22 @@ weight = 3
The sendmail transport sends the email using the local sendmail command. The sendmail transport sends the email using the local sendmail command.
``` rust ```rust,no_run
use lettre::sendmail::SendmailTransport; extern crate lettre;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
use lettre::sendmail::SendmailTransport;
use lettre::{SimpleSendableEmail, EmailTransport};
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"message_id".to_string(), "message_id".to_string(),
"Hello world".to_string(), "Hello world".to_string(),
); ).unwrap();
let mut sender = SendmailTransport::new(); let mut sender = SendmailTransport::new();
let result = sender.send(&email); let result = sender.send(&email);
assert!(result.is_ok()); assert!(result.is_ok());
}
``` ```

View File

@@ -8,10 +8,10 @@ weight = 2
This transport uses the SMTP protocol to send emails over the network (locally or remotely). This transport uses the SMTP protocol to send emails over the network (locally or remotely).
It is desinged to be: It is designed to be:
* Secured: email are encrypted by default * Secured: email are encrypted by default
* Modern: Unicode support for email content and sender/recipient adresses when compatible * Modern: Unicode support for email content and sender/recipient addresses when compatible
* Fast: supports tcp connection reuse * Fast: supports tcp connection reuse
This client is designed to send emails to a relay server, and should *not* be used to send This client is designed to send emails to a relay server, and should *not* be used to send
@@ -23,15 +23,18 @@ The relay server can be the local email server, a specific host or a third-party
This is the most basic example of usage: This is the most basic example of usage:
``` rust ```rust,no_run
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport}; extern crate lettre;
use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"message_id".to_string(), "message_id".to_string(),
"Hello world".to_string(), "Hello world".to_string(),
); ).unwrap();
// Open a local connection on port 25 // Open a local connection on port 25
let mut mailer = let mut mailer =
@@ -40,27 +43,29 @@ SmtpTransport::builder_unencrypted_localhost().unwrap().build();
let result = mailer.send(&email); let result = mailer.send(&email);
assert!(result.is_ok()); assert!(result.is_ok());
}
``` ```
#### Complete example #### Complete example
``` rust ```rust,no_run
extern crate lettre;
use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::smtp::SUBMISSION_PORT; use lettre::{SimpleSendableEmail, EmailTransport, SmtpTransport};
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress, SmtpTransport};
use lettre::smtp::extension::ClientId; use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters; use lettre::smtp::ConnectionReuseParameters;
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"message_id".to_string(), "message_id".to_string(),
"Hello world".to_string(), "Hello world".to_string(),
); ).unwrap();
// Connect to a remote server on a custom port // Connect to a remote server on a custom port
let mut mailer = SmtpTransport::simple_builder("server.tld".to_string()).unwrap() let mut mailer = SmtpTransport::simple_builder("server.tld").unwrap()
// Set the name sent during EHLO/HELO, default is `localhost` // Set the name sent during EHLO/HELO, default is `localhost`
.hello_name(ClientId::Domain("my.hostname.tld".to_string())) .hello_name(ClientId::Domain("my.hostname.tld".to_string()))
// Add credentials for authentication // Add credentials for authentication
@@ -81,6 +86,7 @@ assert!(result_2.is_ok());
// Explicitly close the SMTP transaction as we enabled connection reuse // Explicitly close the SMTP transaction as we enabled connection reuse
mailer.close(); mailer.close();
}
``` ```
#### Lower level #### Lower level
@@ -88,7 +94,9 @@ mailer.close();
You can also send commands, here is a simple email transaction without You can also send commands, here is a simple email transaction without
error handling: error handling:
``` rust ```rust,no_run
extern crate lettre;
use lettre::EmailAddress; use lettre::EmailAddress;
use lettre::smtp::SMTP_PORT; use lettre::smtp::SMTP_PORT;
use lettre::smtp::client::Client; use lettre::smtp::client::Client;
@@ -96,17 +104,19 @@ use lettre::smtp::client::net::NetworkStream;
use lettre::smtp::extension::ClientId; use lettre::smtp::extension::ClientId;
use lettre::smtp::commands::*; use lettre::smtp::commands::*;
fn main() {
let mut email_client: Client<NetworkStream> = Client::new(); let mut email_client: Client<NetworkStream> = Client::new();
let _ = email_client.connect(&("localhost", SMTP_PORT), None); let _ = email_client.connect(&("localhost", SMTP_PORT), None);
let _ = email_client.smtp_command(EhloCommand::new(ClientId::new("my_hostname".to_string()))); let _ = email_client.command(EhloCommand::new(ClientId::new("my_hostname".to_string())));
let _ = email_client.smtp_command( let _ = email_client.command(
MailCommand::new(Some(EmailAddress::new("user@example.com".to_string())), vec![]) MailCommand::new(Some(EmailAddress::new("user@example.com".to_string()).unwrap()), vec![])
); );
let _ = email_client.smtp_command( let _ = email_client.command(
RcptCommand::new(EmailAddress::new("user@example.org".to_string()), vec![]) RcptCommand::new(EmailAddress::new("user@example.org".to_string()).unwrap(), vec![])
); );
let _ = email_client.smtp_command(DataCommand); let _ = email_client.command(DataCommand);
let _ = email_client.message(Box::new("Test email".as_bytes())); let _ = email_client.message(Box::new("Test email".as_bytes()));
let _ = email_client.smtp_command(QuitCommand); let _ = email_client.command(QuitCommand);
}
``` ```

View File

@@ -10,19 +10,23 @@ The stub transport only logs message envelope and drops the content. It can be u
testing purposes. testing purposes.
```rust ```rust
use lettre::stub::StubEmailTransport; extern crate lettre;
use lettre::{SimpleSendableEmail, EmailTransport, EmailAddress};
use lettre::stub::StubEmailTransport;
use lettre::{SimpleSendableEmail, EmailTransport};
fn main() {
let email = SimpleSendableEmail::new( let email = SimpleSendableEmail::new(
EmailAddress::new("user@localhost".to_string()), "user@localhost".to_string(),
vec![EmailAddress::new("root@localhost".to_string())], &["root@localhost".to_string()],
"message_id".to_string(), "message_id".to_string(),
"Hello world".to_string(), "Hello world".to_string(),
); ).unwrap();
let mut sender = StubEmailTransport::new_positive(); let mut sender = StubEmailTransport::new_positive();
let result = sender.send(&email); let result = sender.send(&email);
assert!(result.is_ok()); assert!(result.is_ok());
}
``` ```
Will log (when using a logger like `env_logger`): Will log (when using a logger like `env_logger`):

View File

@@ -1,2 +1,3 @@
.DS_Store .DS_Store
public/ public/
exampleSite/public

Some files were not shown because too many files have changed in this diff Show More