1 /***************************************************************************/
2 /*									   */
3 /* ftplib.c - callable ftp access routines				   */
4 /* Copyright (C) 1996, 1997 Thomas Pfau, pfau@cnj.digex.net		   */
5 /*	73 Catherine Street, South Bound Brook, NJ, 08880		   */
6 /*									   */
7 /* This library is free software; you can redistribute it and/or	   */
8 /* modify it under the terms of the GNU Library General Public		   */
9 /* License as published by the Free Software Foundation; either		   */
10 /* version 2 of the License, or (at your option) any later version.	   */
11 /* 									   */
12 /* This library is distributed in the hope that it will be useful,	   */
13 /* but WITHOUT ANY WARRANTY; without even the implied warranty of	   */
14 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU	   */
15 /* Library General Public License for more details.			   */
16 /* 									   */
17 /* You should have received a copy of the GNU Library General Public	   */
18 /* License along with this progam; if not, write to the			   */
19 /* Free Software Foundation, Inc., 59 Temple Place - Suite 330,		   */
20 /* Boston, MA 02111-1307, USA.						   */
21 /* 									   */
22 /***************************************************************************/
23 
24 #if defined(__unix__) || defined(__VMS)
25 #include <unistd.h>
26 #endif
27 #if defined(_WIN32)
28 #include <windows.h>
29 #endif
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #if defined(__unix__)
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <netdb.h>
40 #include <arpa/inet.h>
41 #elif defined(VMS)
42 #include <types.h>
43 #include <socket.h>
44 #include <in.h>
45 #include <netdb.h>
46 #include <inet.h>
47 #elif defined(_WIN32)
48 #include <winsock.h>
49 #endif
50 
51 #define BUILDING_LIBRARY
52 #include "ftplib.h"
53 
54 #if defined(_WIN32)
55 #define SETSOCKOPT_OPTVAL_TYPE (const char *)
56 #else
57 #define SETSOCKOPT_OPTVAL_TYPE (void *)
58 #endif
59 
60 #define FTPLIB_BUFSIZ 8192
61 #define ACCEPT_TIMEOUT 30
62 
63 #define FTPLIB_CONTROL 0
64 #define FTPLIB_READ 1
65 #define FTPLIB_WRITE 2
66 
67 struct NetBuf {
68     char *cput,*cget;
69     int handle;
70     int cavail,cleft;
71     char *buf;
72     int dir;
73     char response[256];
74 };
75 
76 static char *version =
77     "ftplib Release 3 12/xx/97, copyright 1996, 1997 Thomas Pfau";
78 
79 GLOBALDEF int ftplib_debug = 0;
80 netbuf *DefaultNetbuf;
81 
82 #if defined(__unix__) || defined(VMS)
83 #define net_read read
84 #define net_write write
85 #define net_close close
86 #elif defined(_WIN32)
87 #define net_read(x,y,z) recv(x,y,z,0)
88 #define net_write(x,y,z) send(x,y,z,0)
89 #define net_close closesocket
90 #endif
91 
92 #if defined(VMS)
93 /*
94  * VAX C does not supply a memccpy routine so I provide my own
95  */
memccpy(void * dest,const void * src,int c,size_t n)96 void *memccpy(void *dest, const void *src, int c, size_t n)
97 {
98     int i=0;
99     const unsigned char *ip=src;
100     unsigned char *op=dest;
101 
102     while (i < n)
103     {
104 	if ((*op++ = *ip++) == c)
105 	    break;
106 	i++;
107     }
108     if (i == n)
109 	return NULL;
110     return op;
111 }
112 #endif
113 
114 /*
115  * read a line of text
116  *
117  * return -1 on error or bytecount
118  */
readline(char * buf,int max,netbuf * ctl)119 static int readline(char *buf,int max,netbuf *ctl)
120 {
121     int x,retval = 0;
122     char *end,*bp=buf;
123     int eof = 0;
124 
125     if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ))
126 	return -1;
127     if (max == 0)
128 	return 0;
129     do
130     {
131     	if (ctl->cavail > 0)
132     	{
133 	    x = (max >= ctl->cavail) ? ctl->cavail : max-1;
134 	    end = memccpy(bp,ctl->cget,'\n',x);
135 	    if (end != NULL)
136 		x = end - bp;
137 	    retval += x;
138 	    bp += x;
139 	    *bp = '\0';
140 	    max -= x;
141 	    ctl->cget += x;
142 	    ctl->cavail -= x;
143 	    if (end != NULL)
144 	    {
145 		bp -= 2;
146 		if (strcmp(bp,"\r\n") == 0)
147 		{
148 		    *bp++ = '\n';
149 		    *bp++ = '\0';
150 		    --retval;
151 		}
152 	    	break;
153 	    }
154     	}
155     	if (max == 1)
156     	{
157 	    *buf = '\0';
158 	    break;
159     	}
160     	if (ctl->cput == ctl->cget)
161     	{
162 	    ctl->cput = ctl->cget = ctl->buf;
163 	    ctl->cavail = 0;
164 	    ctl->cleft = FTPLIB_BUFSIZ;
165     	}
166 	if (eof)
167 	{
168 	    if (retval == 0)
169 		retval = -1;
170 	    break;
171 	}
172     	if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1)
173     	{
174 	    perror("read");
175 	    retval = -1;
176 	    break;
177     	}
178 	if (x == 0)
179 	    eof = 1;
180     	ctl->cleft -= x;
181     	ctl->cavail += x;
182     	ctl->cput += x;
183     }
184     while (1);
185     return retval;
186 }
187 
188 /*
189  * write lines of text
190  *
191  * return -1 on error or bytecount
192  */
writeline(char * buf,int len,netbuf * nData)193 static int writeline(char *buf, int len, netbuf *nData)
194 {
195     int x, nb=0, w;
196     char *ubp = buf, *nbp;
197     char lc=0;
198 
199     if (nData->dir != FTPLIB_WRITE)
200 	return -1;
201     nbp = nData->buf;
202     for (x=0; x < len; x++)
203     {
204 	if ((*ubp == '\n') && (lc != '\r'))
205 	{
206 	    if (nb == FTPLIB_BUFSIZ)
207 	    {
208 		w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ);
209 		if (w != FTPLIB_BUFSIZ)
210 		{
211 		    printf("net_write(1) returned %d, errno = %d\n", w, errno);
212 		    return(-1);
213 		}
214 		nb = 0;
215 	    }
216 	    nbp[nb++] = '\r';
217 	}
218 	if (nb == FTPLIB_BUFSIZ)
219 	{
220 	    w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ);
221 	    if (w != FTPLIB_BUFSIZ)
222 	    {
223 		printf("net_write(2) returned %d, errno = %d\n", w, errno);
224 		return(-1);
225 	    }
226 	    nb = 0;
227 	}
228 	nbp[nb++] = lc = *ubp++;
229     }
230     if (nb)
231     {
232 	w = net_write(nData->handle, nbp, nb);
233 	if (w != nb)
234 	{
235 	    printf("net_write(3) returned %d, errno = %d\n", w, errno);
236 	    return(-1);
237 	}
238     }
239     return len;
240 }
241 
242 /*
243  * read a response from the server
244  *
245  * return 0 if first char doesn't match
246  * return 1 if first char matches
247  */
readresp(char c,netbuf * nControl)248 static int readresp(char c, netbuf *nControl)
249 {
250     char match[5];
251     if (readline(nControl->response,256,nControl) == -1)
252     {
253 	perror("Control socket read failed");
254 	return 0;
255     }
256     if (ftplib_debug > 1)
257 	fprintf(stderr,"%s",nControl->response);
258     if (nControl->response[3] == '-')
259     {
260 	strncpy(match,nControl->response,3);
261 	match[3] = ' ';
262 	match[4] = '\0';
263 	do
264 	{
265 	    if (readline(nControl->response,256,nControl) == -1)
266 	    {
267 		perror("Control socket read failed");
268 		return 0;
269 	    }
270 	    if (ftplib_debug > 1)
271 		fprintf(stderr,"%s",nControl->response);
272 	}
273 	while (strncmp(nControl->response,match,4));
274     }
275     if (nControl->response[0] == c)
276 	return 1;
277     return 0;
278 }
279 
280 /*
281  * FtpInit for stupid operating systems that require it (Windows NT)
282  */
FtpInit(void)283 GLOBALDEF void FtpInit(void)
284 {
285 #if defined(_WIN32)
286     WORD wVersionRequested;
287     WSADATA wsadata;
288     int err;
289     wVersionRequested = MAKEWORD(1,1);
290     if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0)
291 	fprintf(stderr,"Network failed to start: %d\n",err);
292 #endif
293 }
294 
295 /*
296  * FtpLastResponse - return a pointer to the last response received
297  */
FtpLastResponse(netbuf * nControl)298 GLOBALDEF char *FtpLastResponse(netbuf *nControl)
299 {
300     if ((nControl) && (nControl->dir == FTPLIB_CONTROL))
301     	return nControl->response;
302     return NULL;
303 }
304 
305 /*
306  * FtpConnect - connect to remote server
307  *
308  * return 1 if connected, 0 if not
309  */
FtpConnect(const char * host,netbuf ** nControl)310 GLOBALDEF int FtpConnect(const char *host, netbuf **nControl)
311 {
312     int sControl;
313     struct sockaddr_in sin;
314     struct hostent *phe;
315     struct servent *pse;
316     int on=1;
317     netbuf *ctrl;
318     char *lhost;
319     char *pnum;
320 
321     memset(&sin,0,sizeof(sin));
322     sin.sin_family = AF_INET;
323     lhost = strdup(host);
324     pnum = strchr(lhost,':');
325     if (pnum == NULL)
326     {
327 #if defined(VMS)
328     	sin.sin_port = htons(21);
329 #else
330     	if ((pse = getservbyname("ftp","tcp")) == NULL)
331     	{
332 	    perror("getservbyname");
333 	    return 0;
334     	}
335     	sin.sin_port = pse->s_port;
336 #endif
337     }
338     else
339     {
340 	*pnum++ = '\0';
341 	if (isdigit(*pnum))
342 	    sin.sin_port = htons(atoi(pnum));
343 	else
344 	{
345 	    pse = getservbyname(pnum,"tcp");
346 	    sin.sin_port = pse->s_port;
347 	}
348     }
349     if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1)
350     {
351     	if ((phe = gethostbyname(lhost)) == NULL)
352     	{
353 	    perror("gethostbyname");
354 	    return 0;
355     	}
356     	memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
357     }
358     free(lhost);
359     sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
360     if (sControl == -1)
361     {
362 	perror("socket");
363 	return 0;
364     }
365     if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR,
366 		   SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
367     {
368 	perror("setsockopt");
369 	net_close(sControl);
370 	return 0;
371     }
372     if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1)
373     {
374 	perror("connect");
375 	net_close(sControl);
376 	return 0;
377     }
378     ctrl = calloc(1,sizeof(netbuf));
379     if (ctrl == NULL)
380     {
381 	perror("calloc");
382 	net_close(sControl);
383 	return 0;
384     }
385     ctrl->buf = malloc(FTPLIB_BUFSIZ);
386     if (ctrl->buf == NULL)
387     {
388 	perror("calloc");
389 	net_close(sControl);
390 	free(ctrl);
391 	return 0;
392     }
393     ctrl->handle = sControl;
394     ctrl->dir = FTPLIB_CONTROL;
395     if (readresp('2', ctrl) == 0)
396     {
397 	net_close(sControl);
398 	free(ctrl->buf);
399 	free(ctrl);
400 	return 0;
401     }
402     *nControl = ctrl;
403     return 1;
404 }
405 
406 /*
407  * FtpSendCmd - send a command and wait for expected response
408  *
409  * return 1 if proper response received, 0 otherwise
410  */
FtpSendCmd(const char * cmd,char expresp,netbuf * nControl)411 static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl)
412 {
413     char buf[256];
414     if (nControl->dir != FTPLIB_CONTROL)
415 	return 0;
416     if (ftplib_debug > 2)
417 	fprintf(stderr,"%s\n",cmd);
418     sprintf(buf,"%s\r\n",cmd);
419     if (net_write(nControl->handle,buf,strlen(buf)) <= 0)
420     {
421 	perror("write");
422 	return 0;
423     }
424     return readresp(expresp, nControl);
425 }
426 
427 /*
428  * FtpLogin - log in to remote server
429  *
430  * return 1 if logged in, 0 otherwise
431  */
FtpLogin(const char * user,const char * pass,netbuf * nControl)432 GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl)
433 {
434     char tempbuf[64];
435 
436     sprintf(tempbuf,"USER %s",user);
437     if (!FtpSendCmd(tempbuf,'3',nControl))
438     {
439 	if (nControl->response[0] == '2')
440 	    return 1;
441 	return 0;
442     }
443     sprintf(tempbuf,"PASS %s",pass);
444     return FtpSendCmd(tempbuf,'2',nControl);
445 }
446 
447 /*
448  * FtpOpenPort - set up data connection
449  *
450  * return 1 if successful, 0 otherwise
451  */
FtpOpenPort(netbuf * nControl,netbuf ** nData,int mode,int dir)452 static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir)
453 {
454     int sData;
455     union {
456 	struct sockaddr sa;
457 	struct sockaddr_in in;
458     } sin;
459     struct linger lng = { 0, 0 };
460     int l;
461     int on=1;
462     char *cp;
463     unsigned int v[6];
464     netbuf *ctrl;
465 
466     if (nControl->dir != FTPLIB_CONTROL)
467 	return -1;
468     if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE))
469     {
470 	sprintf(nControl->response, "Invalid direction %d\n", dir);
471 	return -1;
472     }
473     if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE))
474     {
475 	sprintf(nControl->response, "Invalid mode %c\n", mode);
476 	return -1;
477     }
478     l = sizeof(sin);
479     memset(&sin, 0, l);
480     sin.in.sin_family = AF_INET;
481     if (!FtpSendCmd("PASV",'2',nControl))
482 	return -1;
483     cp = strchr(nControl->response,'(');
484     if (cp == NULL)
485 	return -1;
486     cp++;
487     sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]);
488     sin.sa.sa_data[2] = v[2];
489     sin.sa.sa_data[3] = v[3];
490     sin.sa.sa_data[4] = v[4];
491     sin.sa.sa_data[5] = v[5];
492     sin.sa.sa_data[0] = v[0];
493     sin.sa.sa_data[1] = v[1];
494     sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
495     if (sData == -1)
496     {
497 	perror("socket");
498 	return -1;
499     }
500     if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR,
501 		   SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1)
502     {
503 	perror("setsockopt");
504 	net_close(sData);
505 	return -1;
506     }
507     if (setsockopt(sData,SOL_SOCKET,SO_LINGER,
508 		   SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1)
509     {
510 	perror("setsockopt");
511 	net_close(sData);
512 	return -1;
513     }
514     if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1)
515     {
516 	perror("connect");
517 	net_close(sData);
518 	return -1;
519     }
520     ctrl = calloc(1,sizeof(netbuf));
521     if (ctrl == NULL)
522     {
523 	perror("calloc");
524 	net_close(sData);
525 	return -1;
526     }
527     if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL))
528     {
529 	perror("calloc");
530 	net_close(sData);
531 	free(ctrl);
532 	return -1;
533     }
534     ctrl->handle = sData;
535     ctrl->dir = dir;
536     *nData = ctrl;
537     return 1;
538 }
539 
540 /*
541  * FtpAccess - return a handle for a data stream
542  *
543  * return 1 if successful, 0 otherwise
544  */
FtpAccess(const char * path,int typ,int mode,netbuf * nControl,netbuf ** nData)545 GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl,
546     netbuf **nData)
547 {
548     char buf[256];
549     int dir;
550     if ((path == NULL) && ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ)))
551     {
552 	sprintf(nControl->response, "Missing path argument for file transfer\n");
553 	return 0;
554     }
555     sprintf(buf, "TYPE %c", mode);
556     if (!FtpSendCmd(buf, '2', nControl))
557 	return 0;
558     switch (typ)
559     {
560       case FTPLIB_DIR:
561     	strcpy(buf,"NLST");
562 	dir = FTPLIB_READ;
563 	break;
564       case FTPLIB_DIR_VERBOSE:
565 	strcpy(buf,"LIST");
566 	dir = FTPLIB_READ;
567 	break;
568       case FTPLIB_FILE_READ:
569 	strcpy(buf,"RETR");
570 	dir = FTPLIB_READ;
571 	break;
572       case FTPLIB_FILE_WRITE:
573 	strcpy(buf,"STOR");
574 	dir = FTPLIB_WRITE;
575 	break;
576       default:
577 	sprintf(nControl->response, "Invalid open type %d\n", typ);
578 	return 0;
579     }
580     if (path != NULL)
581 	sprintf(buf+strlen(buf)," %s",path);
582     if (FtpOpenPort(nControl, nData, mode, dir) == -1)
583 	return 0;
584     if (!FtpSendCmd(buf, '1', nControl))
585     {
586 	FtpClose(*nData);
587 	*nData = NULL;
588 	return 0;
589     }
590     return 1;
591 }
592 
593 /*
594  * FtpRead - read from a data connection
595  */
FtpRead(void * buf,int max,netbuf * nData)596 GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData)
597 {
598     if (nData->dir != FTPLIB_READ)
599 	return 0;
600     if (nData->buf)
601     	return readline(buf, max, nData);
602     return net_read(nData->handle, buf, max);
603 }
604 
605 /*
606  * FtpWrite - write to a data connection
607  */
FtpWrite(void * buf,int len,netbuf * nData)608 GLOBALDEF int FtpWrite(void *buf, int len, netbuf *nData)
609 {
610     if (nData->dir != FTPLIB_WRITE)
611 	return 0;
612     if (nData->buf)
613     	return writeline(buf, len, nData);
614     return net_write(nData->handle, buf, len);
615 }
616 
617 /*
618  * FtpClose - close a data connection
619  */
FtpClose(netbuf * nData)620 GLOBALDEF int FtpClose(netbuf *nData)
621 {
622     if (nData->dir == FTPLIB_WRITE)
623     {
624 	if (nData->buf != NULL)
625 	    writeline(NULL, 0, nData);
626     }
627     else if (nData->dir != FTPLIB_READ)
628 	return 0;
629     if (nData->buf)
630     	free(nData->buf);
631     shutdown(nData->handle,2);
632     net_close(nData->handle);
633     return 1;
634 }
635 
636 /*
637  * FtpSite - send a SITE command
638  *
639  * return 1 if command successful, 0 otherwise
640  */
FtpSite(const char * cmd,netbuf * nControl)641 GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl)
642 {
643     char buf[256];
644     sprintf(buf,"SITE %s",cmd);
645     if (!FtpSendCmd(buf,'2',nControl))
646 	return 0;
647     return 1;
648 }
649 
650 /*
651  * FtpMkdir - create a directory at server
652  *
653  * return 1 if successful, 0 otherwise
654  */
FtpMkdir(const char * path,netbuf * nControl)655 GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl)
656 {
657     char buf[256];
658     sprintf(buf,"MKD %s",path);
659     if (!FtpSendCmd(buf,'2', nControl))
660 	return 0;
661     return 1;
662 }
663 
664 /*
665  * FtpChdir - change path at remote
666  *
667  * return 1 if successful, 0 otherwise
668  */
FtpChdir(const char * path,netbuf * nControl)669 GLOBALDEF int FtpChdir(const char *path, netbuf *nControl)
670 {
671     char buf[256];
672     sprintf(buf,"CWD %s",path);
673     if (!FtpSendCmd(buf,'2',nControl))
674 	return 0;
675     return 1;
676 }
677 
678 /*
679  * FtpRmdir - remove directory at remote
680  *
681  * return 1 if successful, 0 otherwise
682  */
FtpRmdir(const char * path,netbuf * nControl)683 GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl)
684 {
685     char buf[256];
686     sprintf(buf,"RMD %s",path);
687     if (!FtpSendCmd(buf,'2',nControl))
688 	return 0;
689     return 1;
690 }
691 
692 /*
693  * FtpXfer - issue a command and transfer data
694  *
695  * return 1 if successful, 0 otherwise
696  */
FtpXfer(const char * localfile,const char * path,netbuf * nControl,int typ,int mode)697 static int FtpXfer(const char *localfile, const char *path,
698 	netbuf *nControl, int typ, int mode)
699 {
700     int l,c;
701     char *dbuf;
702     FILE *local = NULL;
703     netbuf *nData;
704 
705     if (localfile != NULL)
706     {
707 	local = fopen(localfile, (typ == FTPLIB_FILE_WRITE) ? "r" : "w");
708 	if (local == NULL)
709 	{
710 	    strcpy(nControl->response, strerror(errno));
711 	    return 0;
712 	}
713     }
714     if (local == NULL)
715 	local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout;
716     if (!FtpAccess(path, typ, mode, nControl, &nData))
717 	return 0;
718     dbuf = malloc(FTPLIB_BUFSIZ);
719     if (typ == FTPLIB_FILE_WRITE)
720     {
721 	while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0)
722 	    if ((c = FtpWrite(dbuf, l, nData)) < l)
723 		printf("short write: passed %d, wrote %d\n", l, c);
724     }
725     else
726     {
727     	while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0)
728 	    if (fwrite(dbuf, 1, l, local) <= 0)
729 	    {
730 		perror("localfile write");
731 		break;
732 	    }
733     }
734     fflush(local);
735     if (localfile != NULL)
736 	fclose(local);
737     FtpClose(nData);
738     free(dbuf);
739     return readresp('2', nControl);
740 }
741 
742 /*
743  * FtpNlst - issue an NLST command and write response to output
744  *
745  * return 1 if successful, 0 otherwise
746  */
FtpNlst(const char * outputfile,const char * path,netbuf * nControl)747 GLOBALDEF int FtpNlst(const char *outputfile, const char *path,
748 	netbuf *nControl)
749 {
750     return FtpXfer(outputfile, path, nControl, FTPLIB_DIR, FTPLIB_ASCII);
751 }
752 
753 /*
754  * FtpDir - issue a LIST command and write response to output
755  *
756  * return 1 if successful, 0 otherwise
757  */
FtpDir(const char * outputfile,const char * path,netbuf * nControl)758 GLOBALDEF int FtpDir(const char *outputfile, const char *path, netbuf *nControl)
759 {
760     return FtpXfer(outputfile, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII);
761 }
762 
763 /*
764  * FtpGet - issue a GET command and write received data to output
765  *
766  * return 1 if successful, 0 otherwise
767  */
FtpGet(const char * outputfile,const char * path,char mode,netbuf * nControl)768 GLOBALDEF int FtpGet(const char *outputfile, const char *path,
769 	char mode, netbuf *nControl)
770 {
771     return FtpXfer(outputfile, path, nControl, FTPLIB_FILE_READ, mode);
772 }
773 
774 /*
775  * FtpPut - issue a PUT command and send data from input
776  *
777  * return 1 if successful, 0 otherwise
778  */
FtpPut(const char * inputfile,const char * path,char mode,netbuf * nControl)779 GLOBALDEF int FtpPut(const char *inputfile, const char *path, char mode,
780 	netbuf *nControl)
781 {
782     return FtpXfer(inputfile, path, nControl, FTPLIB_FILE_WRITE, mode);
783 }
784 
785 /*
786  * FtpRename - rename a file at remote
787  *
788  * return 1 if successful, 0 otherwise
789  */
FtpRename(const char * src,const char * dst,netbuf * nControl)790 GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl)
791 {
792     char cmd[256];
793     sprintf(cmd,"RNFR %s",src);
794     if (!FtpSendCmd(cmd,'3',nControl))
795 	return 0;
796     sprintf(cmd,"RNTO %s",dst);
797     if (!FtpSendCmd(cmd,'2',nControl))
798 	return 0;
799     return 1;
800 }
801 
802 /*
803  * FtpDelete - delete a file at remote
804  *
805  * return 1 if successful, 0 otherwise
806  */
FtpDelete(const char * fnm,netbuf * nControl)807 GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl)
808 {
809     char cmd[256];
810     sprintf(cmd,"DELE %s",fnm);
811     if (!FtpSendCmd(cmd,'2', nControl))
812 	return 0;
813     return 1;
814 }
815 
816 /*
817  * FtpQuit - disconnect from remote
818  *
819  * return 1 if successful, 0 otherwise
820  */
FtpQuit(netbuf * nControl)821 GLOBALDEF void FtpQuit(netbuf *nControl)
822 {
823     if (nControl->dir != FTPLIB_CONTROL)
824 	return;
825     FtpSendCmd("QUIT",'2',nControl);
826     net_close(nControl->handle);
827     free(nControl->buf);
828     free(nControl);
829 }
830