| 1 | 
         747 | 
         jeremybenn | 
         // Copyright 2010 The Go Authors. All rights reserved.
  | 
      
      
         | 2 | 
          | 
          | 
         // Use of this source code is governed by a BSD-style
  | 
      
      
         | 3 | 
          | 
          | 
         // license that can be found in the LICENSE file.
  | 
      
      
         | 4 | 
          | 
          | 
          
  | 
      
      
         | 5 | 
          | 
          | 
         package smtp
  | 
      
      
         | 6 | 
          | 
          | 
          
  | 
      
      
         | 7 | 
          | 
          | 
         import (
  | 
      
      
         | 8 | 
          | 
          | 
                 "crypto/hmac"
  | 
      
      
         | 9 | 
          | 
          | 
                 "crypto/md5"
  | 
      
      
         | 10 | 
          | 
          | 
                 "errors"
  | 
      
      
         | 11 | 
          | 
          | 
                 "fmt"
  | 
      
      
         | 12 | 
          | 
          | 
         )
  | 
      
      
         | 13 | 
          | 
          | 
          
  | 
      
      
         | 14 | 
          | 
          | 
         // Auth is implemented by an SMTP authentication mechanism.
  | 
      
      
         | 15 | 
          | 
          | 
         type Auth interface {
  | 
      
      
         | 16 | 
          | 
          | 
                 // Start begins an authentication with a server.
  | 
      
      
         | 17 | 
          | 
          | 
                 // It returns the name of the authentication protocol
  | 
      
      
         | 18 | 
          | 
          | 
                 // and optionally data to include in the initial AUTH message
  | 
      
      
         | 19 | 
          | 
          | 
                 // sent to the server. It can return proto == "" to indicate
  | 
      
      
         | 20 | 
          | 
          | 
                 // that the authentication should be skipped.
  | 
      
      
         | 21 | 
          | 
          | 
                 // If it returns a non-nil error, the SMTP client aborts
  | 
      
      
         | 22 | 
          | 
          | 
                 // the authentication attempt and closes the connection.
  | 
      
      
         | 23 | 
          | 
          | 
                 Start(server *ServerInfo) (proto string, toServer []byte, err error)
  | 
      
      
         | 24 | 
          | 
          | 
          
  | 
      
      
         | 25 | 
          | 
          | 
                 // Next continues the authentication. The server has just sent
  | 
      
      
         | 26 | 
          | 
          | 
                 // the fromServer data. If more is true, the server expects a
  | 
      
      
         | 27 | 
          | 
          | 
                 // response, which Next should return as toServer; otherwise
  | 
      
      
         | 28 | 
          | 
          | 
                 // Next should return toServer == nil.
  | 
      
      
         | 29 | 
          | 
          | 
                 // If Next returns a non-nil error, the SMTP client aborts
  | 
      
      
         | 30 | 
          | 
          | 
                 // the authentication attempt and closes the connection.
  | 
      
      
         | 31 | 
          | 
          | 
                 Next(fromServer []byte, more bool) (toServer []byte, err error)
  | 
      
      
         | 32 | 
          | 
          | 
         }
  | 
      
      
         | 33 | 
          | 
          | 
          
  | 
      
      
         | 34 | 
          | 
          | 
         // ServerInfo records information about an SMTP server.
  | 
      
      
         | 35 | 
          | 
          | 
         type ServerInfo struct {
  | 
      
      
         | 36 | 
          | 
          | 
                 Name string   // SMTP server name
  | 
      
      
         | 37 | 
          | 
          | 
                 TLS  bool     // using TLS, with valid certificate for Name
  | 
      
      
         | 38 | 
          | 
          | 
                 Auth []string // advertised authentication mechanisms
  | 
      
      
         | 39 | 
          | 
          | 
         }
  | 
      
      
         | 40 | 
          | 
          | 
          
  | 
      
      
         | 41 | 
          | 
          | 
         type plainAuth struct {
  | 
      
      
         | 42 | 
          | 
          | 
                 identity, username, password string
  | 
      
      
         | 43 | 
          | 
          | 
                 host                         string
  | 
      
      
         | 44 | 
          | 
          | 
         }
  | 
      
      
         | 45 | 
          | 
          | 
          
  | 
      
      
         | 46 | 
          | 
          | 
         // PlainAuth returns an Auth that implements the PLAIN authentication
  | 
      
      
         | 47 | 
          | 
          | 
         // mechanism as defined in RFC 4616.
  | 
      
      
         | 48 | 
          | 
          | 
         // The returned Auth uses the given username and password to authenticate
  | 
      
      
         | 49 | 
          | 
          | 
         // on TLS connections to host and act as identity. Usually identity will be
  | 
      
      
         | 50 | 
          | 
          | 
         // left blank to act as username.
  | 
      
      
         | 51 | 
          | 
          | 
         func PlainAuth(identity, username, password, host string) Auth {
  | 
      
      
         | 52 | 
          | 
          | 
                 return &plainAuth{identity, username, password, host}
  | 
      
      
         | 53 | 
          | 
          | 
         }
  | 
      
      
         | 54 | 
          | 
          | 
          
  | 
      
      
         | 55 | 
          | 
          | 
         func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
  | 
      
      
         | 56 | 
          | 
          | 
                 if !server.TLS {
  | 
      
      
         | 57 | 
          | 
          | 
                         return "", nil, errors.New("unencrypted connection")
  | 
      
      
         | 58 | 
          | 
          | 
                 }
  | 
      
      
         | 59 | 
          | 
          | 
                 if server.Name != a.host {
  | 
      
      
         | 60 | 
          | 
          | 
                         return "", nil, errors.New("wrong host name")
  | 
      
      
         | 61 | 
          | 
          | 
                 }
  | 
      
      
         | 62 | 
          | 
          | 
                 resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password)
  | 
      
      
         | 63 | 
          | 
          | 
                 return "PLAIN", resp, nil
  | 
      
      
         | 64 | 
          | 
          | 
         }
  | 
      
      
         | 65 | 
          | 
          | 
          
  | 
      
      
         | 66 | 
          | 
          | 
         func (a *plainAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  | 
      
      
         | 67 | 
          | 
          | 
                 if more {
  | 
      
      
         | 68 | 
          | 
          | 
                         // We've already sent everything.
  | 
      
      
         | 69 | 
          | 
          | 
                         return nil, errors.New("unexpected server challenge")
  | 
      
      
         | 70 | 
          | 
          | 
                 }
  | 
      
      
         | 71 | 
          | 
          | 
                 return nil, nil
  | 
      
      
         | 72 | 
          | 
          | 
         }
  | 
      
      
         | 73 | 
          | 
          | 
          
  | 
      
      
         | 74 | 
          | 
          | 
         type cramMD5Auth struct {
  | 
      
      
         | 75 | 
          | 
          | 
                 username, secret string
  | 
      
      
         | 76 | 
          | 
          | 
         }
  | 
      
      
         | 77 | 
          | 
          | 
          
  | 
      
      
         | 78 | 
          | 
          | 
         // CRAMMD5Auth returns an Auth that implements the CRAM-MD5 authentication
  | 
      
      
         | 79 | 
          | 
          | 
         // mechanism as defined in RFC 2195.
  | 
      
      
         | 80 | 
          | 
          | 
         // The returned Auth uses the given username and secret to authenticate
  | 
      
      
         | 81 | 
          | 
          | 
         // to the server using the challenge-response mechanism.
  | 
      
      
         | 82 | 
          | 
          | 
         func CRAMMD5Auth(username, secret string) Auth {
  | 
      
      
         | 83 | 
          | 
          | 
                 return &cramMD5Auth{username, secret}
  | 
      
      
         | 84 | 
          | 
          | 
         }
  | 
      
      
         | 85 | 
          | 
          | 
          
  | 
      
      
         | 86 | 
          | 
          | 
         func (a *cramMD5Auth) Start(server *ServerInfo) (string, []byte, error) {
  | 
      
      
         | 87 | 
          | 
          | 
                 return "CRAM-MD5", nil, nil
  | 
      
      
         | 88 | 
          | 
          | 
         }
  | 
      
      
         | 89 | 
          | 
          | 
          
  | 
      
      
         | 90 | 
          | 
          | 
         func (a *cramMD5Auth) Next(fromServer []byte, more bool) ([]byte, error) {
  | 
      
      
         | 91 | 
          | 
          | 
                 if more {
  | 
      
      
         | 92 | 
          | 
          | 
                         d := hmac.New(md5.New, []byte(a.secret))
  | 
      
      
         | 93 | 
          | 
          | 
                         d.Write(fromServer)
  | 
      
      
         | 94 | 
          | 
          | 
                         s := make([]byte, 0, d.Size())
  | 
      
      
         | 95 | 
          | 
          | 
                         return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil
  | 
      
      
         | 96 | 
          | 
          | 
                 }
  | 
      
      
         | 97 | 
          | 
          | 
                 return nil, nil
  | 
      
      
         | 98 | 
          | 
          | 
         }
  |