OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [net/] [smtp/] [smtp.go] - Blame information for rev 747

Details | Compare with Previous | View Log

Line No. Rev Author Line
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 implements the Simple Mail Transfer Protocol as defined in RFC 5321.
6
// It also implements the following extensions:
7
//      8BITMIME  RFC 1652
8
//      AUTH      RFC 2554
9
//      STARTTLS  RFC 3207
10
// Additional extensions may be handled by clients.
11
package smtp
12
 
13
import (
14
        "crypto/tls"
15
        "encoding/base64"
16
        "io"
17
        "net"
18
        "net/textproto"
19
        "strings"
20
)
21
 
22
// A Client represents a client connection to an SMTP server.
23
type Client struct {
24
        // Text is the textproto.Conn used by the Client. It is exported to allow for
25
        // clients to add extensions.
26
        Text *textproto.Conn
27
        // keep a reference to the connection so it can be used to create a TLS
28
        // connection later
29
        conn net.Conn
30
        // whether the Client is using TLS
31
        tls        bool
32
        serverName string
33
        // map of supported extensions
34
        ext map[string]string
35
        // supported auth mechanisms
36
        auth []string
37
}
38
 
39
// Dial returns a new Client connected to an SMTP server at addr.
40
func Dial(addr string) (*Client, error) {
41
        conn, err := net.Dial("tcp", addr)
42
        if err != nil {
43
                return nil, err
44
        }
45
        host := addr[:strings.Index(addr, ":")]
46
        return NewClient(conn, host)
47
}
48
 
49
// NewClient returns a new Client using an existing connection and host as a
50
// server name to be used when authenticating.
51
func NewClient(conn net.Conn, host string) (*Client, error) {
52
        text := textproto.NewConn(conn)
53
        _, msg, err := text.ReadResponse(220)
54
        if err != nil {
55
                text.Close()
56
                return nil, err
57
        }
58
        c := &Client{Text: text, conn: conn, serverName: host}
59
        if strings.Contains(msg, "ESMTP") {
60
                err = c.ehlo()
61
        } else {
62
                err = c.helo()
63
        }
64
        return c, err
65
}
66
 
67
// cmd is a convenience function that sends a command and returns the response
68
func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) {
69
        id, err := c.Text.Cmd(format, args...)
70
        if err != nil {
71
                return 0, "", err
72
        }
73
        c.Text.StartResponse(id)
74
        defer c.Text.EndResponse(id)
75
        code, msg, err := c.Text.ReadResponse(expectCode)
76
        return code, msg, err
77
}
78
 
79
// helo sends the HELO greeting to the server. It should be used only when the
80
// server does not support ehlo.
81
func (c *Client) helo() error {
82
        c.ext = nil
83
        _, _, err := c.cmd(250, "HELO localhost")
84
        return err
85
}
86
 
87
// ehlo sends the EHLO (extended hello) greeting to the server. It
88
// should be the preferred greeting for servers that support it.
89
func (c *Client) ehlo() error {
90
        _, msg, err := c.cmd(250, "EHLO localhost")
91
        if err != nil {
92
                return err
93
        }
94
        ext := make(map[string]string)
95
        extList := strings.Split(msg, "\n")
96
        if len(extList) > 1 {
97
                extList = extList[1:]
98
                for _, line := range extList {
99
                        args := strings.SplitN(line, " ", 2)
100
                        if len(args) > 1 {
101
                                ext[args[0]] = args[1]
102
                        } else {
103
                                ext[args[0]] = ""
104
                        }
105
                }
106
        }
107
        if mechs, ok := ext["AUTH"]; ok {
108
                c.auth = strings.Split(mechs, " ")
109
        }
110
        c.ext = ext
111
        return err
112
}
113
 
114
// StartTLS sends the STARTTLS command and encrypts all further communication.
115
// Only servers that advertise the STARTTLS extension support this function.
116
func (c *Client) StartTLS(config *tls.Config) error {
117
        _, _, err := c.cmd(220, "STARTTLS")
118
        if err != nil {
119
                return err
120
        }
121
        c.conn = tls.Client(c.conn, config)
122
        c.Text = textproto.NewConn(c.conn)
123
        c.tls = true
124
        return c.ehlo()
125
}
126
 
127
// Verify checks the validity of an email address on the server.
128
// If Verify returns nil, the address is valid. A non-nil return
129
// does not necessarily indicate an invalid address. Many servers
130
// will not verify addresses for security reasons.
131
func (c *Client) Verify(addr string) error {
132
        _, _, err := c.cmd(250, "VRFY %s", addr)
133
        return err
134
}
135
 
