]>
Commit | Line | Data |
---|---|---|
769a03c3 MF |
1 | /***************************************************************************/ |
2 | /* */ | |
3 | /* ftplib.c - callable ftp access routines */ | |
4 | /* Copyright (C) 1996-2001, 2013 Thomas Pfau, tfpfau@gmail.com */ | |
5 | /* 1407 Thomas Ave, North Brunswick, NJ, 08902 */ | |
6 | /* */ | |
7 | /* This library is free software. You can redistribute it and/or */ | |
8 | /* modify it under the terms of the Artistic License 2.0. */ | |
9 | /* */ | |
10 | /* This library is distributed in the hope that it will be useful, */ | |
11 | /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ | |
12 | /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ | |
13 | /* Artistic License 2.0 for more details. */ | |
14 | /* */ | |
15 | /* See the file LICENSE or */ | |
16 | /* http://www.perlfoundation.org/artistic_license_2_0 */ | |
17 | /* */ | |
18 | /***************************************************************************/ | |
19 | ||
20 | #if defined(__unix__) || defined(__VMS) | |
21 | #include <unistd.h> | |
22 | #endif | |
23 | #if defined(_WIN32) | |
24 | #include <windows.h> | |
25 | #endif | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | #include <errno.h> | |
30 | #include <ctype.h> | |
31 | #if defined(__unix__) | |
32 | #include <sys/time.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/select.h> | |
35 | #include <sys/socket.h> | |
36 | #include <netinet/in.h> | |
37 | #include <netdb.h> | |
38 | #include <arpa/inet.h> | |
39 | #elif defined(VMS) | |
40 | #include <types.h> | |
41 | #include <socket.h> | |
42 | #include <in.h> | |
43 | #include <netdb.h> | |
44 | #include <inet.h> | |
45 | #elif defined(_WIN32) | |
46 | #include <winsock.h> | |
47 | #endif | |
48 | #undef _REENTRANT | |
49 | /* We don't support "192.168.0.2:ftp" syntax -- use a port # only. */ | |
50 | #define getservbyname(...) NULL | |
51 | #define getservbyname_r(...) NULL | |
52 | #define hstrerror(e) e | |
53 | ||
54 | #define BUILDING_LIBRARY | |
55 | #include "ftplib.h" | |
56 | ||
57 | #if defined(__UINT64_MAX) && !defined(PRIu64) | |
58 | #if ULONG_MAX == __UINT32_MAX | |
59 | #define PRIu64 "llu" | |
60 | #else | |
61 | #define PRIu64 "lu" | |
62 | #endif | |
63 | #endif | |
64 | ||
65 | #if defined(_WIN32) | |
66 | #define SETSOCKOPT_OPTVAL_TYPE (const char *) | |
67 | #else | |
68 | #define SETSOCKOPT_OPTVAL_TYPE (void *) | |
69 | #endif | |
70 | ||
71 | #define FTPLIB_BUFSIZ 8192 | |
72 | #define RESPONSE_BUFSIZ 1024 | |
73 | #define TMP_BUFSIZ 1024 | |
74 | #define ACCEPT_TIMEOUT 30 | |
75 | ||
76 | #define FTPLIB_CONTROL 0 | |
77 | #define FTPLIB_READ 1 | |
78 | #define FTPLIB_WRITE 2 | |
79 | ||
80 | #if !defined FTPLIB_DEFMODE | |
81 | #define FTPLIB_DEFMODE FTPLIB_PASSIVE | |
82 | #endif | |
83 | ||
84 | struct NetBuf { | |
85 | char *cput,*cget; | |
86 | int handle; | |
87 | int cavail,cleft; | |
88 | char *buf; | |
89 | int dir; | |
90 | netbuf *ctrl; | |
91 | netbuf *data; | |
92 | int cmode; | |
93 | struct timeval idletime; | |
94 | FtpCallback idlecb; | |
95 | void *idlearg; | |
96 | unsigned long int xfered; | |
97 | unsigned long int cbbytes; | |
98 | unsigned long int xfered1; | |
99 | FtpResponseCallback respcb, sendcb; | |
100 | char response[RESPONSE_BUFSIZ]; | |
101 | }; | |
102 | ||
103 | char *version = | |
104 | "ftplib Release 4.0 07-Jun-2013, copyright 1996-2003, 2013 Thomas Pfau"; | |
105 | ||
106 | GLOBALDEF int ftplib_debug = 2; | |
107 | ||
108 | #if defined(__unix__) || defined(VMS) | |
109 | int net_read(int fd, char *buf, size_t len) | |
110 | { | |
111 | while ( 1 ) | |
112 | { | |
113 | int c = read(fd, buf, len); | |
114 | if ( c == -1 ) | |
115 | { | |
116 | if ( errno != EINTR && errno != EAGAIN ) | |
117 | return -1; | |
118 | } | |
119 | else | |
120 | { | |
121 | return c; | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
126 | int net_write(int fd, const char *buf, size_t len) | |
127 | { | |
128 | int done = 0; | |
129 | while ( len > 0 ) | |
130 | { | |
131 | int c = write( fd, buf, len ); | |
132 | if ( c == -1 ) | |
133 | { | |
134 | if ( errno != EINTR && errno != EAGAIN ) | |
135 | return -1; | |
136 | } | |
137 | else if ( c == 0 ) | |
138 | { | |
139 | return done; | |
140 | } | |
141 | else | |
142 | { | |
143 | buf += c; | |
144 | done += c; | |
145 | len -= c; | |
146 | } | |
147 | } | |
148 | return done; | |
149 | } | |
150 | #define net_close close | |
151 | #elif defined(_WIN32) | |
152 | #define net_read(x,y,z) recv(x,y,z,0) | |
153 | #define net_write(x,y,z) send(x,y,z,0) | |
154 | #define net_close closesocket | |
155 | #endif | |
156 | \f | |
157 | #if defined(NEED_MEMCCPY) | |
158 | /* | |
159 | * VAX C does not supply a memccpy routine so I provide my own | |
160 | */ | |
161 | void *memccpy(void *dest, const void *src, int c, size_t n) | |
162 | { | |
163 | int i=0; | |
164 | const unsigned char *ip=src; | |
165 | unsigned char *op=dest; | |
166 | ||
167 | while (i < n) | |
168 | { | |
169 | if ((*op++ = *ip++) == c) | |
170 | break; | |
171 | i++; | |
172 | } | |
173 | if (i == n) | |
174 | return NULL; | |
175 | return op; | |
176 | } | |
177 | #endif | |
178 | #if defined(NEED_STRDUP) | |
179 | /* | |
180 | * strdup - return a malloc'ed copy of a string | |
181 | */ | |
182 | char *strdup(const char *src) | |
183 | { | |
184 | int l = strlen(src) + 1; | |
185 | char *dst = malloc(l); | |
186 | if (dst) | |
187 | strcpy(dst,src); | |
188 | return dst; | |
189 | } | |
190 | #endif | |
191 | \f | |
192 | /* | |
193 | * socket_wait - wait for socket to receive or flush data | |
194 | * | |
195 | * return 1 if no user callback, otherwise, return value returned by | |
196 | * user callback | |
197 | */ | |
198 | static int socket_wait(netbuf *ctl) | |
199 | { | |
200 | fd_set fd,*rfd = NULL,*wfd = NULL; | |
201 | struct timeval tv; | |
202 | int rv = 0; | |
203 | if ((ctl->dir == FTPLIB_CONTROL) || (ctl->idlecb == NULL)) | |
204 | return 1; | |
205 | if (ctl->dir == FTPLIB_WRITE) | |
206 | wfd = &fd; | |
207 | else | |
208 | rfd = &fd; | |
209 | FD_ZERO(&fd); | |
210 | do | |
211 | { | |
212 | FD_SET(ctl->handle,&fd); | |
213 | tv = ctl->idletime; | |
214 | rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); | |
215 | if (rv == -1) | |
216 | { | |
217 | rv = 0; | |
218 | strncpy(ctl->ctrl->response, strerror(errno), | |
219 | sizeof(ctl->ctrl->response)); | |
220 | break; | |
221 | } | |
222 | else if (rv > 0) | |
223 | { | |
224 | rv = 1; | |
225 | break; | |
226 | } | |
227 | } | |
228 | while ((rv = ctl->idlecb(ctl, ctl->xfered, ctl->idlearg))); | |
229 | return rv; | |
230 | } | |
231 | \f | |
232 | /* | |
233 | * read a line of text | |
234 | * | |
235 | * return -1 on error or bytecount | |
236 | */ | |
237 | static int readline(char *buf,int max,netbuf *ctl) | |
238 | { | |
239 | int x,retval = 0; | |
240 | char *end,*bp=buf; | |
241 | int eof = 0; | |
242 | ||
243 | if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) | |
244 | return -1; | |
245 | if (max == 0) | |
246 | return 0; | |
247 | do | |
248 | { | |
249 | if (ctl->cavail > 0) | |
250 | { | |
251 | x = (max >= ctl->cavail) ? ctl->cavail : max-1; | |
252 | end = memccpy(bp,ctl->cget,'\n',x); | |
253 | if (end != NULL) | |
254 | x = end - bp; | |
255 | retval += x; | |
256 | bp += x; | |
257 | *bp = '\0'; | |
258 | max -= x; | |
259 | ctl->cget += x; | |
260 | ctl->cavail -= x; | |
261 | if (end != NULL) | |
262 | { | |
263 | bp -= 2; | |
264 | if (strcmp(bp,"\r\n") == 0) | |
265 | { | |
266 | *bp++ = '\n'; | |
267 | *bp++ = '\0'; | |
268 | --retval; | |
269 | } | |
270 | break; | |
271 | } | |
272 | } | |
273 | if (max == 1) | |
274 | { | |
275 | *buf = '\0'; | |
276 | break; | |
277 | } | |
278 | if (ctl->cput == ctl->cget) | |
279 | { | |
280 | ctl->cput = ctl->cget = ctl->buf; | |
281 | ctl->cavail = 0; | |
282 | ctl->cleft = FTPLIB_BUFSIZ; | |
283 | } | |
284 | if (eof) | |
285 | { | |
286 | if (retval == 0) | |
287 | retval = -1; | |
288 | break; | |
289 | } | |
290 | if (!socket_wait(ctl)) | |
291 | return retval; | |
292 | if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1) | |
293 | { | |
294 | if (ftplib_debug) | |
295 | perror("read"); | |
296 | retval = -1; | |
297 | break; | |
298 | } | |
299 | if (x == 0) | |
300 | eof = 1; | |
301 | ctl->cleft -= x; | |
302 | ctl->cavail += x; | |
303 | ctl->cput += x; | |
304 | } | |
305 | while (1); | |
306 | return retval; | |
307 | } | |
308 | \f | |
309 | /* | |
310 | * write lines of text | |
311 | * | |
312 | * return -1 on error or bytecount | |
313 | */ | |
314 | static int writeline(const char *buf, int len, netbuf *nData) | |
315 | { | |
316 | int x, nb=0, w; | |
317 | const char *ubp = buf; | |
318 | char *nbp; | |
319 | char lc=0; | |
320 | ||
321 | if (nData->dir != FTPLIB_WRITE) | |
322 | return -1; | |
323 | nbp = nData->buf; | |
324 | for (x=0; x < len; x++) | |
325 | { | |
326 | if ((*ubp == '\n') && (lc != '\r')) | |
327 | { | |
328 | if (nb == FTPLIB_BUFSIZ) | |
329 | { | |
330 | if (!socket_wait(nData)) | |
331 | return x; | |
332 | w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); | |
333 | if (w != FTPLIB_BUFSIZ) | |
334 | { | |
335 | if (ftplib_debug) | |
336 | printf("net_write(1) returned %d, errno = %d\n", w, errno); | |
337 | return(-1); | |
338 | } | |
339 | nb = 0; | |
340 | } | |
341 | nbp[nb++] = '\r'; | |
342 | } | |
343 | if (nb == FTPLIB_BUFSIZ) | |
344 | { | |
345 | if (!socket_wait(nData)) | |
346 | return x; | |
347 | w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); | |
348 | if (w != FTPLIB_BUFSIZ) | |
349 | { | |
350 | if (ftplib_debug) | |
351 | printf("net_write(2) returned %d, errno = %d\n", w, errno); | |
352 | return(-1); | |
353 | } | |
354 | nb = 0; | |
355 | } | |
356 | nbp[nb++] = lc = *ubp++; | |
357 | } | |
358 | if (nb) | |
359 | { | |
360 | if (!socket_wait(nData)) | |
361 | return x; | |
362 | w = net_write(nData->handle, nbp, nb); | |
363 | if (w != nb) | |
364 | { | |
365 | if (ftplib_debug) | |
366 | printf("net_write(3) returned %d, errno = %d\n", w, errno); | |
367 | return(-1); | |
368 | } | |
369 | } | |
370 | return len; | |
371 | } | |
372 | \f | |
373 | /* | |
374 | * read a response from the server | |
375 | * | |
376 | * return 0 if first char doesn't match | |
377 | * return 1 if first char matches | |
378 | */ | |
379 | static int readresp(char c, netbuf *nControl) | |
380 | { | |
381 | char match[5]; | |
382 | if (readline(nControl->response,RESPONSE_BUFSIZ,nControl) == -1) | |
383 | { | |
384 | if (ftplib_debug) | |
385 | perror("Control socket read failed"); | |
386 | return 0; | |
387 | } | |
388 | if (ftplib_debug > 1) | |
389 | fprintf(stderr,"%s",nControl->response); | |
390 | if (nControl->respcb) | |
391 | nControl->respcb(nControl, nControl->response); | |
392 | if (nControl->response[3] == '-') | |
393 | { | |
394 | strncpy(match,nControl->response,3); | |
395 | match[3] = ' '; | |
396 | match[4] = '\0'; | |
397 | do | |
398 | { | |
399 | if (readline(nControl->response,RESPONSE_BUFSIZ,nControl) == -1) | |
400 | { | |
401 | if (ftplib_debug) | |
402 | perror("Control socket read failed"); | |
403 | return 0; | |
404 | } | |
405 | if (ftplib_debug > 1) | |
406 | fprintf(stderr,"%s",nControl->response); | |
407 | } | |
408 | while (strncmp(nControl->response,match,4)); | |
409 | } | |
410 | if (nControl->response[0] == c) | |
411 | return 1; | |
412 | return 0; | |
413 | } | |
414 | \f | |
415 | /* | |
416 | * FtpInit for stupid operating systems that require it (Windows NT) | |
417 | */ | |
418 | GLOBALDEF void FtpInit(void) | |
419 | { | |
420 | #if defined(_WIN32) | |
421 | WORD wVersionRequested; | |
422 | WSADATA wsadata; | |
423 | int err; | |
424 | wVersionRequested = MAKEWORD(1,1); | |
425 | if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0) | |
426 | fprintf(stderr,"Network failed to start: %d\n",err); | |
427 | #endif | |
428 | } | |
429 | \f | |
430 | /* | |
431 | * FtpLastResponse - return a pointer to the last response received | |
432 | */ | |
433 | GLOBALDEF char *FtpLastResponse(netbuf *nControl) | |
434 | { | |
435 | if ((nControl) && (nControl->dir == FTPLIB_CONTROL)) | |
436 | return nControl->response; | |
437 | return NULL; | |
438 | } | |
439 | \f | |
440 | /* | |
441 | * FtpConnect - connect to remote server | |
442 | * | |
443 | * return 1 if connected, 0 if not | |
444 | */ | |
445 | GLOBALDEF int FtpConnect(const char *host, FtpResponseCallback respcb, netbuf **nControl) | |
446 | { | |
447 | int sControl; | |
448 | struct sockaddr_in sin; | |
449 | int on=1; | |
450 | netbuf *ctrl; | |
451 | char *lhost; | |
452 | char *pnum; | |
453 | ||
454 | memset(&sin,0,sizeof(sin)); | |
455 | sin.sin_family = AF_INET; | |
456 | lhost = strdup(host); | |
457 | pnum = strchr(lhost,':'); | |
458 | if (pnum == NULL) | |
459 | pnum = "ftp"; | |
460 | else | |
461 | *pnum++ = '\0'; | |
462 | if (isdigit((int)*pnum)) | |
463 | sin.sin_port = htons(atoi(pnum)); | |
464 | else | |
465 | { | |
466 | struct servent *pse; | |
467 | #if _REENTRANT | |
468 | struct servent se; | |
469 | char tmpbuf[TMP_BUFSIZ]; | |
470 | int i; | |
471 | if ( ( i = getservbyname_r(pnum,"tcp",&se,tmpbuf,TMP_BUFSIZ,&pse) ) != 0 ) | |
472 | { | |
473 | errno = i; | |
474 | if ( ftplib_debug ) | |
475 | perror("getservbyname_r"); | |
476 | free(lhost); | |
477 | return 0; | |
478 | } | |
479 | #else | |
480 | if ((pse = getservbyname(pnum,"tcp") ) == NULL ) | |
481 | { | |
482 | if ( ftplib_debug ) | |
483 | perror("getservbyname"); | |
484 | free(lhost); | |
485 | return 0; | |
486 | } | |
487 | #endif | |
488 | sin.sin_port = pse->s_port; | |
489 | } | |
490 | if ((sin.sin_addr.s_addr = inet_addr(lhost)) == INADDR_NONE) | |
491 | { | |
492 | struct hostent *phe; | |
493 | #ifdef _REENTRANT | |
494 | struct hostent he; | |
495 | char tmpbuf[TMP_BUFSIZ]; | |
496 | int i, herr; | |
497 | if ( ( i = gethostbyname_r( lhost, &he, tmpbuf, TMP_BUFSIZ, &phe, &herr ) ) != 0 ) | |
498 | { | |
499 | if ( ftplib_debug ) | |
500 | fprintf(stderr, "gethostbyname: %s\n", hstrerror(herr)); | |
501 | free(lhost); | |
502 | return 0; | |
503 | } | |
504 | #else | |
505 | if ((phe = gethostbyname(lhost)) == NULL) | |
506 | { | |
507 | if (ftplib_debug) | |
508 | fprintf(stderr, "gethostbyname: %i\n", hstrerror(h_errno)); | |
509 | free(lhost); | |
510 | return 0; | |
511 | } | |
512 | #endif | |
513 | memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); | |
514 | } | |
515 | free(lhost); | |
516 | sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | |
517 | if (sControl == -1) | |
518 | { | |
519 | if (ftplib_debug) | |
520 | perror("socket"); | |
521 | return 0; | |
522 | } | |
523 | if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, | |
524 | SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) | |
525 | { | |
526 | if (ftplib_debug) | |
527 | perror("setsockopt"); | |
528 | net_close(sControl); | |
529 | return 0; | |
530 | } | |
531 | if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) | |
532 | { | |
533 | if (ftplib_debug) | |
534 | perror("connect"); | |
535 | net_close(sControl); | |
536 | return 0; | |
537 | } | |
538 | ctrl = calloc(1,sizeof(netbuf)); | |
539 | if (ctrl == NULL) | |
540 | { | |
541 | if (ftplib_debug) | |
542 | perror("calloc"); | |
543 | net_close(sControl); | |
544 | return 0; | |
545 | } | |
546 | ctrl->buf = malloc(FTPLIB_BUFSIZ); | |
547 | if (ctrl->buf == NULL) | |
548 | { | |
549 | if (ftplib_debug) | |
550 | perror("calloc"); | |
551 | net_close(sControl); | |
552 | free(ctrl); | |
553 | return 0; | |
554 | } | |
555 | ctrl->respcb = respcb; | |
556 | ctrl->sendcb = NULL; | |
557 | ctrl->handle = sControl; | |
558 | ctrl->dir = FTPLIB_CONTROL; | |
559 | ctrl->ctrl = NULL; | |
560 | ctrl->cmode = FTPLIB_DEFMODE; | |
561 | ctrl->idlecb = NULL; | |
562 | ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; | |
563 | ctrl->idlearg = NULL; | |
564 | ctrl->xfered = 0; | |
565 | ctrl->xfered1 = 0; | |
566 | ctrl->cbbytes = 0; | |
567 | if (readresp('2', ctrl) == 0) | |
568 | { | |
569 | net_close(sControl); | |
570 | free(ctrl->buf); | |
571 | free(ctrl); | |
572 | return 0; | |
573 | } | |
574 | *nControl = ctrl; | |
575 | return 1; | |
576 | } | |
577 | \f | |
578 | GLOBALDEF int FtpSetResponseCallback(FtpResponseCallback respcb, netbuf *nControl) | |
579 | { | |
580 | nControl->respcb = respcb; | |
581 | return 0; | |
582 | } | |
583 | GLOBALDEF int FtpSetSendCmdCallback(FtpResponseCallback sendcb, netbuf *nControl) | |
584 | { | |
585 | nControl->sendcb = sendcb; | |
586 | return 0; | |
587 | } | |
588 | \f | |
589 | GLOBALDEF int FtpSetCallback(const FtpCallbackOptions *opt, netbuf *nControl) | |
590 | { | |
591 | nControl->idlecb = opt->cbFunc; | |
592 | nControl->idlearg = opt->cbArg; | |
593 | nControl->idletime.tv_sec = opt->idleTime / 1000; | |
594 | nControl->idletime.tv_usec = (opt->idleTime % 1000) * 1000; | |
595 | nControl->cbbytes = opt->bytesXferred; | |
596 | return 1; | |
597 | } | |
598 | GLOBALDEF int FtpClearCallback(netbuf *nControl) | |
599 | { | |
600 | nControl->idlecb = NULL; | |
601 | nControl->idlearg = NULL; | |
602 | nControl->idletime.tv_sec = 0; | |
603 | nControl->idletime.tv_usec = 0; | |
604 | nControl->cbbytes = 0; | |
605 | return 1; | |
606 | } | |
607 | /* | |
608 | * FtpOptions - change connection options | |
609 | * | |
610 | * returns 1 if successful, 0 on error | |
611 | */ | |
612 | GLOBALDEF int FtpOptions(int opt, long val, netbuf *nControl) | |
613 | { | |
614 | int v,rv=0; | |
615 | switch (opt) | |
616 | { | |
617 | case FTPLIB_CONNMODE: | |
618 | v = (int) val; | |
619 | if ((v == FTPLIB_PASSIVE) || (v == FTPLIB_PORT)) | |
620 | { | |
621 | nControl->cmode = v; | |
622 | rv = 1; | |
623 | } | |
624 | break; | |
625 | case FTPLIB_CALLBACK: | |
626 | nControl->idlecb = (FtpCallback) val; | |
627 | rv = 1; | |
628 | break; | |
629 | case FTPLIB_IDLETIME: | |
630 | v = (int) val; | |
631 | rv = 1; | |
632 | nControl->idletime.tv_sec = v / 1000; | |
633 | nControl->idletime.tv_usec = (v % 1000) * 1000; | |
634 | break; | |
635 | case FTPLIB_CALLBACKARG: | |
636 | rv = 1; | |
637 | nControl->idlearg = (void *) val; | |
638 | break; | |
639 | case FTPLIB_CALLBACKBYTES: | |
640 | rv = 1; | |
641 | nControl->cbbytes = (int) val; | |
642 | break; | |
643 | } | |
644 | return rv; | |
645 | } | |
646 | \f | |
647 | /* | |
648 | * FtpSendCmd - send a command and wait for expected response | |
649 | * | |
650 | * return 1 if proper response received, 0 otherwise | |
651 | */ | |
652 | static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) | |
653 | { | |
654 | char buf[TMP_BUFSIZ]; | |
655 | if (nControl->dir != FTPLIB_CONTROL) | |
656 | return 0; | |
657 | if (ftplib_debug > 2) | |
658 | fprintf(stderr,"%s\n",cmd); | |
659 | if ((strlen(cmd) + 3) > sizeof(buf)) | |
660 | return 0; | |
661 | sprintf(buf,"%s\r\n",cmd); | |
662 | if (nControl->sendcb) | |
663 | nControl->sendcb(nControl, buf); | |
664 | if (net_write(nControl->handle,buf,strlen(buf)) <= 0) | |
665 | { | |
666 | if (ftplib_debug) | |
667 | perror("write"); | |
668 | return 0; | |
669 | } | |
670 | return readresp(expresp, nControl); | |
671 | } | |
672 | \f | |
673 | /* | |
674 | * FtpLogin - log in to remote server | |
675 | * | |
676 | * return 1 if logged in, 0 otherwise | |
677 | */ | |
678 | GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl) | |
679 | { | |
680 | char tempbuf[64]; | |
681 | ||
682 | if (((strlen(user) + 7) > sizeof(tempbuf)) || | |
683 | ((strlen(pass) + 7) > sizeof(tempbuf))) | |
684 | return 0; | |
685 | sprintf(tempbuf,"USER %s",user); | |
686 | if (!FtpSendCmd(tempbuf,'3',nControl)) | |
687 | { | |
688 | if (nControl->response[0] == '2') | |
689 | return 1; | |
690 | return 0; | |
691 | } | |
692 | sprintf(tempbuf,"PASS %s",pass); | |
693 | return FtpSendCmd(tempbuf,'2',nControl); | |
694 | } | |
695 | \f | |
696 | /* | |
697 | * FtpOpenPort - set up data connection | |
698 | * | |
699 | * return 1 if successful, 0 otherwise | |
700 | */ | |
701 | static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir) | |
702 | { | |
703 | int sData; | |
704 | union { | |
705 | struct sockaddr sa; | |
706 | struct sockaddr_in in; | |
707 | } sin; | |
708 | struct linger lng = { 0, 0 }; | |
709 | unsigned int l; | |
710 | int on=1; | |
711 | netbuf *ctrl; | |
712 | char *cp; | |
713 | unsigned int v[6]; | |
714 | char buf[TMP_BUFSIZ]; | |
715 | ||
716 | if (nControl->dir != FTPLIB_CONTROL) | |
717 | return -1; | |
718 | if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) | |
719 | { | |
720 | sprintf(nControl->response, "Invalid direction %d\n", dir); | |
721 | return -1; | |
722 | } | |
723 | if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE)) | |
724 | { | |
725 | sprintf(nControl->response, "Invalid mode %c\n", mode); | |
726 | return -1; | |
727 | } | |
728 | l = sizeof(sin); | |
729 | if (nControl->cmode == FTPLIB_PASSIVE) | |
730 | { | |
731 | memset(&sin, 0, l); | |
732 | sin.in.sin_family = AF_INET; | |
733 | if (!FtpSendCmd("PASV",'2',nControl)) | |
734 | return -1; | |
735 | cp = strchr(nControl->response,'('); | |
736 | if (cp == NULL) | |
737 | return -1; | |
738 | cp++; | |
739 | sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); | |
740 | sin.sa.sa_data[2] = v[2]; | |
741 | sin.sa.sa_data[3] = v[3]; | |
742 | sin.sa.sa_data[4] = v[4]; | |
743 | sin.sa.sa_data[5] = v[5]; | |
744 | if (sin.sa.sa_data[2] == 0 && sin.sa.sa_data[3] == 0 && | |
745 | sin.sa.sa_data[4] == 0 && sin.sa.sa_data[5] == 0) | |
746 | ; | |
747 | sin.sa.sa_data[0] = v[0]; | |
748 | sin.sa.sa_data[1] = v[1]; | |
749 | } | |
750 | else | |
751 | { | |
752 | if (getsockname(nControl->handle, &sin.sa, &l) < 0) | |
753 | { | |
754 | if (ftplib_debug) | |
755 | perror("getsockname"); | |
756 | return -1; | |
757 | } | |
758 | } | |
759 | sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); | |
760 | if (sData == -1) | |
761 | { | |
762 | if (ftplib_debug) | |
763 | perror("socket"); | |
764 | return -1; | |
765 | } | |
766 | if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, | |
767 | SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) | |
768 | { | |
769 | if (ftplib_debug) | |
770 | perror("setsockopt"); | |
771 | net_close(sData); | |
772 | return -1; | |
773 | } | |
774 | if (setsockopt(sData,SOL_SOCKET,SO_LINGER, | |
775 | SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) | |
776 | { | |
777 | if (ftplib_debug) | |
778 | perror("setsockopt"); | |
779 | net_close(sData); | |
780 | return -1; | |
781 | } | |
782 | if (nControl->cmode == FTPLIB_PASSIVE) | |
783 | { | |
784 | if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) | |
785 | { | |
786 | if (ftplib_debug) | |
787 | perror("connect"); | |
788 | net_close(sData); | |
789 | return -1; | |
790 | } | |
791 | } | |
792 | else | |
793 | { | |
794 | sin.in.sin_port = 0; | |
795 | if (bind(sData, &sin.sa, sizeof(sin)) == -1) | |
796 | { | |
797 | if (ftplib_debug) | |
798 | perror("bind"); | |
799 | net_close(sData); | |
800 | return -1; | |
801 | } | |
802 | if (listen(sData, 1) < 0) | |
803 | { | |
804 | if (ftplib_debug) | |
805 | perror("listen"); | |
806 | net_close(sData); | |
807 | return -1; | |
808 | } | |
809 | if (getsockname(sData, &sin.sa, &l) < 0) | |
810 | return -1; | |
811 | sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", | |
812 | (unsigned char) sin.sa.sa_data[2], | |
813 | (unsigned char) sin.sa.sa_data[3], | |
814 | (unsigned char) sin.sa.sa_data[4], | |
815 | (unsigned char) sin.sa.sa_data[5], | |
816 | (unsigned char) sin.sa.sa_data[0], | |
817 | (unsigned char) sin.sa.sa_data[1]); | |
818 | if (!FtpSendCmd(buf,'2',nControl)) | |
819 | { | |
820 | net_close(sData); | |
821 | return -1; | |
822 | } | |
823 | } | |
824 | ctrl = calloc(1,sizeof(netbuf)); | |
825 | if (ctrl == NULL) | |
826 | { | |
827 | if (ftplib_debug) | |
828 | perror("calloc"); | |
829 | net_close(sData); | |
830 | return -1; | |
831 | } | |
832 | if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL)) | |
833 | { | |
834 | if (ftplib_debug) | |
835 | perror("calloc"); | |
836 | net_close(sData); | |
837 | free(ctrl); | |
838 | return -1; | |
839 | } | |
840 | ctrl->handle = sData; | |
841 | ctrl->dir = dir; | |
842 | ctrl->idletime = nControl->idletime; | |
843 | ctrl->idlearg = nControl->idlearg; | |
844 | ctrl->xfered = 0; | |
845 | ctrl->xfered1 = 0; | |
846 | ctrl->cbbytes = nControl->cbbytes; | |
847 | if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec || ctrl->cbbytes) | |
848 | ctrl->idlecb = nControl->idlecb; | |
849 | else | |
850 | ctrl->idlecb = NULL; | |
851 | *nData = ctrl; | |
852 | return 1; | |
853 | } | |
854 | \f | |
855 | /* | |
856 | * FtpAcceptConnection - accept connection from server | |
857 | * | |
858 | * return 1 if successful, 0 otherwise | |
859 | */ | |
860 | static int FtpAcceptConnection(netbuf *nData, netbuf *nControl) | |
861 | { | |
862 | int sData; | |
863 | struct sockaddr addr; | |
864 | unsigned int l; | |
865 | int i; | |
866 | struct timeval tv; | |
867 | fd_set mask; | |
868 | int rv = 0; | |
869 | ||
870 | FD_ZERO(&mask); | |
871 | FD_SET(nControl->handle, &mask); | |
872 | FD_SET(nData->handle, &mask); | |
873 | tv.tv_usec = 0; | |
874 | tv.tv_sec = ACCEPT_TIMEOUT; | |
875 | i = nControl->handle; | |
876 | if (i < nData->handle) | |
877 | i = nData->handle; | |
878 | i = select(i+1, &mask, NULL, NULL, &tv); | |
879 | if (i == -1) | |
880 | { | |
881 | strncpy(nControl->response, strerror(errno), | |
882 | sizeof(nControl->response)); | |
883 | net_close(nData->handle); | |
884 | nData->handle = 0; | |
885 | } | |
886 | else if (i == 0) | |
887 | { | |
888 | strcpy(nControl->response, "timed out waiting for connection"); | |
889 | net_close(nData->handle); | |
890 | nData->handle = 0; | |
891 | } | |
892 | else | |
893 | { | |
894 | if (FD_ISSET(nData->handle, &mask)) | |
895 | { | |
896 | l = sizeof(addr); | |
897 | sData = accept(nData->handle, &addr, &l); | |
898 | i = errno; | |
899 | net_close(nData->handle); | |
900 | if (sData > 0) | |
901 | { | |
902 | rv = 1; | |
903 | nData->handle = sData; | |
904 | } | |
905 | else | |
906 | { | |
907 | strncpy(nControl->response, strerror(i), | |
908 | sizeof(nControl->response)); | |
909 | nData->handle = 0; | |
910 | } | |
911 | } | |
912 | else if (FD_ISSET(nControl->handle, &mask)) | |
913 | { | |
914 | net_close(nData->handle); | |
915 | nData->handle = 0; | |
916 | readresp('2', nControl); | |
917 | } | |
918 | } | |
919 | return rv; | |
920 | } | |
921 | \f | |
922 | /* | |
923 | * FtpAccess - return a handle for a data stream | |
924 | * | |
925 | * return 1 if successful, 0 otherwise | |
926 | */ | |
927 | GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, | |
928 | netbuf **nData) | |
929 | { | |
930 | char buf[TMP_BUFSIZ]; | |
931 | int dir; | |
932 | if ((path == NULL) && | |
933 | ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ))) | |
934 | { | |
935 | sprintf(nControl->response, | |
936 | "Missing path argument for file transfer\n"); | |
937 | return 0; | |
938 | } | |
939 | sprintf(buf, "TYPE %c", mode); | |
940 | if (!FtpSendCmd(buf, '2', nControl)) | |
941 | return 0; | |
942 | switch (typ) | |
943 | { | |
944 | case FTPLIB_DIR: | |
945 | strcpy(buf,"NLST"); | |
946 | dir = FTPLIB_READ; | |
947 | break; | |
948 | case FTPLIB_DIR_VERBOSE: | |
949 | strcpy(buf,"LIST"); | |
950 | dir = FTPLIB_READ; | |
951 | break; | |
952 | case FTPLIB_FILE_READ: | |
953 | strcpy(buf,"RETR"); | |
954 | dir = FTPLIB_READ; | |
955 | break; | |
956 | case FTPLIB_FILE_WRITE: | |
957 | strcpy(buf,"STOR"); | |
958 | dir = FTPLIB_WRITE; | |
959 | break; | |
960 | default: | |
961 | sprintf(nControl->response, "Invalid open type %d\n", typ); | |
962 | return 0; | |
963 | } | |
964 | if (path != NULL) | |
965 | { | |
966 | int i = strlen(buf); | |
967 | buf[i++] = ' '; | |
968 | if ((strlen(path) + i + 1) >= sizeof(buf)) | |
969 | return 0; | |
970 | strcpy(&buf[i],path); | |
971 | } | |
972 | if (FtpOpenPort(nControl, nData, mode, dir) == -1) | |
973 | return 0; | |
974 | if (!FtpSendCmd(buf, '1', nControl)) | |
975 | { | |
976 | FtpClose(*nData); | |
977 | *nData = NULL; | |
978 | return 0; | |
979 | } | |
980 | (*nData)->ctrl = nControl; | |
981 | nControl->data = *nData; | |
982 | if (nControl->cmode == FTPLIB_PORT) | |
983 | { | |
984 | if (!FtpAcceptConnection(*nData,nControl)) | |
985 | { | |
986 | FtpClose(*nData); | |
987 | *nData = NULL; | |
988 | nControl->data = NULL; | |
989 | return 0; | |
990 | } | |
991 | } | |
992 | return 1; | |
993 | } | |
994 | \f | |
995 | /* | |
996 | * FtpRead - read from a data connection | |
997 | */ | |
998 | GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData) | |
999 | { | |
1000 | int i; | |
1001 | if (nData->dir != FTPLIB_READ) | |
1002 | return 0; | |
1003 | if (nData->buf) | |
1004 | i = readline(buf, max, nData); | |
1005 | else | |
1006 | { | |
1007 | i = socket_wait(nData); | |
1008 | if (i != 1) | |
1009 | return 0; | |
1010 | i = net_read(nData->handle, buf, max); | |
1011 | } | |
1012 | if (i == -1) | |
1013 | return 0; | |
1014 | nData->xfered += i; | |
1015 | if (nData->idlecb && nData->cbbytes) | |
1016 | { | |
1017 | nData->xfered1 += i; | |
1018 | if (nData->xfered1 > nData->cbbytes) | |
1019 | { | |
1020 | if (nData->idlecb(nData, nData->xfered, nData->idlearg) == 0) | |
1021 | return 0; | |
1022 | nData->xfered1 = 0; | |
1023 | } | |
1024 | } | |
1025 | return i; | |
1026 | } | |
1027 | \f | |
1028 | /* | |
1029 | * FtpWrite - write to a data connection | |
1030 | */ | |
1031 | GLOBALDEF int FtpWrite(const void *buf, int len, netbuf *nData) | |
1032 | { | |
1033 | int i; | |
1034 | if (nData->dir != FTPLIB_WRITE) | |
1035 | return 0; | |
1036 | if (nData->buf) | |
1037 | i = writeline(buf, len, nData); | |
1038 | else | |
1039 | { | |
1040 | socket_wait(nData); | |
1041 | i = net_write(nData->handle, buf, len); | |
1042 | } | |
1043 | if (i == -1) | |
1044 | return 0; | |
1045 | nData->xfered += i; | |
1046 | if (nData->idlecb && nData->cbbytes) | |
1047 | { | |
1048 | nData->xfered1 += i; | |
1049 | if (nData->xfered1 > nData->cbbytes) | |
1050 | { | |
1051 | nData->idlecb(nData, nData->xfered, nData->idlearg); | |
1052 | nData->xfered1 = 0; | |
1053 | } | |
1054 | } | |
1055 | return i; | |
1056 | } | |
1057 | \f | |
1058 | /* | |
1059 | * FtpClose - close a data connection | |
1060 | */ | |
1061 | GLOBALDEF int FtpClose(netbuf *nData) | |
1062 | { | |
1063 | netbuf *ctrl; | |
1064 | switch (nData->dir) | |
1065 | { | |
1066 | case FTPLIB_WRITE: | |
1067 | /* potential problem - if buffer flush fails, how to notify user? */ | |
1068 | if (nData->buf != NULL) | |
1069 | writeline(NULL, 0, nData); | |
1070 | case FTPLIB_READ: | |
1071 | if (nData->buf) | |
1072 | free(nData->buf); | |
1073 | shutdown(nData->handle,2); | |
1074 | net_close(nData->handle); | |
1075 | ctrl = nData->ctrl; | |
1076 | free(nData); | |
1077 | if (ctrl) | |
1078 | { | |
1079 | ctrl->data = NULL; | |
1080 | if (ctrl->response[0] != '4' && ctrl->response[0] != 5) | |
1081 | return readresp('2', ctrl); | |
1082 | } | |
1083 | return 1; | |
1084 | case FTPLIB_CONTROL: | |
1085 | if (nData->data) | |
1086 | { | |
1087 | nData->ctrl = NULL; | |
1088 | FtpClose(nData->data); | |
1089 | } | |
1090 | net_close(nData->handle); | |
1091 | free(nData); | |
1092 | return 0; | |
1093 | } | |
1094 | return 1; | |
1095 | } | |
1096 | \f | |
1097 | /* | |
1098 | * FtpSite - send a SITE command | |
1099 | * | |
1100 | * return 1 if command successful, 0 otherwise | |
1101 | */ | |
1102 | GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl) | |
1103 | { | |
1104 | char buf[TMP_BUFSIZ]; | |
1105 | ||
1106 | if ((strlen(cmd) + 7) > sizeof(buf)) | |
1107 | return 0; | |
1108 | sprintf(buf,"SITE %s",cmd); | |
1109 | if (!FtpSendCmd(buf,'2',nControl)) | |
1110 | return 0; | |
1111 | return 1; | |
1112 | } | |
1113 | \f | |
1114 | /* | |
1115 | * FtpSysType - send a SYST command | |
1116 | * | |
1117 | * Fills in the user buffer with the remote system type. If more | |
1118 | * information from the response is required, the user can parse | |
1119 | * it out of the response buffer returned by FtpLastResponse(). | |
1120 | * | |
1121 | * return 1 if command successful, 0 otherwise | |
1122 | */ | |
1123 | GLOBALDEF int FtpSysType(char *buf, int max, netbuf *nControl) | |
1124 | { | |
1125 | int l = max; | |
1126 | char *b = buf; | |
1127 | char *s; | |
1128 | if (!FtpSendCmd("SYST",'2',nControl)) | |
1129 | return 0; | |
1130 | s = &nControl->response[4]; | |
1131 | while ((--l) && (*s != ' ')) | |
1132 | *b++ = *s++; | |
1133 | *b++ = '\0'; | |
1134 | return 1; | |
1135 | } | |
1136 | \f | |
1137 | /* | |
1138 | * FtpMkdir - create a directory at server | |
1139 | * | |
1140 | * return 1 if successful, 0 otherwise | |
1141 | */ | |
1142 | GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl) | |
1143 | { | |
1144 | char buf[TMP_BUFSIZ]; | |
1145 | ||
1146 | if ((strlen(path) + 6) > sizeof(buf)) | |
1147 | return 0; | |
1148 | sprintf(buf,"MKD %s",path); | |
1149 | if (!FtpSendCmd(buf,'2', nControl)) | |
1150 | return 0; | |
1151 | return 1; | |
1152 | } | |
1153 | \f | |
1154 | /* | |
1155 | * FtpChdir - change path at remote | |
1156 | * | |
1157 | * return 1 if successful, 0 otherwise | |
1158 | */ | |
1159 | GLOBALDEF int FtpChdir(const char *path, netbuf *nControl) | |
1160 | { | |
1161 | char buf[TMP_BUFSIZ]; | |
1162 | ||
1163 | if ((strlen(path) + 6) > sizeof(buf)) | |
1164 | return 0; | |
1165 | sprintf(buf,"CWD %s",path); | |
1166 | if (!FtpSendCmd(buf,'2',nControl)) | |
1167 | return 0; | |
1168 | return 1; | |
1169 | } | |
1170 | \f | |
1171 | /* | |
1172 | * FtpCDUp - move to parent directory at remote | |
1173 | * | |
1174 | * return 1 if successful, 0 otherwise | |
1175 | */ | |
1176 | GLOBALDEF int FtpCDUp(netbuf *nControl) | |
1177 | { | |
1178 | if (!FtpSendCmd("CDUP",'2',nControl)) | |
1179 | return 0; | |
1180 | return 1; | |
1181 | } | |
1182 | \f | |
1183 | /* | |
1184 | * FtpRmdir - remove directory at remote | |
1185 | * | |
1186 | * return 1 if successful, 0 otherwise | |
1187 | */ | |
1188 | GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl) | |
1189 | { | |
1190 | char buf[TMP_BUFSIZ]; | |
1191 | ||
1192 | if ((strlen(path) + 6) > sizeof(buf)) | |
1193 | return 0; | |
1194 | sprintf(buf,"RMD %s",path); | |
1195 | if (!FtpSendCmd(buf,'2',nControl)) | |
1196 | return 0; | |
1197 | return 1; | |
1198 | } | |
1199 | \f | |
1200 | /* | |
1201 | * FtpPwd - get working directory at remote | |
1202 | * | |
1203 | * return 1 if successful, 0 otherwise | |
1204 | */ | |
1205 | GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl) | |
1206 | { | |
1207 | int l = max; | |
1208 | char *b = path; | |
1209 | char *s; | |
1210 | if (!FtpSendCmd("PWD",'2',nControl)) | |
1211 | return 0; | |
1212 | s = strchr(nControl->response, '"'); | |
1213 | if (s == NULL) | |
1214 | return 0; | |
1215 | s++; | |
1216 | while ((--l) && (*s) && (*s != '"')) | |
1217 | *b++ = *s++; | |
1218 | *b++ = '\0'; | |
1219 | return 1; | |
1220 | } | |
1221 | \f | |
1222 | /* | |
1223 | * FtpXfer - issue a command and transfer data | |
1224 | * | |
1225 | * return 1 if successful, 0 otherwise | |
1226 | */ | |
1227 | static int FtpXfer(FtpDataCallback cb, const char *path, | |
1228 | netbuf *nControl, int typ, int mode) | |
1229 | { | |
1230 | int l,c; | |
1231 | char *dbuf; | |
1232 | netbuf *nData; | |
1233 | int rv=1; | |
1234 | ||
1235 | if (!FtpAccess(path, typ, mode, nControl, &nData)) | |
1236 | return 0; | |
1237 | dbuf = malloc(FTPLIB_BUFSIZ); | |
1238 | if (typ == FTPLIB_FILE_WRITE) | |
1239 | { | |
1240 | while ((l = cb(dbuf, FTPLIB_BUFSIZ)) > 0) | |
1241 | { | |
1242 | if ((c = FtpWrite(dbuf, l, nData)) < l) | |
1243 | { | |
1244 | printf("short write: passed %d, wrote %d\n", l, c); | |
1245 | rv = 0; | |
1246 | break; | |
1247 | } | |
1248 | } | |
1249 | } | |
1250 | else | |
1251 | { | |
1252 | while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) | |
1253 | { | |
1254 | if (cb(dbuf, l) == 0) | |
1255 | { | |
1256 | if (ftplib_debug) | |
1257 | perror("localfile write"); | |
1258 | rv = 0; | |
1259 | break; | |
1260 | } | |
1261 | } | |
1262 | } | |
1263 | free(dbuf); | |
1264 | FtpClose(nData); | |
1265 | return rv; | |
1266 | } | |
1267 | \f | |
1268 | /* | |
1269 | * FtpNlst - issue an NLST command and write response to output | |
1270 | * | |
1271 | * return 1 if successful, 0 otherwise | |
1272 | */ | |
1273 | GLOBALDEF int FtpNlst(FtpDataCallback cb, const char *path, | |
1274 | netbuf *nControl) | |
1275 | { | |
1276 | return FtpXfer(cb, path, nControl, FTPLIB_DIR, FTPLIB_ASCII); | |
1277 | } | |
1278 | \f | |
1279 | /* | |
1280 | * FtpDir - issue a LIST command and write response to output | |
1281 | * | |
1282 | * return 1 if successful, 0 otherwise | |
1283 | */ | |
1284 | GLOBALDEF int FtpDir(FtpDataCallback cb, const char *path, netbuf *nControl) | |
1285 | { | |
1286 | return FtpXfer(cb, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII); | |
1287 | } | |
1288 | \f | |
1289 | /* | |
1290 | * FtpSize - determine the size of a remote file | |
1291 | * | |
1292 | * return 1 if successful, 0 otherwise | |
1293 | */ | |
1294 | GLOBALDEF int FtpSize(const char *path, unsigned int *size, char mode, netbuf *nControl) | |
1295 | { | |
1296 | char cmd[TMP_BUFSIZ]; | |
1297 | int resp,rv=1; | |
1298 | unsigned int sz; | |
1299 | ||
1300 | if ((strlen(path) + 7) > sizeof(cmd)) | |
1301 | return 0; | |
1302 | sprintf(cmd, "TYPE %c", mode); | |
1303 | if (!FtpSendCmd(cmd, '2', nControl)) | |
1304 | return 0; | |
1305 | sprintf(cmd,"SIZE %s",path); | |
1306 | if (!FtpSendCmd(cmd,'2',nControl)) | |
1307 | rv = 0; | |
1308 | else | |
1309 | { | |
1310 | if (sscanf(nControl->response, "%d %u", &resp, &sz) == 2) | |
1311 | *size = sz; | |
1312 | else | |
1313 | rv = 0; | |
1314 | } | |
1315 | return rv; | |
1316 | } | |
1317 | \f | |
1318 | #if defined(__UINT64_MAX) | |
1319 | /* | |
1320 | * FtpSizeLong - determine the size of a remote file | |
1321 | * | |
1322 | * return 1 if successful, 0 otherwise | |
1323 | */ | |
1324 | GLOBALDEF int FtpSizeLong(const char *path, fsz_t *size, char mode, netbuf *nControl) | |
1325 | { | |
1326 | char cmd[TMP_BUFSIZ]; | |
1327 | int resp,rv=1; | |
1328 | fsz_t sz; | |
1329 | ||
1330 | if ((strlen(path) + 7) > sizeof(cmd)) | |
1331 | return 0; | |
1332 | sprintf(cmd, "TYPE %c", mode); | |
1333 | if (!FtpSendCmd(cmd, '2', nControl)) | |
1334 | return 0; | |
1335 | sprintf(cmd,"SIZE %s",path); | |
1336 | if (!FtpSendCmd(cmd,'2',nControl)) | |
1337 | rv = 0; | |
1338 | else | |
1339 | { | |
1340 | if (sscanf(nControl->response, "%d %" PRIu64 "", &resp, &sz) == 2) | |
1341 | *size = sz; | |
1342 | else | |
1343 | rv = 0; | |
1344 | } | |
1345 | return rv; | |
1346 | } | |
1347 | #endif | |
1348 | \f | |
1349 | /* | |
1350 | * FtpModDate - determine the modification date of a remote file | |
1351 | * | |
1352 | * return 1 if successful, 0 otherwise | |
1353 | */ | |
1354 | GLOBALDEF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl) | |
1355 | { | |
1356 | char buf[TMP_BUFSIZ]; | |
1357 | int rv = 1; | |
1358 | ||
1359 | if ((strlen(path) + 7) > sizeof(buf)) | |
1360 | return 0; | |
1361 | sprintf(buf,"MDTM %s",path); | |
1362 | if (!FtpSendCmd(buf,'2',nControl)) | |
1363 | rv = 0; | |
1364 | else | |
1365 | strncpy(dt, &nControl->response[4], max); | |
1366 | return rv; | |
1367 | } | |
1368 | \f | |
1369 | /* | |
1370 | * FtpGet - issue a GET command and write received data to output | |
1371 | * | |
1372 | * return 1 if successful, 0 otherwise | |
1373 | */ | |
1374 | GLOBALDEF int FtpGet(FtpDataCallback cb, const char *path, | |
1375 | char mode, netbuf *nControl) | |
1376 | { | |
1377 | return FtpXfer(cb, path, nControl, FTPLIB_FILE_READ, mode); | |
1378 | } | |
1379 | \f | |
1380 | /* | |
1381 | * FtpPut - issue a PUT command and send data from input | |
1382 | * | |
1383 | * return 1 if successful, 0 otherwise | |
1384 | */ | |
1385 | GLOBALDEF int FtpPut(FtpDataCallback cb, const char *path, char mode, | |
1386 | netbuf *nControl) | |
1387 | { | |
1388 | return FtpXfer(cb, path, nControl, FTPLIB_FILE_WRITE, mode); | |
1389 | } | |
1390 | \f | |
1391 | /* | |
1392 | * FtpRename - rename a file at remote | |
1393 | * | |
1394 | * return 1 if successful, 0 otherwise | |
1395 | */ | |
1396 | GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl) | |
1397 | { | |
1398 | char cmd[TMP_BUFSIZ]; | |
1399 | ||
1400 | if (((strlen(src) + 7) > sizeof(cmd)) || | |
1401 | ((strlen(dst) + 7) > sizeof(cmd))) | |
1402 | return 0; | |
1403 | sprintf(cmd,"RNFR %s",src); | |
1404 | if (!FtpSendCmd(cmd,'3',nControl)) | |
1405 | return 0; | |
1406 | sprintf(cmd,"RNTO %s",dst); | |
1407 | if (!FtpSendCmd(cmd,'2',nControl)) | |
1408 | return 0; | |
1409 | return 1; | |
1410 | } | |
1411 | \f | |
1412 | /* | |
1413 | * FtpDelete - delete a file at remote | |
1414 | * | |
1415 | * return 1 if successful, 0 otherwise | |
1416 | */ | |
1417 | GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl) | |
1418 | { | |
1419 | char cmd[TMP_BUFSIZ]; | |
1420 | ||
1421 | if ((strlen(fnm) + 7) > sizeof(cmd)) | |
1422 | return 0; | |
1423 | sprintf(cmd,"DELE %s",fnm); | |
1424 | if (!FtpSendCmd(cmd,'2', nControl)) | |
1425 | return 0; | |
1426 | return 1; | |
1427 | } | |
1428 | \f | |
1429 | /* | |
1430 | * FtpQuit - disconnect from remote | |
1431 | * | |
1432 | * return 1 if successful, 0 otherwise | |
1433 | */ | |
1434 | GLOBALDEF int FtpQuit(netbuf *nControl) | |
1435 | { | |
1436 | if (nControl->dir != FTPLIB_CONTROL) | |
1437 | return 0; | |
1438 | FtpSendCmd("QUIT",'2',nControl); | |
1439 | net_close(nControl->handle); | |
1440 | free(nControl->buf); | |
1441 | free(nControl); | |
1442 | return 1; | |
1443 | } |