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