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