]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ftp.cc
assign store.c to debugging section 20
[thirdparty/squid.git] / src / ftp.cc
CommitLineData
6fe6313d 1/* $Id: ftp.cc,v 1.14 1996/03/28 20:42:46 wessels Exp $ */
44a47c6e 2
3#include "squid.h"
090089c4 4
5#define FTP_DELETE_GAP (64*1024)
1a6e21ac 6#define READBUFSIZ 4096
7#define MAGIC_MARKER "\004\004\004" /* No doubt this should be more configurable */
8#define MAGIC_MARKER_SZ 3
090089c4 9
10static char ftpASCII[] = "A";
11static char ftpBinary[] = "I";
12
13typedef struct _Ftpdata {
14 StoreEntry *entry;
090089c4 15 char type_id;
77a30ebb 16 char host[SQUIDHOSTNAMELEN + 1];
090089c4 17 char request[MAX_URL];
77a30ebb 18 char user[MAX_URL];
19 char password[MAX_URL];
090089c4 20 char *type;
21 char *mime_hdr;
090089c4 22 int ftp_fd;
77a30ebb 23 char *icp_page_ptr; /* Used to send proxy-http request:
24 * put_free_8k_page(me) if the lifetime
25 * expires */
26 char *icp_rwd_ptr; /* When a lifetime expires during the
27 * middle of an icpwrite, don't lose the
28 * icpReadWriteData */
1a6e21ac 29 int got_marker; /* denotes end of successful request */
090089c4 30} FtpData;
31
090089c4 32/* XXX: this does not support FTP on a different port! */
77a30ebb 33int ftp_url_parser(url, data)
090089c4 34 char *url;
77a30ebb 35 FtpData *data;
090089c4 36{
37 static char atypebuf[MAX_URL];
38 static char hostbuf[MAX_URL];
39 char *tmp = NULL;
40 int t;
77a30ebb 41 char *host = data->host;
42 char *request = data->request;
43 char *user = data->user;
44 char *password = data->password;
090089c4 45
46 /* initialize everything */
47 atypebuf[0] = hostbuf[0] = '\0';
48 request[0] = host[0] = user[0] = password[0] = '\0';
49
50 t = sscanf(url, "%[a-zA-Z]://%[^/]%s", atypebuf, hostbuf, request);
51 if ((t < 2) ||
52 !(!strcasecmp(atypebuf, "ftp") || !strcasecmp(atypebuf, "file"))) {
53 return -1;
54 } else if (t == 2) { /* no request */
55 strcpy(request, "/");
56 } else {
57 tmp = url_convert_hex(request); /* convert %xx to char */
58 strncpy(request, tmp, MAX_URL);
59 safe_free(tmp);
60 }
61
62 /* url address format is something like this:
63 * [ userid [ : password ] @ ] host
64 * or possibly even
65 * [ [ userid ] [ : [ password ] ] @ ] host
66 *
67 * So we must try to make sense of it. */
68
69 /* XXX: this only support [user:passwd@]host */
70 t = sscanf(hostbuf, "%[^:]:%[^@]@%s", user, password, host);
71 if (t < 3) {
72 strcpy(host, user); /* no login/passwd information */
73 strcpy(user, "anonymous");
74 strcpy(password, "harvest@");
75 }
76 /* we need to convert user and password for URL encodings */
77 tmp = url_convert_hex(user);
78 strcpy(user, tmp);
79 safe_free(tmp);
80
81 tmp = url_convert_hex(password);
82 strcpy(password, tmp);
83 safe_free(tmp);
84
85 return 0;
86}
87
88int ftpCachable(url, type, mime_hdr)
89 char *url;
90 char *type;
91 char *mime_hdr;
92{
93 stoplist *p = NULL;
94
95 /* scan stop list */
96 p = ftp_stoplist;
97 while (p) {
98 if (strstr(url, p->key))
99 return 0;
100 p = p->next;
101 }
102
103 /* else cachable */
104 return 1;
105}
106
107/* This will be called when socket lifetime is expired. */
108void ftpLifetimeExpire(fd, data)
109 int fd;
110 FtpData *data;
111{
112 StoreEntry *entry = NULL;
113 entry = data->entry;
12b9e9b1 114 debug(0, 4, "ftpLifeTimeExpire: FD %d: <URL:%s>\n", fd, entry->url);
77a30ebb 115 if (data->icp_page_ptr) {
116 put_free_8k_page(data->icp_page_ptr);
117 data->icp_page_ptr = NULL;
118 }
119 safe_free(data->icp_rwd_ptr);
b367f261 120 cached_error_entry(entry, ERR_LIFETIME_EXP, NULL);
090089c4 121 comm_close(fd);
090089c4 122 safe_free(data);
123}
124
125
126
127/* This will be called when data is ready to be read from fd. Read until
128 * error or connection closed. */
129int ftpReadReply(fd, data)
130 int fd;
131 FtpData *data;
132{
1a6e21ac 133 static char buf[READBUFSIZ];
090089c4 134 int len;
135 int clen;
136 int off;
137 StoreEntry *entry = NULL;
138
139 entry = data->entry;
140 if (entry->flag & DELETE_BEHIND) {
141 if (storeClientWaiting(entry)) {
142 /* check if we want to defer reading */
22e4fa85 143 clen = entry->mem_obj->e_current_len;
144 off = entry->mem_obj->e_lowest_offset;
090089c4 145 if ((clen - off) > FTP_DELETE_GAP) {
12b9e9b1 146 debug(0, 3, "ftpReadReply: Read deferred for Object: %s\n", entry->key);
147 debug(0, 3, "--> Current Gap: %d bytes\n", clen - off);
090089c4 148 /* reschedule, so it will automatically be reactivated when
149 * Gap is big enough. */
150 comm_set_select_handler(fd,
151 COMM_SELECT_READ,
152 (PF) ftpReadReply,
153 (caddr_t) data);
45cd3339 154 comm_set_stall(fd, getStallDelay()); /* dont try reading again for a while */
090089c4 155 return 0;
156 }
157 } else {
158 /* we can terminate connection right now */
b367f261 159 cached_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL);
090089c4 160 comm_close(fd);
090089c4 161 safe_free(data);
162 return 0;
163 }
164 }
1a6e21ac 165 errno = 0;
166 len = read(fd, buf, READBUFSIZ);
12b9e9b1 167 debug(0, 5, "ftpReadReply: FD %d, Read %d bytes\n", fd, len);
090089c4 168
22e4fa85 169 if (len < 0 || ((len == 0) && (entry->mem_obj->e_current_len == 0))) {
090089c4 170 if (len < 0)
12b9e9b1 171 debug(0, 1, "ftpReadReply: read error: %s\n", xstrerror());
6fe6313d 172 if (errno == ECONNRESET) {
173 /* Connection reset by peer */
174 /* consider it as a EOF */
175 if (!(entry->flag & DELETE_BEHIND))
176 entry->expires = cached_curtime + ttlSet(entry);
177 sprintf(tmp_error_buf, "\nWarning: The Remote Server sent RESET at the end of transmission.\n");
178 storeAppend(entry, tmp_error_buf, strlen(tmp_error_buf));
179 storeComplete(entry);
180 comm_close(fd);
181 safe_free(data);
182 } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
183 /* reinstall handlers */
184 /* XXX This may loop forever */
185 comm_set_select_handler(fd, COMM_SELECT_READ,
186 (PF) ftpReadReply, (caddr_t) data);
187 /* note there is no ftpReadReplyTimeout. Timeouts are handled
188 * by `ftpget'. */
189 } else {
190 cached_error_entry(entry, ERR_READ_ERROR, xstrerror());
191 comm_close(fd);
192 safe_free(data);
193 }
090089c4 194 } else if (len == 0) {
195 /* Connection closed; retrieval done. */
1a6e21ac 196 if (!data->got_marker) {
197 /* If we didn't see the magic marker, assume the transfer failed and arrange
198 * so the object gets ejected and never gets to disk. */
12b9e9b1 199 debug(0, 1, "ftpReadReply: Didn't see magic marker, purging <URL:%s>.\n", entry->url);
090089c4 200 entry->expires = cached_curtime + getNegativeTTL();
201 BIT_RESET(entry->flag, CACHABLE);
202 BIT_SET(entry->flag, RELEASE_REQUEST);
1a6e21ac 203 } else if (!(entry->flag & DELETE_BEHIND)) {
090089c4 204 entry->expires = cached_curtime + ttlSet(entry);
205 }
206 /* update fdstat and fdtable */
207 comm_close(fd);
208 storeComplete(entry);
209 safe_free(data);
22e4fa85 210 } else if (((entry->mem_obj->e_current_len + len) > getFtpMax()) &&
090089c4 211 !(entry->flag & DELETE_BEHIND)) {
212 /* accept data, but start to delete behind it */
213 storeStartDeleteBehind(entry);
090089c4 214 storeAppend(entry, buf, len);
215 comm_set_select_handler(fd,
216 COMM_SELECT_READ,
217 (PF) ftpReadReply,
218 (caddr_t) data);
090089c4 219 } else if (entry->flag & CLIENT_ABORT_REQUEST) {
220 /* append the last bit of info we get */
221 storeAppend(entry, buf, len);
b367f261 222 cached_error_entry(entry, ERR_CLIENT_ABORT, NULL);
090089c4 223 comm_close(fd);
090089c4 224 safe_free(data);
225 } else {
1a6e21ac 226 /* check for a magic marker at the end of the read */
227 if (len >= MAGIC_MARKER_SZ) {
228 if (!memcmp(MAGIC_MARKER, buf + len - MAGIC_MARKER_SZ, MAGIC_MARKER_SZ)) {
229 data->got_marker = 1;
230 len -= MAGIC_MARKER_SZ;
231 }
232 }
090089c4 233 storeAppend(entry, buf, len);
234 comm_set_select_handler(fd,
235 COMM_SELECT_READ,
236 (PF) ftpReadReply,
237 (caddr_t) data);
238 comm_set_select_handler_plus_timeout(fd,
239 COMM_SELECT_TIMEOUT,
240 (PF) ftpLifetimeExpire,
241 (caddr_t) data,
242 getReadTimeout());
243 }
244 return 0;
245}
246
77a30ebb 247void ftpSendComplete(fd, buf, size, errflag, data)
248 int fd;
249 char *buf;
250 int size;
251 int errflag;
252 FtpData *data;
253{
254 StoreEntry *entry = NULL;
255
256 entry = data->entry;
12b9e9b1 257 debug(0, 5, "ftpSendComplete: FD %d: size %d: errflag %d.\n",
77a30ebb 258 fd, size, errflag);
259
260 if (buf) {
261 put_free_8k_page(buf); /* Allocated by ftpSendRequest. */
262 buf = NULL;
263 }
264 data->icp_page_ptr = NULL; /* So lifetime expire doesn't re-free */
265 data->icp_rwd_ptr = NULL; /* Don't double free in lifetimeexpire */
266
267 if (errflag) {
b367f261 268 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
77a30ebb 269 comm_close(fd);
270 safe_free(data);
271 return;
272 } else {
273 comm_set_select_handler(data->ftp_fd,
274 COMM_SELECT_READ,
275 (PF) ftpReadReply,
276 (caddr_t) data);
277 comm_set_select_handler_plus_timeout(data->ftp_fd,
278 COMM_SELECT_TIMEOUT,
279 (PF) ftpLifetimeExpire,
280 (caddr_t) data, getReadTimeout());
281 }
282}
283
284void ftpSendRequest(fd, data)
285 int fd;
286 FtpData *data;
287{
288 char *ext = NULL;
289 ext_table_entry *e = NULL;
290 int l;
291 char *path = NULL;
292 char *mode = NULL;
293 char *buf = NULL;
294 static char tbuf[BUFSIZ];
295 static char opts[BUFSIZ];
296 static char *space = " ";
77a30ebb 297 char *s = NULL;
298 int got_timeout = 0;
299 int got_negttl = 0;
300 int buflen;
301
12b9e9b1 302 debug(0, 5, "ftpSendRequest: FD %d\n", fd);
77a30ebb 303
304 buflen = strlen(data->request) + 256;
305 buf = (char *) get_free_8k_page();
306 data->icp_page_ptr = buf;
307 memset(buf, '\0', buflen);
308
309 path = data->request;
310 l = strlen(path);
311 if (path[l - 1] == '/')
312 mode = ftpASCII;
313 else {
314 if ((ext = strrchr(path, '.')) != NULL) {
315 ext++;
316 mode = ((e = mime_ext_to_type(ext)) &&
317 strncmp(e->mime_type, "text", 4) == 0) ? ftpASCII :
318 ftpBinary;
319 } else
320 mode = ftpBinary;
321 }
322
323 /* Remove leading slash from FTP url-path so that we can
324 * handle ftp://user:pw@host/path objects where path and /path
325 * are quite different. -DW */
326 if (!strcmp(path, "/"))
327 *path = '.';
328 if (*path == '/')
329 path++;
330
331 /* Start building the buffer ... */
332
333 strcat(buf, getFtpProgram());
334 strcat(buf, space);
335
336 strncpy(opts, getFtpOptions(), BUFSIZ);
337 for (s = strtok(opts, w_space); s; s = strtok(NULL, w_space)) {
338 strcat(buf, s);
339 strcat(buf, space);
340 if (!strncmp(s, "-t", 2))
341 got_timeout = 1;
342 if (!strncmp(s, "-n", 2))
343 got_negttl = 1;
344 }
345 if (!got_timeout) {
346 sprintf(tbuf, "-t %d ", getReadTimeout());
347 strcat(buf, tbuf);
348 }
349 if (!got_negttl) {
350 sprintf(tbuf, "-n %d ", getNegativeTTL());
351 strcat(buf, tbuf);
352 }
353 strcat(buf, "-h "); /* httpify */
354 strcat(buf, "- "); /* stdout */
355 strcat(buf, data->host);
356 strcat(buf, space);
357 strcat(buf, path);
358 strcat(buf, space);
359 strcat(buf, mode); /* A or I */
360 strcat(buf, space);
361 strcat(buf, data->user);
362 strcat(buf, space);
363 strcat(buf, data->password);
364 strcat(buf, space);
12b9e9b1 365 debug(0, 5, "ftpSendRequest: FD %d: buf '%s'\n", fd, buf);
44a47c6e 366 data->icp_rwd_ptr = icpWrite(fd, buf, strlen(buf), 30, ftpSendComplete, (caddr_t) data);
77a30ebb 367}
368
369void ftpConnInProgress(fd, data)
370 int fd;
371 FtpData *data;
372{
373 StoreEntry *entry = data->entry;
374
12b9e9b1 375 debug(0, 5, "ftpConnInProgress: FD %d\n", fd);
77a30ebb 376
377 if (comm_connect(fd, "localhost", 3131) != COMM_OK)
378 switch (errno) {
379 case EINPROGRESS:
380 case EALREADY:
381 /* schedule this handler again */
382 comm_set_select_handler(fd,
383 COMM_SELECT_WRITE,
384 (PF) ftpConnInProgress,
385 (caddr_t) data);
386 return;
387 case EISCONN:
12b9e9b1 388 debug(0, 5, "ftpConnInProgress: FD %d is now connected.", fd);
77a30ebb 389 break; /* cool, we're connected */
390 default:
391 comm_close(fd);
b367f261 392 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
77a30ebb 393 safe_free(data);
394 return;
395 }
396 /* Call the real write handler, now that we're fully connected */
397 comm_set_select_handler(fd,
398 COMM_SELECT_WRITE,
399 (PF) ftpSendRequest,
400 (caddr_t) data);
401}
402
403
090089c4 404int ftpStart(unusedfd, url, entry)
405 int unusedfd;
406 char *url;
407 StoreEntry *entry;
408{
090089c4 409 FtpData *data = NULL;
77a30ebb 410 int status;
090089c4 411
12b9e9b1 412 debug(0, 3, "FtpStart: FD %d <URL:%s>\n", unusedfd, url);
090089c4 413
414 data = (FtpData *) xcalloc(1, sizeof(FtpData));
415 data->entry = entry;
416
417 /* Parse url. */
77a30ebb 418 if (ftp_url_parser(url, data)) {
b367f261 419 cached_error_entry(entry, ERR_INVALID_URL, NULL);
090089c4 420 safe_free(data);
421 return COMM_ERROR;
422 }
12b9e9b1 423 debug(0, 5, "FtpStart: FD %d, host=%s, request=%s, user=%s, passwd=%s\n",
77a30ebb 424 unusedfd, data->host, data->request, data->user, data->password);
425
426 data->ftp_fd = comm_open(COMM_NONBLOCKING, 0, 0, url);
427 if (data->ftp_fd == COMM_ERROR) {
b367f261 428 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
090089c4 429 safe_free(data);
430 return COMM_ERROR;
431 }
77a30ebb 432 /* Pipe/socket created ok */
090089c4 433
77a30ebb 434 /* Now connect ... */
435 if ((status = comm_connect(data->ftp_fd, "localhost", 3131))) {
436 if (status != EINPROGRESS) {
437 comm_close(data->ftp_fd);
b367f261 438 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
77a30ebb 439 safe_free(data);
440 return COMM_ERROR;
441 } else {
12b9e9b1 442 debug(0, 5, "ftpStart: FD %d: EINPROGRESS.\n", data->ftp_fd);
77a30ebb 443 comm_set_select_handler(data->ftp_fd, COMM_SELECT_LIFETIME,
444 (PF) ftpLifetimeExpire, (caddr_t) data);
445 comm_set_select_handler(data->ftp_fd, COMM_SELECT_WRITE,
446 (PF) ftpConnInProgress, (caddr_t) data);
447 return COMM_OK;
448 }
449 }
450 fdstat_open(data->ftp_fd, Socket);
090089c4 451 commSetNonBlocking(data->ftp_fd);
452 (void) fd_note(data->ftp_fd, entry->url);
453
454 /* Install connection complete handler. */
455 fd_note(data->ftp_fd, entry->url);
090089c4 456 comm_set_select_handler(data->ftp_fd,
77a30ebb 457 COMM_SELECT_WRITE,
458 (PF) ftpSendRequest,
090089c4 459 (caddr_t) data);
77a30ebb 460 comm_set_fd_lifetime(data->ftp_fd,
461 getClientLifetime());
090089c4 462 comm_set_select_handler(data->ftp_fd,
77a30ebb 463 COMM_SELECT_LIFETIME,
090089c4 464 (PF) ftpLifetimeExpire,
77a30ebb 465 (caddr_t) data);
466
090089c4 467 return COMM_OK;
468}
469
77a30ebb 470int ftpInitialize()
471{
472 int pid;
473 int fd;
474 int p[2];
475 static char pbuf[128];
476 char *ftpget = getFtpProgram();
477
478 if (pipe(p) < 0) {
12b9e9b1 479 debug(0, 0, "ftpInitialize: pipe: %s\n", xstrerror());
77a30ebb 480 return -1;
481 }
482 if ((pid = fork()) < 0) {
12b9e9b1 483 debug(0, 0, "ftpInitialize: fork: %s\n", xstrerror());
77a30ebb 484 return -1;
485 }
486 if (pid != 0) { /* parent */
487 close(p[0]);
488 fdstat_open(p[1], Pipe);
489 fd_note(p[1], "ftpget -S");
490 fcntl(p[1], F_SETFD, 1); /* set close-on-exec */
491 return 0;
492 }
493 /* child */
494 close(0);
495 dup(p[0]);
496 close(p[0]);
497 close(p[1]);
498 /* inherit stdin,stdout,stderr */
499 for (fd = 3; fd < fdstat_biggest_fd(); fd++)
500 (void) close(fd);
501 sprintf(pbuf, "%d", 3131);
502 execlp(ftpget, ftpget, "-D26,1", "-S", pbuf, NULL);
12b9e9b1 503 debug(0, 0, "ftpInitialize: %s: %s\n", ftpget, xstrerror());
77a30ebb 504 _exit(1);
505 return (1); /* eliminate compiler warning */
506}