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
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
72 func pasvToAddr(line string) (string, error) {
73
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
81 pasvData := strings.Split(line[start+1:end], ",")
82 if len(pasvData) < 6 {
83 return "", errors.New("invalid PASV response format")
84 }
85
86
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
98 port := portPart1*256 + portPart2
99
100
101 host := strings.Join(pasvData[0:4], ".")
102 return net.JoinHostPort(host, strconv.Itoa(port)), nil
103 }
104
105
106 func FTPDownload(target string) (bytes.Buffer, error) {
107 var buf bytes.Buffer
108 var err error
109
110
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
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
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
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
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
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
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
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
213 io.Copy(&buf, dataConn)
214
215
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