Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
97041dbd
Commit
97041dbd
authored
Sep 24, 2015
by
bobbyrullo
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #129 from ecnahc515/smtp_support
Add smtp support
parents
825c3cf2
d154cad3
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
1970 additions
and
12 deletions
+1970
-12
email-configuration.md
Documentation/email-configuration.md
+53
-0
getting-started.md
Documentation/getting-started.md
+6
-1
Godeps.json
Godeps/Godeps.json
+5
-0
.travis.yml
Godeps/_workspace/src/gopkg.in/gomail.v2/.travis.yml
+8
-0
CHANGELOG.md
Godeps/_workspace/src/gopkg.in/gomail.v2/CHANGELOG.md
+20
-0
CONTRIBUTING.md
Godeps/_workspace/src/gopkg.in/gomail.v2/CONTRIBUTING.md
+20
-0
LICENSE
Godeps/_workspace/src/gopkg.in/gomail.v2/LICENSE
+20
-0
README.md
Godeps/_workspace/src/gopkg.in/gomail.v2/README.md
+100
-0
auth.go
Godeps/_workspace/src/gopkg.in/gomail.v2/auth.go
+67
-0
auth_test.go
Godeps/_workspace/src/gopkg.in/gomail.v2/auth_test.go
+156
-0
doc.go
Godeps/_workspace/src/gopkg.in/gomail.v2/doc.go
+5
-0
example_test.go
Godeps/_workspace/src/gopkg.in/gomail.v2/example_test.go
+215
-0
message.go
Godeps/_workspace/src/gopkg.in/gomail.v2/message.go
+302
-0
message_test.go
Godeps/_workspace/src/gopkg.in/gomail.v2/message_test.go
+0
-0
mime.go
Godeps/_workspace/src/gopkg.in/gomail.v2/mime.go
+19
-0
mime_go14.go
Godeps/_workspace/src/gopkg.in/gomail.v2/mime_go14.go
+16
-0
send.go
Godeps/_workspace/src/gopkg.in/gomail.v2/send.go
+117
-0
send_test.go
Godeps/_workspace/src/gopkg.in/gomail.v2/send_test.go
+80
-0
smtp.go
Godeps/_workspace/src/gopkg.in/gomail.v2/smtp.go
+175
-0
smtp_test.go
Godeps/_workspace/src/gopkg.in/gomail.v2/smtp_test.go
+254
-0
writeto.go
Godeps/_workspace/src/gopkg.in/gomail.v2/writeto.go
+242
-0
interface.go
email/interface.go
+3
-2
mailgun.go
email/mailgun.go
+1
-7
smtp.go
email/smtp.go
+84
-0
emailer.json.sample
static/fixtures/emailer.json.sample
+2
-2
No files found.
Documentation/email-configuration.md
0 → 100644
View file @
97041dbd
# Configuring Sending Emails
Dex sends emails to a during the registration process to verify an email
address belongs to the person signing up. Currently Dex supports two ways of
sending emails, and has a third option for use during development.
Configuration of the email provider in Dex is provided through a JSON file. All
email providers have a
`type`
and
`id`
field as well as some additional provider
specific fields.
## SMTP
If using SMTP the
`type`
field
**must**
be set to
`smtp`
. Additionally both
`host`
and
`port`
are required. If you wish to use SMTP plain auth, then
set
`auth`
to
`plain`
and specify your username and password.
```
{
"type": "smtp",
"host": "smtp.example.org",
"port": 587,
"auth": "plain",
"username": "postmaster@example.org",
"password": "foo"
}
```
## Mailgun
If using Mailgun the
`type`
field
**must**
be set to
`mailgun`
. Additionally
`privateAPIKey`
,
`publicAPIKey`
, and
`domain`
are required.
```
{
"type": "mailgun",
"privateAPIKey": "key-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"publicAPIKey": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"domain": "sandboxZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.mailgun.org"
}
```
## Dev
The fake emailer should only be used in development. The fake emailer
prints emails to
`stdout`
rather than sending any email. If using the fake
emailer the
`type`
field
**must**
be set to
`fake`
.
```
{
"type": "fake"
}
```
\ No newline at end of file
Documentation/getting-started.md
View file @
97041dbd
...
...
@@ -63,7 +63,12 @@ export DEX_OVERLORD_LOG_DEBUG=true
# Start the dex-worker
Now start the worker:
Before starting
`dex-worker`
you should determine how you want verification emails to be delivered to the user.
If you just want to test dex out, you can just use the provided sample config in
`static/fixtures/emailer.json.sample`
.
Please review
[
email-configuration
](
https://github.com/coreos/dex/blob/master/Documentation/email-configuration.md
)
for details
(make sure you point
`--email-cfg`
to your newly configured file).
Once you have setup your email config run
`dex-worker`
:
`./bin/dex-worker --db-url=$DEX_DB_URL --key-secrets=$DEX_KEY_SECRET --email-cfg=static/fixtures/emailer.json.sample --log-debug=true &`
...
...
Godeps/Godeps.json
View file @
97041dbd
...
...
@@ -120,6 +120,11 @@
"ImportPath"
:
"google.golang.org/api/googleapi"
,
"Rev"
:
"d3edb0282bde692467788c50070a9211afe75cf3"
},
{
"ImportPath"
:
"gopkg.in/gomail.v2"
,
"Comment"
:
"2.0.0-2-gb1e5552"
,
"Rev"
:
"b1e55520bf557d8a614f1e1f493ce892c1b5e97e"
},
{
"ImportPath"
:
"gopkg.in/gorp.v1"
,
"Comment"
:
"v1.7.1"
,
...
...
Godeps/_workspace/src/gopkg.in/gomail.v2/.travis.yml
0 → 100644
View file @
97041dbd
language
:
go
go
:
-
1.2
-
1.3
-
1.4
-
1.5
-
tip
Godeps/_workspace/src/gopkg.in/gomail.v2/CHANGELOG.md
0 → 100644
View file @
97041dbd
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to
[
Semantic Versioning
](
http://semver.org/
)
.
## [2.0.0] - 2015-09-02
-
Mailer has been removed. It has been replaced by Dialer and Sender.
-
`File`
type and the
`CreateFile`
and
`OpenFile`
functions have been removed.
-
`Message.Attach`
and
`Message.Embed`
have a new signature.
-
`Message.GetBodyWriter`
has been removed. Use
`Message.AddAlternativeWriter`
instead.
-
`Message.Export`
has been removed.
`Message.WriteTo`
can be used instead.
-
`Message.DelHeader`
has been removed.
-
The
`Bcc`
header field is no longer sent. It is far more simpler and
efficient: the same message is sent to all recipients instead of sending a
different email to each Bcc address.
-
LoginAuth has been removed.
`NewPlainDialer`
now implements the LOGIN
authentication mechanism when needed.
-
Go 1.2 is now required instead of Go 1.3. No external dependency are used when
using Go 1.5.
Godeps/_workspace/src/gopkg.in/gomail.v2/CONTRIBUTING.md
0 → 100644
View file @
97041dbd
Thank you for contributing to Gomail! Here are a few guidelines:
## Bugs
If you think you found a bug, create an issue and supply the minimum amount
of code triggering the bug so it can be reproduced.
## Fixing a bug
If you want to fix a bug, you can send a pull request. It should contains a
new test or update an existing one to cover that bug.
## New feature proposal
If you think Gomail lacks a feature, you can open an issue or send a pull
request. I want to keep Gomail code and API as simple as possible so please
describe your needs so we can discuss whether this feature should be added to
Gomail or not.
Godeps/_workspace/src/gopkg.in/gomail.v2/LICENSE
0 → 100644
View file @
97041dbd
The MIT License (MIT)
Copyright (c) 2014 Alexandre Cesaro
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Godeps/_workspace/src/gopkg.in/gomail.v2/README.md
0 → 100644
View file @
97041dbd
# Gomail
[
![Build Status
](
https://travis-ci.org/go-gomail/gomail.svg?branch=v2
)
](https://travis-ci.org/go-gomail/gomail)
[
![Code Coverage
](
http://gocover.io/_badge/gopkg.in/gomail.v2
)
](http://gocover.io/gopkg.in/gomail.v2)
[
![Documentation
](
https://godoc.org/gopkg.in/gomail.v2?status.svg
)
](https://godoc.org/gopkg.in/gomail.v2)
## Introduction
Gomail is a simple and efficient package to send emails. It is well tested and
documented.
Gomail can only send emails using an SMTP server. But the API is flexible and it
is easy to implement other methods for sending emails using a local Postfix, an
API, etc.
It is versioned using
[
gopkg.in
](
https://gopkg.in
)
so I promise
they will never be backward incompatible changes within each version.
It requires Go 1.2 or newer. With Go 1.5, no external dependencies are used.
## Features
Gomail supports:
-
Attachments
-
Embedded images
-
HTML and text templates
-
Automatic encoding of special characters
-
SSL and TLS
-
Sending multiple emails with the same SMTP connection
## Documentation
https://godoc.org/gopkg.in/gomail.v2
## Download
go get gopkg.in/gomail.v2
## Examples
See the
[
examples in the documentation
](
https://godoc.org/gopkg.in/gomail.v2#example-package
)
.
## FAQ
### x509: certificate signed by unknown authority
If you get this error it means the certificate used by the SMTP server is not
considered valid by the client running Gomail. As a quick workaround you can
bypass the verification of the server's certificate chain and host name by using
`SetTLSConfig`
:
d := gomail.NewPlainDialer("smtp.example.com", "user", "123456", 587)
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
Note, however, that this is insecure and should not be used in production.
## Contribute
Contributions are more than welcome! See
[
CONTRIBUTING.md
](
CONTRIBUTING.md
)
for
more info.
## Change log
See
[
CHANGELOG.md
](
CHANGELOG.md
)
.
## License
[
MIT
](
LICENSE
)
## Contact
You can ask questions on the
[
Gomail
thread](https://groups.google.com/d/topic/golang-nuts/jMxZHzvvEVg/discussion)
in the Go mailing-list.
## Support
If you want to support the development of Gomail, I gladly accept donations.
I will give 100% of the money I receive to
[
Enfants, Espoir Du Monde
](
http://www.eedm.fr/
)
.
EEDM is a French NGO which helps children in Bangladesh, Cameroun, Haiti, India
and Madagascar.
All its members are volunteers so its operating costs are only 1.9%. So your
money will directly help children of these countries.
As an added bonus, your donations will also tip me by lowering my taxes :smile:
I will send an email with the receipt of the donation to EEDM annually to all
donors.
[
![Donate
](
https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif
)
](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PYQKC7VFVXCFG)
Godeps/_workspace/src/gopkg.in/gomail.v2/auth.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"bytes"
"errors"
"fmt"
"net/smtp"
)
// plainAuth is an smtp.Auth that implements the PLAIN authentication mechanism.
// It fallbacks to the LOGIN mechanism if it is the only mechanism advertised
// by the server.
type
plainAuth
struct
{
username
string
password
string
host
string
login
bool
}
func
(
a
*
plainAuth
)
Start
(
server
*
smtp
.
ServerInfo
)
(
string
,
[]
byte
,
error
)
{
if
server
.
Name
!=
a
.
host
{
return
""
,
nil
,
errors
.
New
(
"gomail: wrong host name"
)
}
var
plain
,
login
bool
for
_
,
a
:=
range
server
.
Auth
{
switch
a
{
case
"PLAIN"
:
plain
=
true
case
"LOGIN"
:
login
=
true
}
}
if
!
server
.
TLS
&&
!
plain
&&
!
login
{
return
""
,
nil
,
errors
.
New
(
"gomail: unencrypted connection"
)
}
if
!
plain
&&
login
{
a
.
login
=
true
return
"LOGIN"
,
nil
,
nil
}
return
"PLAIN"
,
[]
byte
(
"
\x00
"
+
a
.
username
+
"
\x00
"
+
a
.
password
),
nil
}
func
(
a
*
plainAuth
)
Next
(
fromServer
[]
byte
,
more
bool
)
([]
byte
,
error
)
{
if
!
a
.
login
{
if
more
{
return
nil
,
errors
.
New
(
"gomail: unexpected server challenge"
)
}
return
nil
,
nil
}
if
!
more
{
return
nil
,
nil
}
switch
{
case
bytes
.
Equal
(
fromServer
,
[]
byte
(
"Username:"
))
:
return
[]
byte
(
a
.
username
),
nil
case
bytes
.
Equal
(
fromServer
,
[]
byte
(
"Password:"
))
:
return
[]
byte
(
a
.
password
),
nil
default
:
return
nil
,
fmt
.
Errorf
(
"gomail: unexpected server challenge: %s"
,
fromServer
)
}
}
Godeps/_workspace/src/gopkg.in/gomail.v2/auth_test.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"net/smtp"
"testing"
)
const
(
testUser
=
"user"
testPwd
=
"pwd"
testHost
=
"smtp.example.com"
)
var
testAuth
=
&
plainAuth
{
username
:
testUser
,
password
:
testPwd
,
host
:
testHost
,
}
type
plainAuthTest
struct
{
auths
[]
string
challenges
[]
string
tls
bool
wantProto
string
wantData
[]
string
wantError
bool
}
func
TestNoAdvertisement
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
false
,
wantProto
:
"PLAIN"
,
wantError
:
true
,
})
}
func
TestNoAdvertisementTLS
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
true
,
wantProto
:
"PLAIN"
,
wantData
:
[]
string
{
"
\x00
"
+
testUser
+
"
\x00
"
+
testPwd
},
})
}
func
TestPlain
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"PLAIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
false
,
wantProto
:
"PLAIN"
,
wantData
:
[]
string
{
"
\x00
"
+
testUser
+
"
\x00
"
+
testPwd
},
})
}
func
TestPlainTLS
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"PLAIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
true
,
wantProto
:
"PLAIN"
,
wantData
:
[]
string
{
"
\x00
"
+
testUser
+
"
\x00
"
+
testPwd
},
})
}
func
TestPlainAndLogin
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"PLAIN"
,
"LOGIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
false
,
wantProto
:
"PLAIN"
,
wantData
:
[]
string
{
"
\x00
"
+
testUser
+
"
\x00
"
+
testPwd
},
})
}
func
TestPlainAndLoginTLS
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"PLAIN"
,
"LOGIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
true
,
wantProto
:
"PLAIN"
,
wantData
:
[]
string
{
"
\x00
"
+
testUser
+
"
\x00
"
+
testPwd
},
})
}
func
TestLogin
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"LOGIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
false
,
wantProto
:
"LOGIN"
,
wantData
:
[]
string
{
""
,
testUser
,
testPwd
},
})
}
func
TestLoginTLS
(
t
*
testing
.
T
)
{
testPlainAuth
(
t
,
&
plainAuthTest
{
auths
:
[]
string
{
"LOGIN"
},
challenges
:
[]
string
{
"Username:"
,
"Password:"
},
tls
:
true
,
wantProto
:
"LOGIN"
,
wantData
:
[]
string
{
""
,
testUser
,
testPwd
},
})
}
func
testPlainAuth
(
t
*
testing
.
T
,
test
*
plainAuthTest
)
{
auth
:=
&
plainAuth
{
username
:
testUser
,
password
:
testPwd
,
host
:
testHost
,
}
server
:=
&
smtp
.
ServerInfo
{
Name
:
testHost
,
TLS
:
test
.
tls
,
Auth
:
test
.
auths
,
}
proto
,
toServer
,
err
:=
auth
.
Start
(
server
)
if
err
!=
nil
&&
!
test
.
wantError
{
t
.
Fatalf
(
"plainAuth.Start(): %v"
,
err
)
}
if
err
!=
nil
&&
test
.
wantError
{
return
}
if
proto
!=
test
.
wantProto
{
t
.
Errorf
(
"invalid protocol, got %q, want %q"
,
proto
,
test
.
wantProto
)
}
i
:=
0
got
:=
string
(
toServer
)
if
got
!=
test
.
wantData
[
i
]
{
t
.
Errorf
(
"Invalid response, got %q, want %q"
,
got
,
test
.
wantData
[
i
])
}
if
proto
==
"PLAIN"
{
return
}
for
_
,
challenge
:=
range
test
.
challenges
{
i
++
if
i
>=
len
(
test
.
wantData
)
{
t
.
Fatalf
(
"unexpected challenge: %q"
,
challenge
)
}
toServer
,
err
=
auth
.
Next
([]
byte
(
challenge
),
true
)
if
err
!=
nil
{
t
.
Fatalf
(
"plainAuth.Auth(): %v"
,
err
)
}
got
=
string
(
toServer
)
if
got
!=
test
.
wantData
[
i
]
{
t
.
Errorf
(
"Invalid response, got %q, want %q"
,
got
,
test
.
wantData
[
i
])
}
}
}
Godeps/_workspace/src/gopkg.in/gomail.v2/doc.go
0 → 100644
View file @
97041dbd
// Package gomail provides a simple interface to compose emails and to mail them
// efficiently.
//
// More info on Github: https://github.com/go-gomail/gomail
package
gomail
Godeps/_workspace/src/gopkg.in/gomail.v2/example_test.go
0 → 100644
View file @
97041dbd
package
gomail_test
import
(
"fmt"
"html/template"
"io"
"log"
"time"
"gopkg.in/gomail.v2"
)
func
Example
()
{
m
:=
gomail
.
NewMessage
()
m
.
SetHeader
(
"From"
,
"alex@example.com"
)
m
.
SetHeader
(
"To"
,
"bob@example.com"
,
"cora@example.com"
)
m
.
SetAddressHeader
(
"Cc"
,
"dan@example.com"
,
"Dan"
)
m
.
SetHeader
(
"Subject"
,
"Hello!"
)
m
.
SetBody
(
"text/html"
,
"Hello <b>Bob</b> and <i>Cora</i>!"
)
m
.
Attach
(
"/home/Alex/lolcat.jpg"
)
d
:=
gomail
.
NewPlainDialer
(
"smtp.example.com"
,
587
,
"user"
,
"123456"
)
// Send the email to Bob, Cora and Dan.
if
err
:=
d
.
DialAndSend
(
m
);
err
!=
nil
{
panic
(
err
)
}
}
// A daemon that listens to a channel and sends all incoming messages.
func
Example_daemon
()
{
ch
:=
make
(
chan
*
gomail
.
Message
)
go
func
()
{
d
:=
gomail
.
NewPlainDialer
(
"smtp.example.com"
,
587
,
"user"
,
"123456"
)
var
s
gomail
.
SendCloser
var
err
error
open
:=
false
for
{
select
{
case
m
,
ok
:=
<-
ch
:
if
!
ok
{
return
}
if
!
open
{
if
s
,
err
=
d
.
Dial
();
err
!=
nil
{
panic
(
err
)
}
open
=
true
}
if
err
:=
gomail
.
Send
(
s
,
m
);
err
!=
nil
{
log
.
Print
(
err
)
}
// Close the connection to the SMTP server if no email was sent in
// the last 30 seconds.
case
<-
time
.
After
(
30
*
time
.
Second
)
:
if
open
{
if
err
:=
s
.
Close
();
err
!=
nil
{
panic
(
err
)
}
open
=
false
}
}
}
}()
// Use the channel in your program to send emails.
// Close the channel to stop the mail daemon.
close
(
ch
)
}
// Efficiently send a customized newsletter to a list of recipients.
func
Example_newsletter
()
{
// The list of recipients.
var
list
[]
struct
{
Name
string
Address
string
}
d
:=
gomail
.
NewPlainDialer
(
"smtp.example.com"
,
587
,
"user"
,
"123456"
)
s
,
err
:=
d
.
Dial
()
if
err
!=
nil
{
panic
(
err
)
}
m
:=
gomail
.
NewMessage
()
for
_
,
r
:=
range
list
{
m
.
SetHeader
(
"From"
,
"no-reply@example.com"
)
m
.
SetAddressHeader
(
"To"
,
r
.
Address
,
r
.
Name
)
m
.
SetHeader
(
"Subject"
,
"Newsletter #1"
)
m
.
SetBody
(
"text/html"
,
fmt
.
Sprintf
(
"Hello %s!"
,
r
.
Name
))
if
err
:=
gomail
.
Send
(
s
,
m
);
err
!=
nil
{
log
.
Printf
(
"Could not send email to %q: %v"
,
r
.
Address
,
err
)
}
m
.
Reset
()
}
}
// Send an email using a local SMTP server.
func
Example_noAuth
()
{
m
:=
gomail
.
NewMessage
()
m
.
SetHeader
(
"From"
,
"from@example.com"
)
m
.
SetHeader
(
"To"
,
"to@example.com"
)
m
.
SetHeader
(
"Subject"
,
"Hello!"
)
m
.
SetBody
(
"text/plain"
,
"Hello!"
)
d
:=
gomail
.
Dialer
{
Host
:
"localhost"
,
Port
:
587
}
if
err
:=
d
.
DialAndSend
(
m
);
err
!=
nil
{
panic
(
err
)
}
}
// Send an email using an API or postfix.
func
Example_noSMTP
()
{
m
:=
gomail
.
NewMessage
()
m
.
SetHeader
(
"From"
,
"from@example.com"
)
m
.
SetHeader
(
"To"
,
"to@example.com"
)
m
.
SetHeader
(
"Subject"
,
"Hello!"
)
m
.
SetBody
(
"text/plain"
,
"Hello!"
)
s
:=
gomail
.
SendFunc
(
func
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
{
// Implements you email-sending function, for example by calling
// an API, or running postfix, etc.
fmt
.
Println
(
"From:"
,
from
)
fmt
.
Println
(
"To:"
,
to
)
return
nil
})
if
err
:=
gomail
.
Send
(
s
,
m
);
err
!=
nil
{
panic
(
err
)
}
// Output:
// From: from@example.com
// To: [to@example.com]
}
var
m
*
gomail
.
Message
func
ExampleSetCopyFunc
()
{
m
.
Attach
(
"foo.txt"
,
gomail
.
SetCopyFunc
(
func
(
w
io
.
Writer
)
error
{
_
,
err
:=
w
.
Write
([]
byte
(
"Content of foo.txt"
))
return
err
}))
}
func
ExampleSetHeader
()
{
h
:=
map
[
string
][]
string
{
"Content-ID"
:
{
"<foo@bar.mail>"
}}
m
.
Attach
(
"foo.jpg"
,
gomail
.
SetHeader
(
h
))
}
func
ExampleMessage_AddAlternative
()
{
m
.
SetBody
(
"text/plain"
,
"Hello!"
)
m
.
AddAlternative
(
"text/html"
,
"<p>Hello!</p>"
)
}
func
ExampleMessage_AddAlternativeWriter
()
{
t
:=
template
.
Must
(
template
.
New
(
"example"
)
.
Parse
(
"Hello {{.}}!"
))
m
.
AddAlternativeWriter
(
"text/plain"
,
func
(
w
io
.
Writer
)
error
{
return
t
.
Execute
(
w
,
"Bob"
)
})
}
func
ExampleMessage_Attach
()
{
m
.
Attach
(
"/tmp/image.jpg"
)
}
func
ExampleMessage_Embed
()
{
m
.
Embed
(
"/tmp/image.jpg"
)
m
.
SetBody
(
"text/html"
,
`<img src="cid:image.jpg" alt="My image" />`
)
}
func
ExampleMessage_FormatAddress
()
{
m
.
SetHeader
(
"To"
,
m
.
FormatAddress
(
"bob@example.com"
,
"Bob"
),
m
.
FormatAddress
(
"cora@example.com"
,
"Cora"
))
}
func
ExampleMessage_FormatDate
()
{
m
.
SetHeaders
(
map
[
string
][]
string
{
"X-Date"
:
{
m
.
FormatDate
(
time
.
Now
())},
})
}
func
ExampleMessage_SetAddressHeader
()
{
m
.
SetAddressHeader
(
"To"
,
"bob@example.com"
,
"Bob"
)
}
func
ExampleMessage_SetBody
()
{
m
.
SetBody
(
"text/plain"
,
"Hello!"
)
}
func
ExampleMessage_SetDateHeader
()
{
m
.
SetDateHeader
(
"X-Date"
,
time
.
Now
())
}
func
ExampleMessage_SetHeader
()
{
m
.
SetHeader
(
"Subject"
,
"Hello!"
)
}
func
ExampleMessage_SetHeaders
()
{
m
.
SetHeaders
(
map
[
string
][]
string
{
"From"
:
{
m
.
FormatAddress
(
"alex@example.com"
,
"Alex"
)},
"To"
:
{
"bob@example.com"
,
"cora@example.com"
},
"Subject"
:
{
"Hello"
},
})
}
func
ExampleSetCharset
()
{
m
=
gomail
.
NewMessage
(
gomail
.
SetCharset
(
"ISO-8859-1"
))
}
func
ExampleSetEncoding
()
{
m
=
gomail
.
NewMessage
(
gomail
.
SetEncoding
(
gomail
.
Base64
))
}
Godeps/_workspace/src/gopkg.in/gomail.v2/message.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"bytes"
"io"
"os"
"path/filepath"
"time"
)
// Message represents an email.
type
Message
struct
{
header
header
parts
[]
part
attachments
[]
*
file
embedded
[]
*
file
charset
string
encoding
Encoding
hEncoder
mimeEncoder
buf
bytes
.
Buffer
}
type
header
map
[
string
][]
string
type
part
struct
{
header
header
copier
func
(
io
.
Writer
)
error
}
// NewMessage creates a new message. It uses UTF-8 and quoted-printable encoding
// by default.
func
NewMessage
(
settings
...
MessageSetting
)
*
Message
{
m
:=
&
Message
{
header
:
make
(
header
),
charset
:
"UTF-8"
,
encoding
:
QuotedPrintable
,
}
m
.
applySettings
(
settings
)
if
m
.
encoding
==
Base64
{
m
.
hEncoder
=
bEncoding
}
else
{
m
.
hEncoder
=
qEncoding
}
return
m
}
// Reset resets the message so it can be reused. The message keeps its previous
// settings so it is in the same state that after a call to NewMessage.
func
(
m
*
Message
)
Reset
()
{
for
k
:=
range
m
.
header
{
delete
(
m
.
header
,
k
)
}
m
.
parts
=
nil
m
.
attachments
=
nil
m
.
embedded
=
nil
}
func
(
m
*
Message
)
applySettings
(
settings
[]
MessageSetting
)
{
for
_
,
s
:=
range
settings
{
s
(
m
)
}
}
// A MessageSetting can be used as an argument in NewMessage to configure an
// email.
type
MessageSetting
func
(
m
*
Message
)
// SetCharset is a message setting to set the charset of the email.
func
SetCharset
(
charset
string
)
MessageSetting
{
return
func
(
m
*
Message
)
{
m
.
charset
=
charset
}
}
// SetEncoding is a message setting to set the encoding of the email.
func
SetEncoding
(
enc
Encoding
)
MessageSetting
{
return
func
(
m
*
Message
)
{
m
.
encoding
=
enc
}
}
// Encoding represents a MIME encoding scheme like quoted-printable or base64.
type
Encoding
string
const
(
// QuotedPrintable represents the quoted-printable encoding as defined in
// RFC 2045.
QuotedPrintable
Encoding
=
"quoted-printable"
// Base64 represents the base64 encoding as defined in RFC 2045.
Base64
Encoding
=
"base64"
// Unencoded can be used to avoid encoding the body of an email. The headers
// will still be encoded using quoted-printable encoding.
Unencoded
Encoding
=
"8bit"
)
// SetHeader sets a value to the given header field.
func
(
m
*
Message
)
SetHeader
(
field
string
,
value
...
string
)
{
m
.
encodeHeader
(
value
)
m
.
header
[
field
]
=
value
}
func
(
m
*
Message
)
encodeHeader
(
values
[]
string
)
{
for
i
:=
range
values
{
values
[
i
]
=
m
.
encodeString
(
values
[
i
])
}
}
func
(
m
*
Message
)
encodeString
(
value
string
)
string
{
return
m
.
hEncoder
.
Encode
(
m
.
charset
,
value
)
}
// SetHeaders sets the message headers.
func
(
m
*
Message
)
SetHeaders
(
h
map
[
string
][]
string
)
{
for
k
,
v
:=
range
h
{
m
.
SetHeader
(
k
,
v
...
)
}
}
// SetAddressHeader sets an address to the given header field.
func
(
m
*
Message
)
SetAddressHeader
(
field
,
address
,
name
string
)
{
m
.
header
[
field
]
=
[]
string
{
m
.
FormatAddress
(
address
,
name
)}
}
// FormatAddress formats an address and a name as a valid RFC 5322 address.
func
(
m
*
Message
)
FormatAddress
(
address
,
name
string
)
string
{
enc
:=
m
.
encodeString
(
name
)
if
enc
==
name
{
m
.
buf
.
WriteByte
(
'"'
)
for
i
:=
0
;
i
<
len
(
name
);
i
++
{
b
:=
name
[
i
]
if
b
==
'\\'
||
b
==
'"'
{
m
.
buf
.
WriteByte
(
'\\'
)
}
m
.
buf
.
WriteByte
(
b
)
}
m
.
buf
.
WriteByte
(
'"'
)
}
else
if
hasSpecials
(
name
)
{
m
.
buf
.
WriteString
(
bEncoding
.
Encode
(
m
.
charset
,
name
))
}
else
{
m
.
buf
.
WriteString
(
enc
)
}
m
.
buf
.
WriteString
(
" <"
)
m
.
buf
.
WriteString
(
address
)
m
.
buf
.
WriteByte
(
'>'
)
addr
:=
m
.
buf
.
String
()
m
.
buf
.
Reset
()
return
addr
}
func
hasSpecials
(
text
string
)
bool
{
for
i
:=
0
;
i
<
len
(
text
);
i
++
{
switch
c
:=
text
[
i
];
c
{
case
'('
,
')'
,
'<'
,
'>'
,
'['
,
']'
,
':'
,
';'
,
'@'
,
'\\'
,
','
,
'.'
,
'"'
:
return
true
}
}
return
false
}
// SetDateHeader sets a date to the given header field.
func
(
m
*
Message
)
SetDateHeader
(
field
string
,
date
time
.
Time
)
{
m
.
header
[
field
]
=
[]
string
{
m
.
FormatDate
(
date
)}
}
// FormatDate formats a date as a valid RFC 5322 date.
func
(
m
*
Message
)
FormatDate
(
date
time
.
Time
)
string
{
return
date
.
Format
(
time
.
RFC1123Z
)
}
// GetHeader gets a header field.
func
(
m
*
Message
)
GetHeader
(
field
string
)
[]
string
{
return
m
.
header
[
field
]
}
// SetBody sets the body of the message.
func
(
m
*
Message
)
SetBody
(
contentType
,
body
string
)
{
m
.
parts
=
[]
part
{
{
header
:
m
.
getPartHeader
(
contentType
),
copier
:
func
(
w
io
.
Writer
)
error
{
_
,
err
:=
io
.
WriteString
(
w
,
body
)
return
err
},
},
}
}
// AddAlternative adds an alternative part to the message.
//
// It is commonly used to send HTML emails that default to the plain text
// version for backward compatibility.
//
// More info: http://en.wikipedia.org/wiki/MIME#Alternative
func
(
m
*
Message
)
AddAlternative
(
contentType
,
body
string
)
{
m
.
parts
=
append
(
m
.
parts
,
part
{
header
:
m
.
getPartHeader
(
contentType
),
copier
:
func
(
w
io
.
Writer
)
error
{
_
,
err
:=
io
.
WriteString
(
w
,
body
)
return
err
},
},
)
}
// AddAlternativeWriter adds an alternative part to the message. It can be
// useful with the text/template or html/template packages.
func
(
m
*
Message
)
AddAlternativeWriter
(
contentType
string
,
f
func
(
io
.
Writer
)
error
)
{
m
.
parts
=
[]
part
{
{
header
:
m
.
getPartHeader
(
contentType
),
copier
:
f
,
},
}
}
func
(
m
*
Message
)
getPartHeader
(
contentType
string
)
header
{
return
map
[
string
][]
string
{
"Content-Type"
:
{
contentType
+
"; charset="
+
m
.
charset
},
"Content-Transfer-Encoding"
:
{
string
(
m
.
encoding
)},
}
}
type
file
struct
{
Name
string
Header
map
[
string
][]
string
CopyFunc
func
(
w
io
.
Writer
)
error
}
func
(
f
*
file
)
setHeader
(
field
,
value
string
)
{
f
.
Header
[
field
]
=
[]
string
{
value
}
}
// A FileSetting can be used as an argument in Message.Attach or Message.Embed.
type
FileSetting
func
(
*
file
)
// SetHeader is a file setting to set the MIME header of the message part that
// contains the file content.
//
// Mandatory headers are automatically added if they are not set when sending
// the email.
func
SetHeader
(
h
map
[
string
][]
string
)
FileSetting
{
return
func
(
f
*
file
)
{
for
k
,
v
:=
range
h
{
f
.
Header
[
k
]
=
v
}
}
}
// SetCopyFunc is a file setting to replace the function that runs when the
// message is sent. It should copy the content of the file to the io.Writer.
//
// The default copy function opens the file with the given filename, and copy
// its content to the io.Writer.
func
SetCopyFunc
(
f
func
(
io
.
Writer
)
error
)
FileSetting
{
return
func
(
fi
*
file
)
{
fi
.
CopyFunc
=
f
}
}
func
(
m
*
Message
)
appendFile
(
list
[]
*
file
,
name
string
,
settings
[]
FileSetting
)
[]
*
file
{
f
:=
&
file
{
Name
:
filepath
.
Base
(
name
),
Header
:
make
(
map
[
string
][]
string
),
CopyFunc
:
func
(
w
io
.
Writer
)
error
{
h
,
err
:=
os
.
Open
(
name
)
if
err
!=
nil
{
return
err
}
if
_
,
err
:=
io
.
Copy
(
w
,
h
);
err
!=
nil
{
h
.
Close
()
return
err
}
return
h
.
Close
()
},
}
for
_
,
s
:=
range
settings
{
s
(
f
)
}
if
list
==
nil
{
return
[]
*
file
{
f
}
}
return
append
(
list
,
f
)
}
// Attach attaches the files to the email.
func
(
m
*
Message
)
Attach
(
filename
string
,
settings
...
FileSetting
)
{
m
.
attachments
=
m
.
appendFile
(
m
.
attachments
,
filename
,
settings
)
}
// Embed embeds the images to the email.
func
(
m
*
Message
)
Embed
(
filename
string
,
settings
...
FileSetting
)
{
m
.
embedded
=
m
.
appendFile
(
m
.
embedded
,
filename
,
settings
)
}
Godeps/_workspace/src/gopkg.in/gomail.v2/message_test.go
0 → 100644
View file @
97041dbd
This diff is collapsed.
Click to expand it.
Godeps/_workspace/src/gopkg.in/gomail.v2/mime.go
0 → 100644
View file @
97041dbd
// +build go1.5
package
gomail
import
(
"mime"
"mime/quotedprintable"
)
var
newQPWriter
=
quotedprintable
.
NewWriter
type
mimeEncoder
struct
{
mime
.
WordEncoder
}
var
(
bEncoding
=
mimeEncoder
{
mime
.
BEncoding
}
qEncoding
=
mimeEncoder
{
mime
.
QEncoding
}
)
Godeps/_workspace/src/gopkg.in/gomail.v2/mime_go14.go
0 → 100644
View file @
97041dbd
// +build !go1.5
package
gomail
import
"gopkg.in/alexcesaro/quotedprintable.v3"
var
newQPWriter
=
quotedprintable
.
NewWriter
type
mimeEncoder
struct
{
quotedprintable
.
WordEncoder
}
var
(
bEncoding
=
mimeEncoder
{
quotedprintable
.
BEncoding
}
qEncoding
=
mimeEncoder
{
quotedprintable
.
QEncoding
}
)
Godeps/_workspace/src/gopkg.in/gomail.v2/send.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"errors"
"fmt"
"io"
"net/mail"
)
// Sender is the interface that wraps the Send method.
//
// Send sends an email to the given addresses.
type
Sender
interface
{
Send
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
}
// SendCloser is the interface that groups the Send and Close methods.
type
SendCloser
interface
{
Sender
Close
()
error
}
// A SendFunc is a function that sends emails to the given adresses.
//
// The SendFunc type is an adapter to allow the use of ordinary functions as
// email senders. If f is a function with the appropriate signature, SendFunc(f)
// is a Sender object that calls f.
type
SendFunc
func
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
// Send calls f(from, to, msg).
func
(
f
SendFunc
)
Send
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
{
return
f
(
from
,
to
,
msg
)
}
// Send sends emails using the given Sender.
func
Send
(
s
Sender
,
msg
...*
Message
)
error
{
for
i
,
m
:=
range
msg
{
if
err
:=
send
(
s
,
m
);
err
!=
nil
{
return
fmt
.
Errorf
(
"gomail: could not send email %d: %v"
,
i
+
1
,
err
)
}
}
return
nil
}
func
send
(
s
Sender
,
m
*
Message
)
error
{
from
,
err
:=
m
.
getFrom
()
if
err
!=
nil
{
return
err
}
to
,
err
:=
m
.
getRecipients
()
if
err
!=
nil
{
return
err
}
if
err
:=
s
.
Send
(
from
,
to
,
m
);
err
!=
nil
{
return
err
}
return
nil
}
func
(
m
*
Message
)
getFrom
()
(
string
,
error
)
{
from
:=
m
.
header
[
"Sender"
]
if
len
(
from
)
==
0
{
from
=
m
.
header
[
"From"
]
if
len
(
from
)
==
0
{
return
""
,
errors
.
New
(
`gomail: invalid message, "From" field is absent`
)
}
}
return
parseAddress
(
from
[
0
])
}
func
(
m
*
Message
)
getRecipients
()
([]
string
,
error
)
{
n
:=
0
for
_
,
field
:=
range
[]
string
{
"To"
,
"Cc"
,
"Bcc"
}
{
if
addresses
,
ok
:=
m
.
header
[
field
];
ok
{
n
+=
len
(
addresses
)
}
}
list
:=
make
([]
string
,
0
,
n
)
for
_
,
field
:=
range
[]
string
{
"To"
,
"Cc"
,
"Bcc"
}
{
if
addresses
,
ok
:=
m
.
header
[
field
];
ok
{
for
_
,
a
:=
range
addresses
{
addr
,
err
:=
parseAddress
(
a
)
if
err
!=
nil
{
return
nil
,
err
}
list
=
addAddress
(
list
,
addr
)
}
}
}
return
list
,
nil
}
func
addAddress
(
list
[]
string
,
addr
string
)
[]
string
{
for
_
,
a
:=
range
list
{
if
addr
==
a
{
return
list
}
}
return
append
(
list
,
addr
)
}
func
parseAddress
(
field
string
)
(
string
,
error
)
{
a
,
err
:=
mail
.
ParseAddress
(
field
)
if
a
==
nil
{
return
""
,
err
}
return
a
.
Address
,
err
}
Godeps/_workspace/src/gopkg.in/gomail.v2/send_test.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"bytes"
"io"
"reflect"
"testing"
)
const
(
testTo1
=
"to1@example.com"
testTo2
=
"to2@example.com"
testFrom
=
"from@example.com"
testBody
=
"Test message"
testMsg
=
"To: "
+
testTo1
+
", "
+
testTo2
+
"
\r\n
"
+
"From: "
+
testFrom
+
"
\r\n
"
+
"Mime-Version: 1.0
\r\n
"
+
"Date: Wed, 25 Jun 2014 17:46:00 +0000
\r\n
"
+
"Content-Type: text/plain; charset=UTF-8
\r\n
"
+
"Content-Transfer-Encoding: quoted-printable
\r\n
"
+
"
\r\n
"
+
testBody
)
type
mockSender
SendFunc
func
(
s
mockSender
)
Send
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
{
return
s
(
from
,
to
,
msg
)
}
type
mockSendCloser
struct
{
mockSender
close
func
()
error
}
func
(
s
*
mockSendCloser
)
Close
()
error
{
return
s
.
close
()
}
func
TestSend
(
t
*
testing
.
T
)
{
s
:=
&
mockSendCloser
{
mockSender
:
stubSend
(
t
,
testFrom
,
[]
string
{
testTo1
,
testTo2
},
testMsg
),
close
:
func
()
error
{
t
.
Error
(
"Close() should not be called in Send()"
)
return
nil
},
}
if
err
:=
Send
(
s
,
getTestMessage
());
err
!=
nil
{
t
.
Errorf
(
"Send(): %v"
,
err
)
}
}
func
getTestMessage
()
*
Message
{
m
:=
NewMessage
()
m
.
SetHeader
(
"From"
,
testFrom
)
m
.
SetHeader
(
"To"
,
testTo1
,
testTo2
)
m
.
SetBody
(
"text/plain"
,
testBody
)
return
m
}
func
stubSend
(
t
*
testing
.
T
,
wantFrom
string
,
wantTo
[]
string
,
wantBody
string
)
mockSender
{
return
func
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
{
if
from
!=
wantFrom
{
t
.
Errorf
(
"invalid from, got %q, want %q"
,
from
,
wantFrom
)
}
if
!
reflect
.
DeepEqual
(
to
,
wantTo
)
{
t
.
Errorf
(
"invalid to, got %v, want %v"
,
to
,
wantTo
)
}
buf
:=
new
(
bytes
.
Buffer
)
_
,
err
:=
msg
.
WriteTo
(
buf
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
compareBodies
(
t
,
buf
.
String
(),
wantBody
)
return
nil
}
}
Godeps/_workspace/src/gopkg.in/gomail.v2/smtp.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"crypto/tls"
"fmt"
"io"
"net"
"net/smtp"
)
// A Dialer is a dialer to an SMTP server.
type
Dialer
struct
{
// Host represents the host of the SMTP server.
Host
string
// Port represents the port of the SMTP server.
Port
int
// Auth represents the authentication mechanism used to authenticate to the
// SMTP server.
Auth
smtp
.
Auth
// SSL defines whether an SSL connection is used. It should be false in
// most cases since the authentication mechanism should use the STARTTLS
// extension instead.
SSL
bool
// TSLConfig represents the TLS configuration used for the TLS (when the
// STARTTLS extension is used) or SSL connection.
TLSConfig
*
tls
.
Config
}
// NewPlainDialer returns a Dialer. The given parameters are used to connect to
// the SMTP server via a PLAIN authentication mechanism.
//
// It fallbacks to the LOGIN mechanism if it is the only mechanism advertised by
// the server.
func
NewPlainDialer
(
host
string
,
port
int
,
username
,
password
string
)
*
Dialer
{
return
&
Dialer
{
Host
:
host
,
Port
:
port
,
Auth
:
&
plainAuth
{
username
:
username
,
password
:
password
,
host
:
host
,
},
SSL
:
port
==
465
,
}
}
// Dial dials and authenticates to an SMTP server. The returned SendCloser
// should be closed when done using it.
func
(
d
*
Dialer
)
Dial
()
(
SendCloser
,
error
)
{
c
,
err
:=
d
.
dial
()
if
err
!=
nil
{
return
nil
,
err
}
if
d
.
Auth
!=
nil
{
if
ok
,
_
:=
c
.
Extension
(
"AUTH"
);
ok
{
if
err
=
c
.
Auth
(
d
.
Auth
);
err
!=
nil
{
c
.
Close
()
return
nil
,
err
}
}
}
return
&
smtpSender
{
c
},
nil
}
func
(
d
*
Dialer
)
dial
()
(
smtpClient
,
error
)
{
if
d
.
SSL
{
return
d
.
sslDial
()
}
return
d
.
starttlsDial
()
}
func
(
d
*
Dialer
)
starttlsDial
()
(
smtpClient
,
error
)
{
c
,
err
:=
smtpDial
(
addr
(
d
.
Host
,
d
.
Port
))
if
err
!=
nil
{
return
nil
,
err
}
if
ok
,
_
:=
c
.
Extension
(
"STARTTLS"
);
ok
{
if
err
:=
c
.
StartTLS
(
d
.
tlsConfig
());
err
!=
nil
{
c
.
Close
()
return
nil
,
err
}
}
return
c
,
nil
}
func
(
d
*
Dialer
)
sslDial
()
(
smtpClient
,
error
)
{
conn
,
err
:=
tlsDial
(
"tcp"
,
addr
(
d
.
Host
,
d
.
Port
),
d
.
tlsConfig
())
if
err
!=
nil
{
return
nil
,
err
}
return
newClient
(
conn
,
d
.
Host
)
}
func
(
d
*
Dialer
)
tlsConfig
()
*
tls
.
Config
{
if
d
.
TLSConfig
==
nil
{
return
&
tls
.
Config
{
ServerName
:
d
.
Host
}
}
return
d
.
TLSConfig
}
func
addr
(
host
string
,
port
int
)
string
{
return
fmt
.
Sprintf
(
"%s:%d"
,
host
,
port
)
}
// DialAndSend opens a connection to the SMTP server, sends the given emails and
// closes the connection.
func
(
d
*
Dialer
)
DialAndSend
(
m
...*
Message
)
error
{
s
,
err
:=
d
.
Dial
()
if
err
!=
nil
{
return
err
}
defer
s
.
Close
()
return
Send
(
s
,
m
...
)
}
type
smtpSender
struct
{
smtpClient
}
func
(
c
*
smtpSender
)
Send
(
from
string
,
to
[]
string
,
msg
io
.
WriterTo
)
error
{
if
err
:=
c
.
Mail
(
from
);
err
!=
nil
{
return
err
}
for
_
,
addr
:=
range
to
{
if
err
:=
c
.
Rcpt
(
addr
);
err
!=
nil
{
return
err
}
}
w
,
err
:=
c
.
Data
()
if
err
!=
nil
{
return
err
}
if
_
,
err
=
msg
.
WriteTo
(
w
);
err
!=
nil
{
w
.
Close
()
return
err
}
return
w
.
Close
()
}
func
(
c
*
smtpSender
)
Close
()
error
{
return
c
.
Quit
()
}
// Stubbed out for tests.
var
(
smtpDial
=
func
(
addr
string
)
(
smtpClient
,
error
)
{
return
smtp
.
Dial
(
addr
)
}
tlsDial
=
tls
.
Dial
newClient
=
func
(
conn
net
.
Conn
,
host
string
)
(
smtpClient
,
error
)
{
return
smtp
.
NewClient
(
conn
,
host
)
}
)
type
smtpClient
interface
{
Extension
(
string
)
(
bool
,
string
)
StartTLS
(
*
tls
.
Config
)
error
Auth
(
smtp
.
Auth
)
error
Mail
(
string
)
error
Rcpt
(
string
)
error
Data
()
(
io
.
WriteCloser
,
error
)
Quit
()
error
Close
()
error
}
Godeps/_workspace/src/gopkg.in/gomail.v2/smtp_test.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"bytes"
"crypto/tls"
"io"
"net"
"net/smtp"
"reflect"
"testing"
)
const
(
testPort
=
587
testSSLPort
=
465
)
var
(
testTLSConn
=
&
tls
.
Conn
{}
testConfig
=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
)
func
TestDialer
(
t
*
testing
.
T
)
{
d
:=
NewPlainDialer
(
testHost
,
testPort
,
"user"
,
"pwd"
)
testSendMail
(
t
,
d
,
[]
string
{
"Extension STARTTLS"
,
"StartTLS"
,
"Extension AUTH"
,
"Auth"
,
"Mail "
+
testFrom
,
"Rcpt "
+
testTo1
,
"Rcpt "
+
testTo2
,
"Data"
,
"Write message"
,
"Close writer"
,
"Quit"
,
"Close"
,
})
}
func
TestDialerSSL
(
t
*
testing
.
T
)
{
d
:=
NewPlainDialer
(
testHost
,
testSSLPort
,
"user"
,
"pwd"
)
testSendMail
(
t
,
d
,
[]
string
{
"Extension AUTH"
,
"Auth"
,
"Mail "
+
testFrom
,
"Rcpt "
+
testTo1
,
"Rcpt "
+
testTo2
,
"Data"
,
"Write message"
,
"Close writer"
,
"Quit"
,
"Close"
,
})
}
func
TestDialerConfig
(
t
*
testing
.
T
)
{
d
:=
NewPlainDialer
(
testHost
,
testPort
,
"user"
,
"pwd"
)
d
.
TLSConfig
=
testConfig
testSendMail
(
t
,
d
,
[]
string
{
"Extension STARTTLS"
,
"StartTLS"
,
"Extension AUTH"
,
"Auth"
,
"Mail "
+
testFrom
,
"Rcpt "
+
testTo1
,
"Rcpt "
+
testTo2
,
"Data"
,
"Write message"
,
"Close writer"
,
"Quit"
,
"Close"
,
})
}
func
TestDialerSSLConfig
(
t
*
testing
.
T
)
{
d
:=
NewPlainDialer
(
testHost
,
testSSLPort
,
"user"
,
"pwd"
)
d
.
TLSConfig
=
testConfig
testSendMail
(
t
,
d
,
[]
string
{
"Extension AUTH"
,
"Auth"
,
"Mail "
+
testFrom
,
"Rcpt "
+
testTo1
,
"Rcpt "
+
testTo2
,
"Data"
,
"Write message"
,
"Close writer"
,
"Quit"
,
"Close"
,
})
}
func
TestDialerNoAuth
(
t
*
testing
.
T
)
{
d
:=
&
Dialer
{
Host
:
testHost
,
Port
:
testPort
,
}
testSendMail
(
t
,
d
,
[]
string
{
"Extension STARTTLS"
,
"StartTLS"
,
"Mail "
+
testFrom
,
"Rcpt "
+
testTo1
,
"Rcpt "
+
testTo2
,
"Data"
,
"Write message"
,
"Close writer"
,
"Quit"
,
"Close"
,
})
}
type
mockClient
struct
{
t
*
testing
.
T
i
int
want
[]
string
addr
string
auth
smtp
.
Auth
config
*
tls
.
Config
}
func
(
c
*
mockClient
)
Extension
(
ext
string
)
(
bool
,
string
)
{
c
.
do
(
"Extension "
+
ext
)
return
true
,
""
}
func
(
c
*
mockClient
)
StartTLS
(
config
*
tls
.
Config
)
error
{
assertConfig
(
c
.
t
,
config
,
c
.
config
)
c
.
do
(
"StartTLS"
)
return
nil
}
func
(
c
*
mockClient
)
Auth
(
a
smtp
.
Auth
)
error
{
assertAuth
(
c
.
t
,
a
,
c
.
auth
)
c
.
do
(
"Auth"
)
return
nil
}
func
(
c
*
mockClient
)
Mail
(
from
string
)
error
{
c
.
do
(
"Mail "
+
from
)
return
nil
}
func
(
c
*
mockClient
)
Rcpt
(
to
string
)
error
{
c
.
do
(
"Rcpt "
+
to
)
return
nil
}
func
(
c
*
mockClient
)
Data
()
(
io
.
WriteCloser
,
error
)
{
c
.
do
(
"Data"
)
return
&
mockWriter
{
c
:
c
,
want
:
testMsg
},
nil
}
func
(
c
*
mockClient
)
Quit
()
error
{
c
.
do
(
"Quit"
)
return
nil
}
func
(
c
*
mockClient
)
Close
()
error
{
c
.
do
(
"Close"
)
return
nil
}
func
(
c
*
mockClient
)
do
(
cmd
string
)
{
if
c
.
i
>=
len
(
c
.
want
)
{
c
.
t
.
Fatalf
(
"Invalid command %q"
,
cmd
)
}
if
cmd
!=
c
.
want
[
c
.
i
]
{
c
.
t
.
Fatalf
(
"Invalid command, got %q, want %q"
,
cmd
,
c
.
want
[
c
.
i
])
}
c
.
i
++
}
type
mockWriter
struct
{
want
string
c
*
mockClient
buf
bytes
.
Buffer
}
func
(
w
*
mockWriter
)
Write
(
p
[]
byte
)
(
int
,
error
)
{
if
w
.
buf
.
Len
()
==
0
{
w
.
c
.
do
(
"Write message"
)
}
w
.
buf
.
Write
(
p
)
return
len
(
p
),
nil
}
func
(
w
*
mockWriter
)
Close
()
error
{
compareBodies
(
w
.
c
.
t
,
w
.
buf
.
String
(),
w
.
want
)
w
.
c
.
do
(
"Close writer"
)
return
nil
}
func
testSendMail
(
t
*
testing
.
T
,
d
*
Dialer
,
want
[]
string
)
{
testClient
:=
&
mockClient
{
t
:
t
,
want
:
want
,
addr
:
addr
(
d
.
Host
,
d
.
Port
),
auth
:
testAuth
,
config
:
d
.
TLSConfig
,
}
smtpDial
=
func
(
addr
string
)
(
smtpClient
,
error
)
{
assertAddr
(
t
,
addr
,
testClient
.
addr
)
return
testClient
,
nil
}
tlsDial
=
func
(
network
,
addr
string
,
config
*
tls
.
Config
)
(
*
tls
.
Conn
,
error
)
{
if
network
!=
"tcp"
{
t
.
Errorf
(
"Invalid network, got %q, want tcp"
,
network
)
}
assertAddr
(
t
,
addr
,
testClient
.
addr
)
assertConfig
(
t
,
config
,
testClient
.
config
)
return
testTLSConn
,
nil
}
newClient
=
func
(
conn
net
.
Conn
,
host
string
)
(
smtpClient
,
error
)
{
if
conn
!=
testTLSConn
{
t
.
Error
(
"Invalid TLS connection used"
)
}
if
host
!=
testHost
{
t
.
Errorf
(
"Invalid host, got %q, want %q"
,
host
,
testHost
)
}
return
testClient
,
nil
}
if
err
:=
d
.
DialAndSend
(
getTestMessage
());
err
!=
nil
{
t
.
Error
(
err
)
}
}
func
assertAuth
(
t
*
testing
.
T
,
got
,
want
smtp
.
Auth
)
{
if
!
reflect
.
DeepEqual
(
got
,
want
)
{
t
.
Errorf
(
"Invalid auth, got %#v, want %#v"
,
got
,
want
)
}
}
func
assertAddr
(
t
*
testing
.
T
,
got
,
want
string
)
{
if
got
!=
want
{
t
.
Errorf
(
"Invalid addr, got %q, want %q"
,
got
,
want
)
}
}
func
assertConfig
(
t
*
testing
.
T
,
got
,
want
*
tls
.
Config
)
{
if
want
==
nil
{
want
=
&
tls
.
Config
{
ServerName
:
testHost
}
}
if
got
.
ServerName
!=
want
.
ServerName
{
t
.
Errorf
(
"Invalid field ServerName in config, got %q, want %q"
,
got
.
ServerName
,
want
.
ServerName
)
}
if
got
.
InsecureSkipVerify
!=
want
.
InsecureSkipVerify
{
t
.
Errorf
(
"Invalid field InsecureSkipVerify in config, got %v, want %v"
,
got
.
InsecureSkipVerify
,
want
.
InsecureSkipVerify
)
}
}
Godeps/_workspace/src/gopkg.in/gomail.v2/writeto.go
0 → 100644
View file @
97041dbd
package
gomail
import
(
"encoding/base64"
"errors"
"io"
"mime"
"mime/multipart"
"path/filepath"
"time"
)
// WriteTo implements io.WriterTo. It dumps the whole message into w.
func
(
m
*
Message
)
WriteTo
(
w
io
.
Writer
)
(
int64
,
error
)
{
mw
:=
&
messageWriter
{
w
:
w
}
mw
.
writeMessage
(
m
)
return
mw
.
n
,
mw
.
err
}
func
(
w
*
messageWriter
)
writeMessage
(
m
*
Message
)
{
if
_
,
ok
:=
m
.
header
[
"Mime-Version"
];
!
ok
{
w
.
writeString
(
"Mime-Version: 1.0
\r\n
"
)
}
if
_
,
ok
:=
m
.
header
[
"Date"
];
!
ok
{
w
.
writeHeader
(
"Date"
,
m
.
FormatDate
(
now
()))
}
w
.
writeHeaders
(
m
.
header
)
if
m
.
hasMixedPart
()
{
w
.
openMultipart
(
"mixed"
)
}
if
m
.
hasRelatedPart
()
{
w
.
openMultipart
(
"related"
)
}
if
m
.
hasAlternativePart
()
{
w
.
openMultipart
(
"alternative"
)
}
for
_
,
part
:=
range
m
.
parts
{
w
.
writeHeaders
(
part
.
header
)
w
.
writeBody
(
part
.
copier
,
m
.
encoding
)
}
if
m
.
hasAlternativePart
()
{
w
.
closeMultipart
()
}
w
.
addFiles
(
m
.
embedded
,
false
)
if
m
.
hasRelatedPart
()
{
w
.
closeMultipart
()
}
w
.
addFiles
(
m
.
attachments
,
true
)
if
m
.
hasMixedPart
()
{
w
.
closeMultipart
()
}
}
func
(
m
*
Message
)
hasMixedPart
()
bool
{
return
(
len
(
m
.
parts
)
>
0
&&
len
(
m
.
attachments
)
>
0
)
||
len
(
m
.
attachments
)
>
1
}
func
(
m
*
Message
)
hasRelatedPart
()
bool
{
return
(
len
(
m
.
parts
)
>
0
&&
len
(
m
.
embedded
)
>
0
)
||
len
(
m
.
embedded
)
>
1
}
func
(
m
*
Message
)
hasAlternativePart
()
bool
{
return
len
(
m
.
parts
)
>
1
}
type
messageWriter
struct
{
w
io
.
Writer
n
int64
writers
[
3
]
*
multipart
.
Writer
partWriter
io
.
Writer
depth
uint8
err
error
}
func
(
w
*
messageWriter
)
openMultipart
(
mimeType
string
)
{
mw
:=
multipart
.
NewWriter
(
w
)
contentType
:=
"multipart/"
+
mimeType
+
"; boundary="
+
mw
.
Boundary
()
w
.
writers
[
w
.
depth
]
=
mw
if
w
.
depth
==
0
{
w
.
writeHeader
(
"Content-Type"
,
contentType
)
w
.
writeString
(
"
\r\n
"
)
}
else
{
w
.
createPart
(
map
[
string
][]
string
{
"Content-Type"
:
{
contentType
},
})
}
w
.
depth
++
}
func
(
w
*
messageWriter
)
createPart
(
h
map
[
string
][]
string
)
{
w
.
partWriter
,
w
.
err
=
w
.
writers
[
w
.
depth
-
1
]
.
CreatePart
(
h
)
}
func
(
w
*
messageWriter
)
closeMultipart
()
{
if
w
.
depth
>
0
{
w
.
writers
[
w
.
depth
-
1
]
.
Close
()
w
.
depth
--
}
}
func
(
w
*
messageWriter
)
addFiles
(
files
[]
*
file
,
isAttachment
bool
)
{
for
_
,
f
:=
range
files
{
if
_
,
ok
:=
f
.
Header
[
"Content-Type"
];
!
ok
{
mediaType
:=
mime
.
TypeByExtension
(
filepath
.
Ext
(
f
.
Name
))
if
mediaType
==
""
{
mediaType
=
"application/octet-stream"
}
f
.
setHeader
(
"Content-Type"
,
mediaType
+
`; name="`
+
f
.
Name
+
`"`
)
}
if
_
,
ok
:=
f
.
Header
[
"Content-Transfer-Encoding"
];
!
ok
{
f
.
setHeader
(
"Content-Transfer-Encoding"
,
string
(
Base64
))
}
if
_
,
ok
:=
f
.
Header
[
"Content-Disposition"
];
!
ok
{
var
disp
string
if
isAttachment
{
disp
=
"attachment"
}
else
{
disp
=
"inline"
}
f
.
setHeader
(
"Content-Disposition"
,
disp
+
`; filename="`
+
f
.
Name
+
`"`
)
}
if
!
isAttachment
{
if
_
,
ok
:=
f
.
Header
[
"Content-ID"
];
!
ok
{
f
.
setHeader
(
"Content-ID"
,
"<"
+
f
.
Name
+
">"
)
}
}
w
.
writeHeaders
(
f
.
Header
)
w
.
writeBody
(
f
.
CopyFunc
,
Base64
)
}
}
func
(
w
*
messageWriter
)
Write
(
p
[]
byte
)
(
int
,
error
)
{
if
w
.
err
!=
nil
{
return
0
,
errors
.
New
(
"gomail: cannot write as writer is in error"
)
}
var
n
int
n
,
w
.
err
=
w
.
w
.
Write
(
p
)
w
.
n
+=
int64
(
n
)
return
n
,
w
.
err
}
func
(
w
*
messageWriter
)
writeString
(
s
string
)
{
n
,
_
:=
io
.
WriteString
(
w
.
w
,
s
)
w
.
n
+=
int64
(
n
)
}
func
(
w
*
messageWriter
)
writeStrings
(
a
[]
string
,
sep
string
)
{
if
len
(
a
)
>
0
{
w
.
writeString
(
a
[
0
])
if
len
(
a
)
==
1
{
return
}
}
for
_
,
s
:=
range
a
[
1
:
]
{
w
.
writeString
(
sep
)
w
.
writeString
(
s
)
}
}
func
(
w
*
messageWriter
)
writeHeader
(
k
string
,
v
...
string
)
{
w
.
writeString
(
k
)
w
.
writeString
(
": "
)
w
.
writeStrings
(
v
,
", "
)
w
.
writeString
(
"
\r\n
"
)
}
func
(
w
*
messageWriter
)
writeHeaders
(
h
map
[
string
][]
string
)
{
if
w
.
depth
==
0
{
for
k
,
v
:=
range
h
{
if
k
!=
"Bcc"
{
w
.
writeHeader
(
k
,
v
...
)
}
}
}
else
{
w
.
createPart
(
h
)
}
}
func
(
w
*
messageWriter
)
writeBody
(
f
func
(
io
.
Writer
)
error
,
enc
Encoding
)
{
var
subWriter
io
.
Writer
if
w
.
depth
==
0
{
w
.
writeString
(
"
\r\n
"
)
subWriter
=
w
.
w
}
else
{
subWriter
=
w
.
partWriter
}
if
enc
==
Base64
{
wc
:=
base64
.
NewEncoder
(
base64
.
StdEncoding
,
newBase64LineWriter
(
subWriter
))
w
.
err
=
f
(
wc
)
wc
.
Close
()
}
else
if
enc
==
Unencoded
{
w
.
err
=
f
(
subWriter
)
}
else
{
wc
:=
newQPWriter
(
subWriter
)
w
.
err
=
f
(
wc
)
wc
.
Close
()
}
}
// As required by RFC 2045, 6.7. (page 21) for quoted-printable, and
// RFC 2045, 6.8. (page 25) for base64.
const
maxLineLen
=
76
// base64LineWriter limits text encoded in base64 to 76 characters per line
type
base64LineWriter
struct
{
w
io
.
Writer
lineLen
int
}
func
newBase64LineWriter
(
w
io
.
Writer
)
*
base64LineWriter
{
return
&
base64LineWriter
{
w
:
w
}
}
func
(
w
*
base64LineWriter
)
Write
(
p
[]
byte
)
(
int
,
error
)
{
n
:=
0
for
len
(
p
)
+
w
.
lineLen
>
maxLineLen
{
w
.
w
.
Write
(
p
[
:
maxLineLen
-
w
.
lineLen
])
w
.
w
.
Write
([]
byte
(
"
\r\n
"
))
p
=
p
[
maxLineLen
-
w
.
lineLen
:
]
n
+=
maxLineLen
-
w
.
lineLen
w
.
lineLen
=
0
}
w
.
w
.
Write
(
p
)
w
.
lineLen
+=
len
(
p
)
return
n
+
len
(
p
),
nil
}
// Stubbed out for testing.
var
now
=
time
.
Now
email/interface.go
View file @
97041dbd
...
...
@@ -3,6 +3,7 @@ package email
import
(
"encoding/json"
"errors"
"expvar"
"fmt"
"io"
"os"
...
...
@@ -14,6 +15,7 @@ const (
)
var
(
counterEmailSendErr
=
expvar
.
NewInt
(
"email.send.err"
)
ErrorNoTemplate
=
errors
.
New
(
"No HTML or Text template found for template name."
)
)
...
...
@@ -63,7 +65,6 @@ func NewEmailerConfigFromFile(loc string) (EmailerConfig, error) {
}
type
FakeEmailerConfig
struct
{
ID
string
`json:"id"`
}
func
(
cfg
FakeEmailerConfig
)
EmailerType
()
string
{
...
...
@@ -71,7 +72,7 @@ func (cfg FakeEmailerConfig) EmailerType() string {
}
func
(
cfg
FakeEmailerConfig
)
EmailerID
()
string
{
return
cfg
.
ID
return
FakeEmailerType
}
func
(
cfg
FakeEmailerConfig
)
Emailer
()
(
Emailer
,
error
)
{
...
...
email/mailgun.go
View file @
97041dbd
...
...
@@ -3,7 +3,6 @@ package email
import
(
"encoding/json"
"errors"
"expvar"
"github.com/coreos/dex/pkg/log"
mailgun
"github.com/mailgun/mailgun-go"
...
...
@@ -13,16 +12,11 @@ const (
MailgunEmailerType
=
"mailgun"
)
var
(
counterEmailSendErr
=
expvar
.
NewInt
(
"mailgun.send.err"
)
)
func
init
()
{
RegisterEmailerConfigType
(
MailgunEmailerType
,
func
()
EmailerConfig
{
return
&
MailgunEmailerConfig
{}
})
}
type
MailgunEmailerConfig
struct
{
ID
string
`json:"id"`
PrivateAPIKey
string
`json:"privateAPIKey"`
PublicAPIKey
string
`json:"publicAPIKey"`
Domain
string
`json:"domain"`
...
...
@@ -33,7 +27,7 @@ func (cfg MailgunEmailerConfig) EmailerType() string {
}
func
(
cfg
MailgunEmailerConfig
)
EmailerID
()
string
{
return
cfg
.
ID
return
MailgunEmailerType
}
func
(
cfg
MailgunEmailerConfig
)
Emailer
()
(
Emailer
,
error
)
{
...
...
email/smtp.go
0 → 100644
View file @
97041dbd
package
email
import
(
"encoding/json"
"errors"
"gopkg.in/gomail.v2"
)
const
(
SmtpEmailerType
=
"smtp"
)
func
init
()
{
RegisterEmailerConfigType
(
SmtpEmailerType
,
func
()
EmailerConfig
{
return
&
SmtpEmailerConfig
{}
})
}
type
SmtpEmailerConfig
struct
{
Host
string
`json:"host"`
Port
int
`json:"port"`
Auth
string
`json:"auth"`
Username
string
`json:"username"`
Password
string
`json:"password"`
}
func
(
cfg
SmtpEmailerConfig
)
EmailerType
()
string
{
return
SmtpEmailerType
}
func
(
cfg
SmtpEmailerConfig
)
EmailerID
()
string
{
return
SmtpEmailerType
}
func
(
cfg
SmtpEmailerConfig
)
Emailer
()
(
Emailer
,
error
)
{
var
dialer
*
gomail
.
Dialer
if
cfg
.
Auth
==
"plain"
{
dialer
=
gomail
.
NewPlainDialer
(
cfg
.
Host
,
cfg
.
Port
,
cfg
.
Username
,
cfg
.
Password
)
}
else
{
dialer
=
&
gomail
.
Dialer
{
Host
:
cfg
.
Host
,
Port
:
cfg
.
Port
,
}
}
return
&
smtpEmailer
{
dialer
:
dialer
,
},
nil
}
type
smtpEmailerConfig
SmtpEmailerConfig
func
(
cfg
*
SmtpEmailerConfig
)
UnmarshalJSON
(
data
[]
byte
)
error
{
smtpCfg
:=
smtpEmailerConfig
{}
err
:=
json
.
Unmarshal
(
data
,
&
smtpCfg
)
if
err
!=
nil
{
return
err
}
if
smtpCfg
.
Host
==
""
{
return
errors
.
New
(
"must set SMTP host"
)
}
if
smtpCfg
.
Port
==
""
{
return
errors
.
New
(
"must set SMTP port"
)
}
*
cfg
=
SmtpEmailerConfig
(
smtpCfg
)
return
nil
}
type
smtpEmailer
struct
{
dialer
*
gomail
.
Dialer
}
func
(
emailer
*
smtpEmailer
)
SendMail
(
from
,
subject
,
text
,
html
string
,
to
...
string
)
error
{
msg
:=
gomail
.
NewMessage
()
msg
.
SetHeader
(
"From"
,
from
)
msg
.
SetHeader
(
"To"
,
to
...
)
msg
.
SetHeader
(
"Subject"
,
subject
)
msg
.
SetBody
(
"text/plain"
,
text
)
msg
.
SetBody
(
"text/html"
,
html
)
err
:=
emailer
.
dialer
.
DialAndSend
(
msg
)
if
err
!=
nil
{
counterEmailSendErr
.
Add
(
1
)
return
err
}
return
nil
}
static/fixtures/emailer.json.sample
View file @
97041dbd
{
"type": "fake",
"id": "fake"
"type": "fake"
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment