]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ftp.cc
1 /* $Id: ftp.cc,v 1.18 1996/04/04 01:30:43 wessels Exp $ */
4 * DEBUG: Section 9 ftp: FTP
9 #define FTP_DELETE_GAP (64*1024)
10 #define READBUFSIZ 4096
11 #define MAGIC_MARKER "\004\004\004" /* No doubt this should be more configurable */
12 #define MAGIC_MARKER_SZ 3
14 static char ftpASCII
[] = "A";
15 static char ftpBinary
[] = "I";
17 typedef struct _Ftpdata
{
20 char host
[SQUIDHOSTNAMELEN
+ 1];
21 char request
[MAX_URL
];
23 char password
[MAX_URL
];
27 char *icp_page_ptr
; /* Used to send proxy-http request:
28 * put_free_8k_page(me) if the lifetime
30 char *icp_rwd_ptr
; /* When a lifetime expires during the
31 * middle of an icpwrite, don't lose the
33 int got_marker
; /* denotes end of successful request */
36 static void ftpCloseAndFree(fd
, data
)
45 /* XXX: this does not support FTP on a different port! */
46 int ftp_url_parser(url
, data
)
50 static char atypebuf
[MAX_URL
];
51 static char hostbuf
[MAX_URL
];
54 char *host
= data
->host
;
55 char *request
= data
->request
;
56 char *user
= data
->user
;
57 char *password
= data
->password
;
59 /* initialize everything */
60 atypebuf
[0] = hostbuf
[0] = '\0';
61 request
[0] = host
[0] = user
[0] = password
[0] = '\0';
63 t
= sscanf(url
, "%[a-zA-Z]://%[^/]%s", atypebuf
, hostbuf
, request
);
65 !(!strcasecmp(atypebuf
, "ftp") || !strcasecmp(atypebuf
, "file"))) {
67 } else if (t
== 2) { /* no request */
70 tmp
= url_convert_hex(request
); /* convert %xx to char */
71 strncpy(request
, tmp
, MAX_URL
);
75 /* url address format is something like this:
76 * [ userid [ : password ] @ ] host
78 * [ [ userid ] [ : [ password ] ] @ ] host
80 * So we must try to make sense of it. */
82 /* XXX: this only support [user:passwd@]host */
83 t
= sscanf(hostbuf
, "%[^:]:%[^@]@%s", user
, password
, host
);
85 strcpy(host
, user
); /* no login/passwd information */
86 strcpy(user
, "anonymous");
87 strcpy(password
, "harvest@");
89 /* we need to convert user and password for URL encodings */
90 tmp
= url_convert_hex(user
);
94 tmp
= url_convert_hex(password
);
95 strcpy(password
, tmp
);
109 if (strstr(url
, p
->key
))
118 /* This will be called when socket lifetime is expired. */
119 void ftpLifetimeExpire(fd
, data
)
123 StoreEntry
*entry
= NULL
;
125 debug(9, 4, "ftpLifeTimeExpire: FD %d: <URL:%s>\n", fd
, entry
->url
);
126 if (data
->icp_page_ptr
) {
127 put_free_8k_page(data
->icp_page_ptr
);
128 data
->icp_page_ptr
= NULL
;
130 safe_free(data
->icp_rwd_ptr
);
131 cached_error_entry(entry
, ERR_LIFETIME_EXP
, NULL
);
132 ftpCloseAndFree(fd
, data
);
137 /* This will be called when data is ready to be read from fd. Read until
138 * error or connection closed. */
139 int ftpReadReply(fd
, data
)
143 static char buf
[READBUFSIZ
];
147 StoreEntry
*entry
= NULL
;
150 if (entry
->flag
& DELETE_BEHIND
) {
151 if (storeClientWaiting(entry
)) {
152 /* check if we want to defer reading */
153 clen
= entry
->mem_obj
->e_current_len
;
154 off
= entry
->mem_obj
->e_lowest_offset
;
155 if ((clen
- off
) > FTP_DELETE_GAP
) {
156 debug(9, 3, "ftpReadReply: Read deferred for Object: %s\n",
158 debug(9, 3, "--> Current Gap: %d bytes\n", clen
- off
);
159 /* reschedule, so it will automatically be reactivated when
160 * Gap is big enough. */
161 comm_set_select_handler(fd
,
165 comm_set_stall(fd
, getStallDelay()); /* dont try reading again for a while */
169 /* we can terminate connection right now */
170 cached_error_entry(entry
, ERR_NO_CLIENTS_BIG_OBJ
, NULL
);
171 ftpCloseAndFree(fd
, data
);
176 len
= read(fd
, buf
, READBUFSIZ
);
177 debug(9, 5, "ftpReadReply: FD %d, Read %d bytes\n", fd
, len
);
180 debug(9, 1, "ftpReadReply: read error: %s\n", xstrerror());
181 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
182 /* reinstall handlers */
183 /* XXX This may loop forever */
184 comm_set_select_handler(fd
, COMM_SELECT_READ
,
185 (PF
) ftpReadReply
, (caddr_t
) data
);
186 /* note there is no ftpReadReplyTimeout. Timeouts are handled
189 BIT_RESET(entry
->flag
, CACHABLE
);
190 BIT_SET(entry
->flag
, RELEASE_REQUEST
);
191 cached_error_entry(entry
, ERR_READ_ERROR
, xstrerror());
192 ftpCloseAndFree(fd
, data
);
194 } else if (len
== 0 && entry
->mem_obj
->e_current_len
== 0) {
195 cached_error_entry(entry
,
196 ERR_ZERO_SIZE_OBJECT
,
197 errno
? xstrerror() : NULL
);
198 ftpCloseAndFree(fd
, data
);
199 } else if (len
== 0) {
200 /* Connection closed; retrieval done. */
201 if (!data
->got_marker
) {
202 /* If we didn't see the magic marker, assume the transfer
203 * failed and arrange so the object gets ejected and
204 * never gets to disk. */
205 debug(9, 1, "ftpReadReply: Didn't see magic marker, purging <URL:%s>.\n", entry
->url
);
206 entry
->expires
= cached_curtime
+ getNegativeTTL();
207 BIT_RESET(entry
->flag
, CACHABLE
);
208 BIT_SET(entry
->flag
, RELEASE_REQUEST
);
209 } else if (!(entry
->flag
& DELETE_BEHIND
)) {
210 entry
->expires
= cached_curtime
+ ttlSet(entry
);
212 /* update fdstat and fdtable */
213 storeComplete(entry
);
214 ftpCloseAndFree(fd
, data
);
215 } else if (((entry
->mem_obj
->e_current_len
+ len
) > getFtpMax()) &&
216 !(entry
->flag
& DELETE_BEHIND
)) {
217 /* accept data, but start to delete behind it */
218 storeStartDeleteBehind(entry
);
219 storeAppend(entry
, buf
, len
);
220 comm_set_select_handler(fd
,
224 } else if (entry
->flag
& CLIENT_ABORT_REQUEST
) {
225 /* append the last bit of info we get */
226 storeAppend(entry
, buf
, len
);
227 cached_error_entry(entry
, ERR_CLIENT_ABORT
, NULL
);
228 ftpCloseAndFree(fd
, data
);
230 /* check for a magic marker at the end of the read */
231 data
->got_marker
= 0;
232 if (len
>= MAGIC_MARKER_SZ
) {
233 if (!memcmp(MAGIC_MARKER
, buf
+ len
- MAGIC_MARKER_SZ
, MAGIC_MARKER_SZ
)) {
234 data
->got_marker
= 1;
235 len
-= MAGIC_MARKER_SZ
;
238 storeAppend(entry
, buf
, len
);
239 comm_set_select_handler(fd
,
243 comm_set_select_handler_plus_timeout(fd
,
245 (PF
) ftpLifetimeExpire
,
252 void ftpSendComplete(fd
, buf
, size
, errflag
, data
)
259 StoreEntry
*entry
= NULL
;
262 debug(9, 5, "ftpSendComplete: FD %d: size %d: errflag %d.\n",
266 put_free_8k_page(buf
); /* Allocated by ftpSendRequest. */
269 data
->icp_page_ptr
= NULL
; /* So lifetime expire doesn't re-free */
270 data
->icp_rwd_ptr
= NULL
; /* Don't double free in lifetimeexpire */
273 cached_error_entry(entry
, ERR_CONNECT_FAIL
, xstrerror());
274 ftpCloseAndFree(fd
, data
);
277 comm_set_select_handler(data
->ftp_fd
,
281 comm_set_select_handler_plus_timeout(data
->ftp_fd
,
283 (PF
) ftpLifetimeExpire
,
284 (caddr_t
) data
, getReadTimeout());
288 void ftpSendRequest(fd
, data
)
293 ext_table_entry
*e
= NULL
;
298 static char tbuf
[BUFSIZ
];
299 static char opts
[BUFSIZ
];
300 static char *space
= " ";
306 debug(9, 5, "ftpSendRequest: FD %d\n", fd
);
308 buflen
= strlen(data
->request
) + 256;
309 buf
= (char *) get_free_8k_page();
310 data
->icp_page_ptr
= buf
;
311 memset(buf
, '\0', buflen
);
313 path
= data
->request
;
315 if (path
[l
- 1] == '/')
318 if ((ext
= strrchr(path
, '.')) != NULL
) {
320 mode
= ((e
= mime_ext_to_type(ext
)) &&
321 strncmp(e
->mime_type
, "text", 4) == 0) ? ftpASCII
:
327 /* Remove leading slash from FTP url-path so that we can
328 * handle ftp://user:pw@host/path objects where path and /path
329 * are quite different. -DW */
330 if (!strcmp(path
, "/"))
335 /* Start building the buffer ... */
337 strcat(buf
, getFtpProgram());
340 strncpy(opts
, getFtpOptions(), BUFSIZ
);
341 for (s
= strtok(opts
, w_space
); s
; s
= strtok(NULL
, w_space
)) {
344 if (!strncmp(s
, "-t", 2))
346 if (!strncmp(s
, "-n", 2))
350 sprintf(tbuf
, "-t %d ", getReadTimeout());
354 sprintf(tbuf
, "-n %d ", getNegativeTTL());
357 strcat(buf
, "-h "); /* httpify */
358 strcat(buf
, "- "); /* stdout */
359 strcat(buf
, data
->host
);
363 strcat(buf
, mode
); /* A or I */
365 strcat(buf
, data
->user
);
367 strcat(buf
, data
->password
);
369 debug(9, 5, "ftpSendRequest: FD %d: buf '%s'\n", fd
, buf
);
370 data
->icp_rwd_ptr
= icpWrite(fd
, buf
, strlen(buf
), 30, ftpSendComplete
, (caddr_t
) data
);
373 void ftpConnInProgress(fd
, data
)
377 StoreEntry
*entry
= data
->entry
;
379 debug(9, 5, "ftpConnInProgress: FD %d\n", fd
);
381 if (comm_connect(fd
, "localhost", 3131) != COMM_OK
)
385 /* schedule this handler again */
386 comm_set_select_handler(fd
,
388 (PF
) ftpConnInProgress
,
392 debug(9, 5, "ftpConnInProgress: FD %d is now connected.", fd
);
393 break; /* cool, we're connected */
395 cached_error_entry(entry
, ERR_CONNECT_FAIL
, xstrerror());
396 ftpCloseAndFree(fd
, data
);
399 /* Call the real write handler, now that we're fully connected */
400 comm_set_select_handler(fd
,
407 int ftpStart(unusedfd
, url
, entry
)
412 FtpData
*data
= NULL
;
415 debug(9, 3, "FtpStart: FD %d <URL:%s>\n", unusedfd
, url
);
417 data
= (FtpData
*) xcalloc(1, sizeof(FtpData
));
421 if (ftp_url_parser(url
, data
)) {
422 cached_error_entry(entry
, ERR_INVALID_URL
, NULL
);
426 debug(9, 5, "FtpStart: FD %d, host=%s, request=%s, user=%s, passwd=%s\n",
427 unusedfd
, data
->host
, data
->request
, data
->user
, data
->password
);
429 data
->ftp_fd
= comm_open(COMM_NONBLOCKING
, 0, 0, url
);
430 if (data
->ftp_fd
== COMM_ERROR
) {
431 cached_error_entry(entry
, ERR_CONNECT_FAIL
, xstrerror());
435 /* Pipe/socket created ok */
437 /* Now connect ... */
438 if ((status
= comm_connect(data
->ftp_fd
, "localhost", 3131))) {
439 if (status
!= EINPROGRESS
) {
440 cached_error_entry(entry
, ERR_CONNECT_FAIL
, xstrerror());
441 ftpCloseAndFree(data
->ftp_fd
, data
);
444 debug(9, 5, "ftpStart: FD %d: EINPROGRESS.\n", data
->ftp_fd
);
445 comm_set_select_handler(data
->ftp_fd
, COMM_SELECT_LIFETIME
,
446 (PF
) ftpLifetimeExpire
, (caddr_t
) data
);
447 comm_set_select_handler(data
->ftp_fd
, COMM_SELECT_WRITE
,
448 (PF
) ftpConnInProgress
, (caddr_t
) data
);
452 fdstat_open(data
->ftp_fd
, Socket
);
453 commSetNonBlocking(data
->ftp_fd
);
454 (void) fd_note(data
->ftp_fd
, entry
->url
);
456 /* Install connection complete handler. */
457 fd_note(data
->ftp_fd
, entry
->url
);
458 comm_set_select_handler(data
->ftp_fd
,
462 comm_set_fd_lifetime(data
->ftp_fd
,
463 getClientLifetime());
464 comm_set_select_handler(data
->ftp_fd
,
465 COMM_SELECT_LIFETIME
,
466 (PF
) ftpLifetimeExpire
,
468 if (!BIT_TEST(entry
->flag
, ENTRY_PRIVATE
))
469 storeSetPublicKey(entry
); /* Make it public */
479 static char pbuf
[128];
480 char *ftpget
= getFtpProgram();
483 debug(9, 0, "ftpInitialize: pipe: %s\n", xstrerror());
486 if ((pid
= fork()) < 0) {
487 debug(9, 0, "ftpInitialize: fork: %s\n", xstrerror());
490 if (pid
!= 0) { /* parent */
492 fdstat_open(p
[1], Pipe
);
493 fd_note(p
[1], "ftpget -S");
494 fcntl(p
[1], F_SETFD
, 1); /* set close-on-exec */
502 /* inherit stdin,stdout,stderr */
503 for (fd
= 3; fd
< fdstat_biggest_fd(); fd
++)
505 sprintf(pbuf
, "%d", 3131);
506 execlp(ftpget
, ftpget
, "-D26,1", "-S", pbuf
, NULL
);
507 debug(9, 0, "ftpInitialize: %s: %s\n", ftpget
, xstrerror());
509 return (1); /* eliminate compiler warning */