From f23c3ddfabaf776dc6aa7ff532f511764767fd4f Mon Sep 17 00:00:00 2001 From: Julien Laffaye Date: Sun, 19 May 2013 21:54:57 +0200 Subject: [PATCH] Added feature discovery as per RFC 2389. --- ftp.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/ftp.go b/ftp.go index 2c9725e..45b3356 100644 --- a/ftp.go +++ b/ftp.go @@ -23,8 +23,9 @@ const ( // ServerConn represents the connection to a remote FTP server. type ServerConn struct { - conn *textproto.Conn - host string + conn *textproto.Conn + host string + features map[string]string } // Entry describes a file and is returned by List(). @@ -34,7 +35,7 @@ type Entry struct { Size uint64 } -// +// response represent a data-connection type response struct { conn net.Conn c *ServerConn @@ -51,7 +52,11 @@ func Connect(addr string) (*ServerConn, error) { } a := strings.SplitN(addr, ":", 2) - c := &ServerConn{conn, a[0]} + c := &ServerConn{ + conn: conn, + host: a[0], + features: make(map[string]string), + } _, _, err = c.conn.ReadCodeLine(StatusReady) if err != nil { @@ -59,6 +64,12 @@ func Connect(addr string) (*ServerConn, error) { return nil, err } + err = c.feat() + if err != nil { + c.Quit() + return nil, err + } + return c, nil } @@ -86,6 +97,43 @@ func (c *ServerConn) Login(user, password string) error { return nil } +// feat issues a FEAT FTP command to list the additional commands supported by +// the remote FTP server. +// FEAT is described in RFC 2389 +func (c *ServerConn) feat() error { + code, message, err := c.cmd(-1, "FEAT") + if err != nil { + return err + } + + if code != StatusSystem { + // The server does not support the FEAT command. This is not an + // error: we consider that they are no additional features. + return nil + } + + lines := strings.Split(message, "\n") + for _, line := range lines { + if !strings.HasPrefix(line, " ") { + continue + } + + line = strings.Trim(line, " ") + featureElements := strings.SplitN(line, " ", 2) + + command := featureElements[0] + + var commandDesc string + if len(featureElements) == 2 { + commandDesc = featureElements[1] + } + + c.features[command] = commandDesc + } + + return nil +} + // epsv issues an "EPSV" command to get a port number for a data connection. func (c *ServerConn) epsv() (port int, err error) { _, line, err := c.cmd(StatusExtendedPassiveMode, "EPSV")