...

Source file src/github.com/thlib/go-timezone-local/tzdata/ftp.go

Documentation: github.com/thlib/go-timezone-local/tzdata

     1  package tzdata
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/url"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  )
    15  
    16  // FTP status codes, defined in RFC 959
    17  const (
    18  	StatusInitiating    = 100
    19  	StatusRestartMarker = 110
    20  	StatusReadyMinute   = 120
    21  	StatusAlreadyOpen   = 125
    22  	StatusAboutToSend   = 150
    23  
    24  	StatusCommandOK             = 200
    25  	StatusCommandNotImplemented = 202
    26  	StatusSystem                = 211
    27  	StatusDirectory             = 212
    28  	StatusFile                  = 213
    29  	StatusHelp                  = 214
    30  	StatusName                  = 215
    31  	StatusReady                 = 220
    32  	StatusClosing               = 221
    33  	StatusDataConnectionOpen    = 225
    34  	StatusClosingDataConnection = 226
    35  	StatusPassiveMode           = 227
    36  	StatusLongPassiveMode       = 228
    37  	StatusExtendedPassiveMode   = 229
    38  	StatusLoggedIn              = 230
    39  	StatusLoggedOut             = 231
    40  	StatusLogoutAck             = 232
    41  	StatusAuthOK                = 234
    42  	StatusRequestedFileActionOK = 250
    43  	StatusPathCreated           = 257
    44  
    45  	StatusUserOK             = 331
    46  	StatusLoginNeedAccount   = 332
    47  	StatusRequestFilePending = 350
    48  
    49  	StatusNotAvailable             = 421
    50  	StatusCanNotOpenDataConnection = 425
    51  	StatusTransfertAborted         = 426
    52  	StatusInvalidCredentials       = 430
    53  	StatusHostUnavailable          = 434
    54  	StatusFileActionIgnored        = 450
    55  	StatusActionAborted            = 451
    56  	Status452                      = 452
    57  
    58  	StatusBadCommand              = 500
    59  	StatusBadArguments            = 501
    60  	StatusNotImplemented          = 502
    61  	StatusBadSequence             = 503
    62  	StatusNotImplementedParameter = 504
    63  	StatusNotLoggedIn             = 530
    64  	StatusStorNeedAccount         = 532
    65  	StatusFileUnavailable         = 550
    66  	StatusPageTypeUnknown         = 551
    67  	StatusExceededStorage         = 552
    68  	StatusBadFileName             = 553
    69  )
    70  
    71  // pasv will parse the PASV response into an address
    72  func pasvToAddr(line string) (string, error) {
    73  	// PASV response format : 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
    74  	start := strings.Index(line, "(")
    75  	end := strings.LastIndex(line, ")")
    76  	if start == -1 || end == -1 {
    77  		return "", errors.New("invalid PASV response format")
    78  	}
    79  
    80  	// We have to split the response string
    81  	pasvData := strings.Split(line[start+1:end], ",")
    82  	if len(pasvData) < 6 {
    83  		return "", errors.New("invalid PASV response format")
    84  	}
    85  
    86  	// Let's compute the port number
    87  	portPart1, err := strconv.Atoi(pasvData[4])
    88  	if err != nil {
    89  		return "", err
    90  	}
    91  
    92  	portPart2, err := strconv.Atoi(pasvData[5])
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  
    97  	// Recompose port
    98  	port := portPart1*256 + portPart2
    99  
   100  	// Make the IP address to connect to
   101  	host := strings.Join(pasvData[0:4], ".")
   102  	return net.JoinHostPort(host, strconv.Itoa(port)), nil
   103  }
   104  
   105  // UpdateOldNames fetches the list of old tz names and returns a mapping
   106  func FTPDownload(target string) (bytes.Buffer, error) {
   107  	var buf bytes.Buffer
   108  	var err error
   109  
   110  	// Parse the url
   111  	u, err := url.Parse(target)
   112  	if err != nil {
   113  		return buf, err
   114  	}
   115  	port := u.Port()
   116  	if port == "" {
   117  		port = "21"
   118  	}
   119  	origin := net.JoinHostPort(u.Host, port)
   120  
   121  	// Connect to the command server
   122  	conn, err := net.DialTimeout("tcp", origin, 30*time.Second)
   123  	if err != nil {
   124  		return buf, err
   125  	}
   126  	defer conn.Close()
   127  	r := bufio.NewReader(conn)
   128  	if err != nil {
   129  		return buf, err
   130  	}
   131  	resp, err := r.ReadString('\n')
   132  	if err != nil {
   133  		return buf, err
   134  	}
   135  	if !strings.HasPrefix(resp, "220") {
   136  		return buf, fmt.Errorf("failed to connect: %v", resp)
   137  	}
   138  
   139  	// Send username
   140  	_, err = conn.Write([]byte("USER anonymous\n"))
   141  	if err != nil {
   142  		return buf, err
   143  	}
   144  	resp, err = r.ReadString('\n')
   145  	if err != nil {
   146  		return buf, err
   147  	}
   148  	if !strings.HasPrefix(resp, "331") {
   149  		return buf, fmt.Errorf("failed to login: %v", resp)
   150  	}
   151  
   152  	// Send password
   153  	_, err = conn.Write([]byte("PASS anonymous\n"))
   154  	if err != nil {
   155  		return buf, err
   156  	}
   157  	resp, err = r.ReadString('\n')
   158  	if err != nil {
   159  		return buf, err
   160  	}
   161  	if !strings.HasPrefix(resp, "230") {
   162  		return buf, fmt.Errorf("failed to login: %v", resp)
   163  	}
   164  
   165  	// Binary mode
   166  	_, err = conn.Write([]byte("TYPE I\n"))
   167  	if err != nil {
   168  		return buf, err
   169  	}
   170  	resp, err = r.ReadString('\n')
   171  	if err != nil {
   172  		return buf, err
   173  	}
   174  	if !strings.HasPrefix(resp, "200") {
   175  		return buf, fmt.Errorf("failed to switch to binary mode: %v", resp)
   176  	}
   177  
   178  	// Get the file transfer address
   179  	_, err = conn.Write([]byte("PASV\n"))
   180  	if err != nil {
   181  		return buf, err
   182  	}
   183  	resp, err = r.ReadString('\n')
   184  	if err != nil {
   185  		return buf, err
   186  	}
   187  	dataAddr, err := pasvToAddr(resp)
   188  	if err != nil {
   189  		return buf, err
   190  	}
   191  
   192  	// Connect to the data transfer address
   193  	dataConn, err := net.DialTimeout("tcp", dataAddr, 30*time.Second)
   194  	if err != nil {
   195  		return buf, err
   196  	}
   197  	defer dataConn.Close()
   198  
   199  	// Send the command to download the file through the data transfer connection
   200  	_, err = conn.Write([]byte(fmt.Sprintf("RETR %v\n", u.Path)))
   201  	if err != nil {
   202  		return buf, err
   203  	}
   204  	resp, err = r.ReadString('\n')
   205  	if err != nil {
   206  		return buf, err
   207  	}
   208  	if !strings.HasPrefix(resp, "150") {
   209  		return buf, fmt.Errorf("RETR failed: %v", resp)
   210  	}
   211  
   212  	// Get the file response
   213  	io.Copy(&buf, dataConn)
   214  
   215  	// Get the transfer complete response
   216  	resp, err = r.ReadString('\n')
   217  	if err != nil {
   218  		return buf, err
   219  	}
   220  	if !strings.HasPrefix(resp, "226") {
   221  		return buf, fmt.Errorf("transfer failed: %v", resp)
   222  	}
   223  
   224  	return buf, err
   225  }
   226  

View as plain text