Skip to content

Commit

Permalink
Added the LOGIN authentication mechanism support
Browse files Browse the repository at this point in the history
Closes #16.
  • Loading branch information
alexcesaro committed Feb 10, 2015
1 parent d729406 commit de623e6
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ bypass the verification of the server's certificate chain and host name by using

Note, however, that this is insecure and should not be used in production.

### 504 5.7.4 Unrecognized authentication type

If you get this error, you should try using the LOGIN authentication mechanism:

addr := "smtp.example.com:587"
auth := gomail.LoginAuth("username", "password", "smtp.example.com")
mailer := gomail.NewCustomMailer(addr, auth)

See [issue #16](https://github.com/go-gomail/gomail/issues/16).


## Contact

Expand Down
54 changes: 54 additions & 0 deletions login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package gomail

import (
"errors"
"fmt"
"net/smtp"
"strings"
)

type loginAuth struct {
username string
password string
host string
}

// LoginAuth returns an Auth that implements the LOGIN authentication mechanism.
func LoginAuth(username, password, host string) smtp.Auth {
return &loginAuth{username, password, host}
}

func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
if !server.TLS {
advertised := false
for _, mechanism := range server.Auth {
if mechanism == "LOGIN" {
advertised = true
break
}
}
if !advertised {
return "", nil, errors.New("gomail: unencrypted connection")
}
}
if server.Name != a.host {
return "", nil, errors.New("gomail: wrong host name")
}
return "LOGIN", nil, nil
}

func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if !more {
return nil, nil
}

command := strings.ToLower(strings.TrimSuffix(string(fromServer), ":"))
switch command {
case "username":
return []byte(fmt.Sprintf("%s", a.username)), nil
case "password":
return []byte(fmt.Sprintf("%s", a.password)), nil
default:
return nil, fmt.Errorf("gomail: unexpected server challenge: %s", command)
}
}
66 changes: 66 additions & 0 deletions login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package gomail

import (
"net/smtp"
"testing"
)

type output struct {
proto string
data []string
err error
}

const (
testUser = "user"
testPwd = "pwd"
)

func TestPlainAuth(t *testing.T) {
tests := []struct {
serverProtos []string
serverChallenges []string
proto string
data []string
}{
{
serverProtos: []string{"LOGIN"},
serverChallenges: []string{"Username:", "Password:"},
proto: "LOGIN",
data: []string{"", testUser, testPwd},
},
}

for _, test := range tests {
auth := LoginAuth(testUser, testPwd, testHost)
server := &smtp.ServerInfo{
Name: testHost,
TLS: true,
Auth: test.serverProtos,
}
proto, toServer, err := auth.Start(server)
if err != nil {
t.Fatalf("Start error: %v", err)
}
if proto != test.proto {
t.Errorf("Invalid protocol, got %q, want %q", proto, test.proto)
}

i := 0
got := string(toServer)
if got != test.data[i] {
t.Errorf("Invalid response, got %q, want %q", got, test.data[i])
}
for _, challenge := range test.serverChallenges {
toServer, err = auth.Next([]byte(challenge), true)
if err != nil {
t.Fatalf("Auth error: %v", err)
}
i++
got = string(toServer)
if got != test.data[i] {
t.Errorf("Invalid response, got %q, want %q", got, test.data[i])
}
}
}
}

2 comments on commit de623e6

@concernedrat
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please approve this asap, thanks!!! Thanks :)

@alexcesaro
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Please sign in to comment.