Deleted Added
full compact
ftp.c (40939) ftp.c (40975)
1/*-
2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
1/*-
2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id: ftp.c,v 1.5 1998/08/17 09:30:19 des Exp $
28 * $Id: ftp.c,v 1.6 1998/11/05 19:48:17 des Exp $
29 */
30
31/*
32 * Portions of this code were taken from or based on ftpio.c:
33 *
34 * ----------------------------------------------------------------------------
35 * "THE BEER-WARE LICENSE" (Revision 42):
36 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
37 * can do whatever you want with this stuff. If we meet some day, and you think
38 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
39 * ----------------------------------------------------------------------------
40 *
41 * Major Changelog:
42 *
43 * Dag-Erling Co�dan Sm�rgrav
44 * 9 Jun 1998
45 *
46 * Incorporated into libfetch
47 *
48 * Jordan K. Hubbard
49 * 17 Jan 1996
50 *
51 * Turned inside out. Now returns xfers as new file ids, not as a special
52 * `state' of FTP_t
53 *
54 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
55 *
56 */
57
58#include <sys/types.h>
59#include <sys/socket.h>
60#include <netinet/in.h>
61#include <sys/errno.h>
62
63#include <ctype.h>
64#include <errno.h>
65#include <netdb.h>
66#include <stdarg.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71
72#include "fetch.h"
73#include "common.h"
29 */
30
31/*
32 * Portions of this code were taken from or based on ftpio.c:
33 *
34 * ----------------------------------------------------------------------------
35 * "THE BEER-WARE LICENSE" (Revision 42):
36 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
37 * can do whatever you want with this stuff. If we meet some day, and you think
38 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
39 * ----------------------------------------------------------------------------
40 *
41 * Major Changelog:
42 *
43 * Dag-Erling Co�dan Sm�rgrav
44 * 9 Jun 1998
45 *
46 * Incorporated into libfetch
47 *
48 * Jordan K. Hubbard
49 * 17 Jan 1996
50 *
51 * Turned inside out. Now returns xfers as new file ids, not as a special
52 * `state' of FTP_t
53 *
54 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
55 *
56 */
57
58#include <sys/types.h>
59#include <sys/socket.h>
60#include <netinet/in.h>
61#include <sys/errno.h>
62
63#include <ctype.h>
64#include <errno.h>
65#include <netdb.h>
66#include <stdarg.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71
72#include "fetch.h"
73#include "common.h"
74#include "ftperr.c"
74#include "ftperr.inc"
75
76#define FTP_DEFAULT_TO_ANONYMOUS
77#define FTP_ANONYMOUS_USER "ftp"
78#define FTP_ANONYMOUS_PASSWORD "ftp"
79#define FTP_DEFAULT_PORT 21
80
81#define FTP_OPEN_DATA_CONNECTION 150
82#define FTP_OK 200
83#define FTP_PASSIVE_MODE 227
84#define FTP_LOGGED_IN 230
85#define FTP_FILE_ACTION_OK 250
86#define FTP_NEED_PASSWORD 331
87#define FTP_NEED_ACCOUNT 332
88
89#define ENDL "\r\n"
90
75
76#define FTP_DEFAULT_TO_ANONYMOUS
77#define FTP_ANONYMOUS_USER "ftp"
78#define FTP_ANONYMOUS_PASSWORD "ftp"
79#define FTP_DEFAULT_PORT 21
80
81#define FTP_OPEN_DATA_CONNECTION 150
82#define FTP_OK 200
83#define FTP_PASSIVE_MODE 227
84#define FTP_LOGGED_IN 230
85#define FTP_FILE_ACTION_OK 250
86#define FTP_NEED_PASSWORD 331
87#define FTP_NEED_ACCOUNT 332
88
89#define ENDL "\r\n"
90
91static url_t cached_host;
91static struct url cached_host;
92static FILE *cached_socket;
93
94static char *_ftp_last_reply;
95
96/*
97 * Get server response, check that first digit is a '2'
98 */
99static int
100_ftp_chkerr(FILE *s, int *e)
101{
102 char *line;
103 size_t len;
92static FILE *cached_socket;
93
94static char *_ftp_last_reply;
95
96/*
97 * Get server response, check that first digit is a '2'
98 */
99static int
100_ftp_chkerr(FILE *s, int *e)
101{
102 char *line;
103 size_t len;
104 int err;
104
105 if (e)
106 *e = 0;
107
108 do {
109 if (((line = fgetln(s, &len)) == NULL) || (len < 4)) {
110 _fetch_syserr();
111 return -1;
112 }
113 } while (line[3] == '-');
114
115 _ftp_last_reply = line;
116
117#ifndef NDEBUG
118 fprintf(stderr, "\033[1m<<< ");
119 fprintf(stderr, "%*.*s", (int)len, (int)len, line);
120 fprintf(stderr, "\033[m");
121#endif
122
123 if (!isdigit(line[1]) || !isdigit(line[1])
124 || !isdigit(line[2]) || (line[3] != ' ')) {
105
106 if (e)
107 *e = 0;
108
109 do {
110 if (((line = fgetln(s, &len)) == NULL) || (len < 4)) {
111 _fetch_syserr();
112 return -1;
113 }
114 } while (line[3] == '-');
115
116 _ftp_last_reply = line;
117
118#ifndef NDEBUG
119 fprintf(stderr, "\033[1m<<< ");
120 fprintf(stderr, "%*.*s", (int)len, (int)len, line);
121 fprintf(stderr, "\033[m");
122#endif
123
124 if (!isdigit(line[1]) || !isdigit(line[1])
125 || !isdigit(line[2]) || (line[3] != ' ')) {
125 _ftp_seterr(-1);
126 _ftp_seterr(0);
126 return -1;
127 }
128
127 return -1;
128 }
129
129 _ftp_seterr((line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0'));
130 err = (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0');
131 _ftp_seterr(err);
130
131 if (e)
132
133 if (e)
132 *e = fetchLastErrCode;
134 *e = err;
133
134 return (line[0] == '2') - 1;
135}
136
137/*
138 * Send a command and check reply
139 */
140static int
141_ftp_cmd(FILE *f, char *fmt, ...)
142{
143 va_list ap;
144 int e;
145
146 va_start(ap, fmt);
147 vfprintf(f, fmt, ap);
148#ifndef NDEBUG
149 fprintf(stderr, "\033[1m>>> ");
150 vfprintf(stderr, fmt, ap);
151 fprintf(stderr, "\033[m");
152#endif
153 va_end(ap);
154
155 _ftp_chkerr(f, &e);
156 return e;
157}
158
159/*
160 * Transfer file
161 */
162static FILE *
163_ftp_transfer(FILE *cf, char *oper, char *file, char *mode, int pasv)
164{
165 struct sockaddr_in sin;
166 int sd = -1, l;
167 char *s;
168 FILE *df;
169
170 /* change directory */
171 if (((s = strrchr(file, '/')) != NULL) && (s != file)) {
172 *s = 0;
173 if (_ftp_cmd(cf, "CWD %s" ENDL, file) != FTP_FILE_ACTION_OK) {
174 *s = '/';
175 return NULL;
176 }
177 *s++ = '/';
178 } else {
179 if (_ftp_cmd(cf, "CWD /" ENDL) != FTP_FILE_ACTION_OK)
180 return NULL;
181 }
182
183 /* s now points to file name */
184
185 /* open data socket */
186 if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
187 _fetch_syserr();
188 return NULL;
189 }
190
191 if (pasv) {
192 u_char addr[6];
193 char *ln, *p;
194 int i;
195
196 /* send PASV command */
197 if (_ftp_cmd(cf, "PASV" ENDL) != FTP_PASSIVE_MODE)
198 goto ouch;
199
200 /* find address and port number. The reply to the PASV command
201 is IMHO the one and only weak point in the FTP protocol. */
202 ln = _ftp_last_reply;
203 for (p = ln + 3; !isdigit(*p); p++)
204 /* nothing */ ;
205 for (p--, i = 0; i < 6; i++) {
206 p++; /* skip the comma */
207 addr[i] = strtol(p, &p, 10);
208 }
209
210 /* construct sockaddr for data socket */
211 l = sizeof(sin);
212 if (getpeername(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
213 goto sysouch;
214 bcopy(addr, (char *)&sin.sin_addr, 4);
215 bcopy(addr + 4, (char *)&sin.sin_port, 2);
216
217 /* connect to data port */
218 if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
219 goto sysouch;
220
221 /* make the server initiate the transfer */
222 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
223 goto ouch;
224
225 } else {
226 u_int32_t a;
227 u_short p;
228 int d;
229
230 /* find our own address, bind, and listen */
231 l = sizeof(sin);
232 if (getsockname(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
233 goto sysouch;
234 sin.sin_port = 0;
235 if (bind(sd, (struct sockaddr *)&sin, l) == -1)
236 goto sysouch;
237 if (listen(sd, 1) == -1)
238 goto sysouch;
239
240 /* find what port we're on and tell the server */
241 if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
242 goto sysouch;
243 a = ntohl(sin.sin_addr.s_addr);
244 p = ntohs(sin.sin_port);
245 if (_ftp_cmd(cf, "PORT %d,%d,%d,%d,%d,%d" ENDL,
246 (a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff,
247 (p >> 8) & 0xff, p & 0xff) != FTP_OK)
248 goto ouch;
249
250 /* make the server initiate the transfer */
251 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
252 goto ouch;
253
254 /* accept the incoming connection and go to town */
255 if ((d = accept(sd, NULL, NULL)) == -1)
256 goto sysouch;
257 close(sd);
258 sd = d;
259 }
260
261 if ((df = fdopen(sd, mode)) == NULL)
262 goto sysouch;
263 return df;
264
265sysouch:
266 _fetch_syserr();
267ouch:
268 close(sd);
269 return NULL;
270}
271
272/*
273 * Log on to FTP server
274 */
275static FILE *
276_ftp_connect(char *host, int port, char *user, char *pwd)
277{
278 int sd, e, pp = FTP_DEFAULT_PORT;
279 char *p, *q;
280 FILE *f;
281
282 /* check for proxy */
283 if ((p = getenv("FTP_PROXY")) != NULL) {
284 if ((q = strchr(p, ':')) != NULL) {
285 /* XXX check that it's a valid number */
286 pp = atoi(q+1);
287 }
288 if (q)
289 *q = 0;
290 sd = fetchConnect(p, pp);
291 if (q)
292 *q = ':';
293 } else {
294 /* no proxy, go straight to target */
295 sd = fetchConnect(host, port);
296 }
297
298 /* check connection */
299 if (sd == -1) {
300 _fetch_syserr();
301 return NULL;
302 }
303
304 /* streams make life easier */
305 if ((f = fdopen(sd, "r+")) == NULL) {
306 _fetch_syserr();
307 goto ouch;
308 }
309
310 /* expect welcome message */
311 if (_ftp_chkerr(f, NULL) == -1)
312 goto fouch;
313
314 /* send user name and password */
315 if (!user || !*user)
316 user = FTP_ANONYMOUS_USER;
317 e = p ? _ftp_cmd(f, "USER %s@%s@%d" ENDL, user, host, port)
318 : _ftp_cmd(f, "USER %s" ENDL, user);
319
320 /* did the server request a password? */
321 if (e == FTP_NEED_PASSWORD) {
322 if (!pwd || !*pwd)
323 pwd = FTP_ANONYMOUS_PASSWORD;
324 e = _ftp_cmd(f, "PASS %s" ENDL, pwd);
325 }
326
327 /* did the server request an account? */
328 if (e == FTP_NEED_ACCOUNT)
329 /* help! */ ;
330
331 /* we should be done by now */
332 if (e != FTP_LOGGED_IN)
333 goto fouch;
334
335 /* might as well select mode and type at once */
336#ifdef FTP_FORCE_STREAM_MODE
337 if (_ftp_cmd(f, "MODE S" ENDL) != FTP_OK) /* default is S */
338 goto ouch;
339#endif
340 if (_ftp_cmd(f, "TYPE I" ENDL) != FTP_OK) /* default is A */
341 goto ouch;
342
343 /* done */
344 return f;
345
346ouch:
347 close(sd);
348 return NULL;
349fouch:
350 fclose(f);
351 return NULL;
352}
353
354/*
355 * Disconnect from server
356 */
357static void
358_ftp_disconnect(FILE *f)
359{
360 _ftp_cmd(f, "QUIT" ENDL);
361 fclose(f);
362}
363
364/*
365 * Check if we're already connected
366 */
367static int
135
136 return (line[0] == '2') - 1;
137}
138
139/*
140 * Send a command and check reply
141 */
142static int
143_ftp_cmd(FILE *f, char *fmt, ...)
144{
145 va_list ap;
146 int e;
147
148 va_start(ap, fmt);
149 vfprintf(f, fmt, ap);
150#ifndef NDEBUG
151 fprintf(stderr, "\033[1m>>> ");
152 vfprintf(stderr, fmt, ap);
153 fprintf(stderr, "\033[m");
154#endif
155 va_end(ap);
156
157 _ftp_chkerr(f, &e);
158 return e;
159}
160
161/*
162 * Transfer file
163 */
164static FILE *
165_ftp_transfer(FILE *cf, char *oper, char *file, char *mode, int pasv)
166{
167 struct sockaddr_in sin;
168 int sd = -1, l;
169 char *s;
170 FILE *df;
171
172 /* change directory */
173 if (((s = strrchr(file, '/')) != NULL) && (s != file)) {
174 *s = 0;
175 if (_ftp_cmd(cf, "CWD %s" ENDL, file) != FTP_FILE_ACTION_OK) {
176 *s = '/';
177 return NULL;
178 }
179 *s++ = '/';
180 } else {
181 if (_ftp_cmd(cf, "CWD /" ENDL) != FTP_FILE_ACTION_OK)
182 return NULL;
183 }
184
185 /* s now points to file name */
186
187 /* open data socket */
188 if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
189 _fetch_syserr();
190 return NULL;
191 }
192
193 if (pasv) {
194 u_char addr[6];
195 char *ln, *p;
196 int i;
197
198 /* send PASV command */
199 if (_ftp_cmd(cf, "PASV" ENDL) != FTP_PASSIVE_MODE)
200 goto ouch;
201
202 /* find address and port number. The reply to the PASV command
203 is IMHO the one and only weak point in the FTP protocol. */
204 ln = _ftp_last_reply;
205 for (p = ln + 3; !isdigit(*p); p++)
206 /* nothing */ ;
207 for (p--, i = 0; i < 6; i++) {
208 p++; /* skip the comma */
209 addr[i] = strtol(p, &p, 10);
210 }
211
212 /* construct sockaddr for data socket */
213 l = sizeof(sin);
214 if (getpeername(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
215 goto sysouch;
216 bcopy(addr, (char *)&sin.sin_addr, 4);
217 bcopy(addr + 4, (char *)&sin.sin_port, 2);
218
219 /* connect to data port */
220 if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
221 goto sysouch;
222
223 /* make the server initiate the transfer */
224 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
225 goto ouch;
226
227 } else {
228 u_int32_t a;
229 u_short p;
230 int d;
231
232 /* find our own address, bind, and listen */
233 l = sizeof(sin);
234 if (getsockname(fileno(cf), (struct sockaddr *)&sin, &l) == -1)
235 goto sysouch;
236 sin.sin_port = 0;
237 if (bind(sd, (struct sockaddr *)&sin, l) == -1)
238 goto sysouch;
239 if (listen(sd, 1) == -1)
240 goto sysouch;
241
242 /* find what port we're on and tell the server */
243 if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
244 goto sysouch;
245 a = ntohl(sin.sin_addr.s_addr);
246 p = ntohs(sin.sin_port);
247 if (_ftp_cmd(cf, "PORT %d,%d,%d,%d,%d,%d" ENDL,
248 (a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff,
249 (p >> 8) & 0xff, p & 0xff) != FTP_OK)
250 goto ouch;
251
252 /* make the server initiate the transfer */
253 if (_ftp_cmd(cf, "%s %s" ENDL, oper, s) != FTP_OPEN_DATA_CONNECTION)
254 goto ouch;
255
256 /* accept the incoming connection and go to town */
257 if ((d = accept(sd, NULL, NULL)) == -1)
258 goto sysouch;
259 close(sd);
260 sd = d;
261 }
262
263 if ((df = fdopen(sd, mode)) == NULL)
264 goto sysouch;
265 return df;
266
267sysouch:
268 _fetch_syserr();
269ouch:
270 close(sd);
271 return NULL;
272}
273
274/*
275 * Log on to FTP server
276 */
277static FILE *
278_ftp_connect(char *host, int port, char *user, char *pwd)
279{
280 int sd, e, pp = FTP_DEFAULT_PORT;
281 char *p, *q;
282 FILE *f;
283
284 /* check for proxy */
285 if ((p = getenv("FTP_PROXY")) != NULL) {
286 if ((q = strchr(p, ':')) != NULL) {
287 /* XXX check that it's a valid number */
288 pp = atoi(q+1);
289 }
290 if (q)
291 *q = 0;
292 sd = fetchConnect(p, pp);
293 if (q)
294 *q = ':';
295 } else {
296 /* no proxy, go straight to target */
297 sd = fetchConnect(host, port);
298 }
299
300 /* check connection */
301 if (sd == -1) {
302 _fetch_syserr();
303 return NULL;
304 }
305
306 /* streams make life easier */
307 if ((f = fdopen(sd, "r+")) == NULL) {
308 _fetch_syserr();
309 goto ouch;
310 }
311
312 /* expect welcome message */
313 if (_ftp_chkerr(f, NULL) == -1)
314 goto fouch;
315
316 /* send user name and password */
317 if (!user || !*user)
318 user = FTP_ANONYMOUS_USER;
319 e = p ? _ftp_cmd(f, "USER %s@%s@%d" ENDL, user, host, port)
320 : _ftp_cmd(f, "USER %s" ENDL, user);
321
322 /* did the server request a password? */
323 if (e == FTP_NEED_PASSWORD) {
324 if (!pwd || !*pwd)
325 pwd = FTP_ANONYMOUS_PASSWORD;
326 e = _ftp_cmd(f, "PASS %s" ENDL, pwd);
327 }
328
329 /* did the server request an account? */
330 if (e == FTP_NEED_ACCOUNT)
331 /* help! */ ;
332
333 /* we should be done by now */
334 if (e != FTP_LOGGED_IN)
335 goto fouch;
336
337 /* might as well select mode and type at once */
338#ifdef FTP_FORCE_STREAM_MODE
339 if (_ftp_cmd(f, "MODE S" ENDL) != FTP_OK) /* default is S */
340 goto ouch;
341#endif
342 if (_ftp_cmd(f, "TYPE I" ENDL) != FTP_OK) /* default is A */
343 goto ouch;
344
345 /* done */
346 return f;
347
348ouch:
349 close(sd);
350 return NULL;
351fouch:
352 fclose(f);
353 return NULL;
354}
355
356/*
357 * Disconnect from server
358 */
359static void
360_ftp_disconnect(FILE *f)
361{
362 _ftp_cmd(f, "QUIT" ENDL);
363 fclose(f);
364}
365
366/*
367 * Check if we're already connected
368 */
369static int
368_ftp_isconnected(url_t *url)
370_ftp_isconnected(struct url *url)
369{
370 return (cached_socket
371 && (strcmp(url->host, cached_host.host) == 0)
372 && (strcmp(url->user, cached_host.user) == 0)
373 && (strcmp(url->pwd, cached_host.pwd) == 0)
374 && (url->port == cached_host.port));
375}
376
377/*
378 * FTP session
379 */
380static FILE *
371{
372 return (cached_socket
373 && (strcmp(url->host, cached_host.host) == 0)
374 && (strcmp(url->user, cached_host.user) == 0)
375 && (strcmp(url->pwd, cached_host.pwd) == 0)
376 && (url->port == cached_host.port));
377}
378
379/*
380 * FTP session
381 */
382static FILE *
381fetchXxxFTP(url_t *url, char *oper, char *mode, char *flags)
383fetchXxxFTP(struct url *url, char *oper, char *mode, char *flags)
382{
383 FILE *cf = NULL;
384 int e;
385
386 /* set default port */
387 if (!url->port)
388 url->port = FTP_DEFAULT_PORT;
389
390 /* try to use previously cached connection; there should be a 226 waiting */
391 if (_ftp_isconnected(url)) {
392 _ftp_chkerr(cached_socket, &e);
393 if (e > 0)
394 cf = cached_socket;
395 }
396
397 /* connect to server */
398 if (!cf) {
399 cf = _ftp_connect(url->host, url->port, url->user, url->pwd);
400 if (!cf)
401 return NULL;
402 if (cached_socket)
403 _ftp_disconnect(cached_socket);
404 cached_socket = cf;
384{
385 FILE *cf = NULL;
386 int e;
387
388 /* set default port */
389 if (!url->port)
390 url->port = FTP_DEFAULT_PORT;
391
392 /* try to use previously cached connection; there should be a 226 waiting */
393 if (_ftp_isconnected(url)) {
394 _ftp_chkerr(cached_socket, &e);
395 if (e > 0)
396 cf = cached_socket;
397 }
398
399 /* connect to server */
400 if (!cf) {
401 cf = _ftp_connect(url->host, url->port, url->user, url->pwd);
402 if (!cf)
403 return NULL;
404 if (cached_socket)
405 _ftp_disconnect(cached_socket);
406 cached_socket = cf;
405 memcpy(&cached_host, url, sizeof(url_t));
407 memcpy(&cached_host, url, sizeof(struct url));
406 }
407
408 /* initiate the transfer */
409 return _ftp_transfer(cf, oper, url->doc, mode, (flags && strchr(flags, 'p')));
410}
411
412/*
413 * Itsy bitsy teeny weenie
414 */
415FILE *
408 }
409
410 /* initiate the transfer */
411 return _ftp_transfer(cf, oper, url->doc, mode, (flags && strchr(flags, 'p')));
412}
413
414/*
415 * Itsy bitsy teeny weenie
416 */
417FILE *
416fetchGetFTP(url_t *url, char *flags)
418fetchGetFTP(struct url *url, char *flags)
417{
418 return fetchXxxFTP(url, "RETR", "r", flags);
419}
420
421FILE *
419{
420 return fetchXxxFTP(url, "RETR", "r", flags);
421}
422
423FILE *
422fetchPutFTP(url_t *url, char *flags)
424fetchPutFTP(struct url *url, char *flags)
423{
424 if (flags && strchr(flags, 'a'))
425 return fetchXxxFTP(url, "APPE", "w", flags);
426 else return fetchXxxFTP(url, "STOR", "w", flags);
427}
425{
426 if (flags && strchr(flags, 'a'))
427 return fetchXxxFTP(url, "APPE", "w", flags);
428 else return fetchXxxFTP(url, "STOR", "w", flags);
429}
430
431extern void warnx(char *fmt, ...);
432int
433fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
434{
435 warnx("fetchStatFTP(): not implemented");
436 return -1;
437}
438