136
// Auth authenticates a client using the provided authentication mechanism.
137
// A failed authentication closes the connection.
138
// Only servers that advertise the AUTH extension support this function.
139
func (c *Client) Auth(a Auth) error {
140
        encoding := base64.StdEncoding
141
        mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
142
        if err != nil {
143
                c.Quit()
144
                return err
145
        }
146
        resp64 := make([]byte, encoding.EncodedLen(len(resp)))
147
        encoding.Encode(resp64, resp)
148
        code, msg64, err := c.cmd(0, "AUTH %s %s", mech, resp64)
149
        for err == nil {
150
                var msg []byte
151
                switch code {
152
                case 334:
153
                        msg, err = encoding.DecodeString(msg64)
154
                case 235:
155
                        // the last message isn't base64 because it isn't a challenge
156
                        msg = []byte(msg64)
157
                default:
158
                        err = &textproto.Error{Code: code, Msg: msg64}
159
                }
160
                resp, err = a.Next(msg, code == 334)
161
                if err != nil {
162
                        // abort the AUTH
163
                        c.cmd(501, "*")
164
                        c.Quit()
165
                        break
166
                }
167
                if resp == nil {
168
                        break
169
                }
170
                resp64 = make([]byte, encoding.EncodedLen(len(resp)))
171
                encoding.Encode(resp64, resp)
172
                code, msg64, err = c.cmd(0, string(resp64))
173
        }
174
        return err
175
}
176
 
177
// Mail issues a MAIL command to the server using the provided email address.
178
// If the server supports the 8BITMIME extension, Mail adds the BODY=8BITMIME
179
// parameter.
180
// This initiates a mail transaction and is followed by one or more Rcpt calls.
181
func (c *Client) Mail(from string) error {
182
        cmdStr := "MAIL FROM:<%s>"
183
        if c.ext != nil {
184
                if _, ok := c.ext["8BITMIME"]; ok {
185
                        cmdStr += " BODY=8BITMIME"
186
                }
187
        }
188
        _, _, err := c.cmd(250, cmdStr, from)
189
        return err
190
}
191
 
192
// Rcpt issues a RCPT command to the server using the provided email address.
193
// A call to Rcpt must be preceded by a call to Mail and may be followed by
194
// a Data call or another Rcpt call.
195
func (c *Client) Rcpt(to string) error {
196
        _, _, err := c.cmd(25, "RCPT TO:<%s>", to)
197
        return err
198
}
199
 
200
type dataCloser struct {
201
        c *Client
202
        io.WriteCloser
203
}
204
 
205
func (d *dataCloser) Close() error {
206
        d.WriteCloser.Close()
207
        _, _, err := d.c.Text.ReadResponse(250)
208
        return err
209
}
210
 
211
// Data issues a DATA command to the server and returns a writer that
212
// can be used to write the data. The caller should close the writer
213
// before calling any more methods on c.
214
// A call to Data must be preceded by one or more calls to Rcpt.
215
func (c *Client) Data() (io.WriteCloser, error) {
216
        _, _, err := c.cmd(354, "DATA")
217
        if err != nil {
218
                return nil, err
219
        }
220
        return &dataCloser{c, c.Text.DotWriter()}, nil
221
}
222
 
223
// SendMail connects to the server at addr, switches to TLS if possible,
224
// authenticates with mechanism a if possible, and then sends an email from
225
// address from, to addresses to, with message msg.
226
func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
227
        c, err := Dial(addr)
228
        if err != nil {
229
                return err
230
        }
231
        if ok, _ := c.Extension("STARTTLS"); ok {
232
                if err = c.StartTLS(nil); err != nil {
233
                        return err
234
                }
235
        }
236
        if a != nil && c.ext != nil {
237
                if _, ok := c.ext["AUTH"]; ok {
238
                        if err = c.Auth(a); err != nil {
239
                                return err
240
                        }
241
                }
242
        }
243
        if err = c.Mail(from); err != nil {
244
                return err
245
        }
246
        for _, addr := range to {
247
                if err = c.Rcpt(addr); err != nil {
248
                        return err
249
                }
250
        }
251
        w, err := c.Data()
252
        if err != nil {
253
                return err
254
        }
255
        _, err = w.Write(msg)
256
        if err != nil {
257
                return err
258
        }
259
        err = w.Close()
260
        if err != nil {
261
                return err
262
        }
263
        return c.Quit()
264
}
265
 
266
// Extension reports whether an extension is support by the server.
267
// The extension name is case-insensitive. If the extension is supported,
268
// Extension also returns a string that contains any parameters the
269
// server specifies for the extension.
270
func (c *Client) Extension(ext string) (bool, string) {
271
        if c.ext == nil {
272
                return false, ""
273
        }
274
        ext = strings.ToUpper(ext)
275
        param, ok := c.ext[ext]
276
        return ok, param
277
}
278
 
279
// Reset sends the RSET command to the server, aborting the current mail
280
// transaction.
281
func (c *Client) Reset() error {
282
        _, _, err := c.cmd(250, "RSET")
283
        return err
284
}
285
 
286
// Quit sends the QUIT command and closes the connection to the server.
287
func (c *Client) Quit() error {
288
        _, _, err := c.cmd(221, "QUIT")
289
        if err != nil {
290
                return err
291
        }
292
        return c.Text.Close()
293
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.