1 /***************************************************************************/
3 /* ftplib.c - callable ftp access routines */
4 /* Copyright (C) 1996-2001, 2013 Thomas Pfau, tfpfau@gmail.com */
5 /* 1407 Thomas Ave, North Brunswick, NJ, 08902 */
7 /* This library is free software. You can redistribute it and/or */
8 /* modify it under the terms of the Artistic License 2.0. */
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. */
15 /* See the file LICENSE or */
16 /* http://www.perlfoundation.org/artistic_license_2_0 */
18 /***************************************************************************/
20 #if defined(__unix__) || defined(__VMS)
33 #include <sys/types.h>
34 #include <sys/select.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
38 #include <arpa/inet.h>
49 /* We don't support "192.168.0.2:ftp" syntax -- use a port # only. */
50 #define getservbyname(...) NULL
51 #define getservbyname_r(...) NULL
52 #define hstrerror(e) e
54 #define BUILDING_LIBRARY
57 #if defined(__UINT64_MAX) && !defined(PRIu64)
58 #if ULONG_MAX == __UINT32_MAX
66 #define SETSOCKOPT_OPTVAL_TYPE (const char *)
68 #define SETSOCKOPT_OPTVAL_TYPE (void *)
71 #define FTPLIB_BUFSIZ 8192
72 #define RESPONSE_BUFSIZ 1024
73 #define TMP_BUFSIZ 1024
74 #define ACCEPT_TIMEOUT 30
76 #define FTPLIB_CONTROL 0
78 #define FTPLIB_WRITE 2
80 #if !defined FTPLIB_DEFMODE
81 #define FTPLIB_DEFMODE FTPLIB_PASSIVE
93 struct timeval idletime
;
96 unsigned long int xfered
;
97 unsigned long int cbbytes
;
98 unsigned long int xfered1
;
99 FtpResponseCallback respcb
, sendcb
;
100 char response
[RESPONSE_BUFSIZ
];
104 "ftplib Release 4.0 07-Jun-2013, copyright 1996-2003, 2013 Thomas Pfau";
106 GLOBALDEF
int ftplib_debug
= 2;
108 #if defined(__unix__) || defined(VMS)
109 int net_read(int fd
, char *buf
, size_t len
)
113 int c
= read(fd
, buf
, len
);
116 if ( errno
!= EINTR
&& errno
!= EAGAIN
)
126 int net_write(int fd
, const char *buf
, size_t len
)
131 int c
= write( fd
, buf
, len
);
134 if ( errno
!= EINTR
&& errno
!= EAGAIN
)
150 #define net_close close
151 #elif defined(_WIN32)
152 #define net_read(x,y,z) recv(x,y,z,0)
153 #define net_write(x,y,z) send(x,y,z,0)
154 #define net_close closesocket
157 #if defined(NEED_MEMCCPY)
159 * VAX C does not supply a memccpy routine so I provide my own
161 void *memccpy(void *dest
, const void *src
, int c
, size_t n
)
164 const unsigned char *ip
=src
;
165 unsigned char *op
=dest
;
169 if ((*op
++ = *ip
++) == c
)
178 #if defined(NEED_STRDUP)
180 * strdup - return a malloc'ed copy of a string
182 char *strdup(const char *src
)
184 int l
= strlen(src
) + 1;
185 char *dst
= malloc(l
);
193 * socket_wait - wait for socket to receive or flush data
195 * return 1 if no user callback, otherwise, return value returned by
198 static int socket_wait(netbuf
*ctl
)
200 fd_set fd
,*rfd
= NULL
,*wfd
= NULL
;
203 if ((ctl
->dir
== FTPLIB_CONTROL
) || (ctl
->idlecb
== NULL
))
205 if (ctl
->dir
== FTPLIB_WRITE
)
212 FD_SET(ctl
->handle
,&fd
);
214 rv
= select(ctl
->handle
+1, rfd
, wfd
, NULL
, &tv
);
218 strncpy(ctl
->ctrl
->response
, strerror(errno
),
219 sizeof(ctl
->ctrl
->response
));
228 while ((rv
= ctl
->idlecb(ctl
, ctl
->xfered
, ctl
->idlearg
)));
233 * read a line of text
235 * return -1 on error or bytecount
237 static int readline(char *buf
,int max
,netbuf
*ctl
)
243 if ((ctl
->dir
!= FTPLIB_CONTROL
) && (ctl
->dir
!= FTPLIB_READ
))
251 x
= (max
>= ctl
->cavail
) ? ctl
->cavail
: max
-1;
252 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
264 if (strcmp(bp
,"\r\n") == 0)
278 if (ctl
->cput
== ctl
->cget
)
280 ctl
->cput
= ctl
->cget
= ctl
->buf
;
282 ctl
->cleft
= FTPLIB_BUFSIZ
;
290 if (!socket_wait(ctl
))
292 if ((x
= net_read(ctl
->handle
,ctl
->cput
,ctl
->cleft
)) == -1)
310 * write lines of text
312 * return -1 on error or bytecount
314 static int writeline(const char *buf
, int len
, netbuf
*nData
)
317 const char *ubp
= buf
;
321 if (nData
->dir
!= FTPLIB_WRITE
)
324 for (x
=0; x
< len
; x
++)
326 if ((*ubp
== '\n') && (lc
!= '\r'))
328 if (nb
== FTPLIB_BUFSIZ
)
330 if (!socket_wait(nData
))
332 w
= net_write(nData
->handle
, nbp
, FTPLIB_BUFSIZ
);
333 if (w
!= FTPLIB_BUFSIZ
)
336 printf("net_write(1) returned %d, errno = %d\n", w
, errno
);
343 if (nb
== FTPLIB_BUFSIZ
)
345 if (!socket_wait(nData
))
347 w
= net_write(nData
->handle
, nbp
, FTPLIB_BUFSIZ
);
348 if (w
!= FTPLIB_BUFSIZ
)
351 printf("net_write(2) returned %d, errno = %d\n", w
, errno
);
356 nbp
[nb
++] = lc
= *ubp
++;
360 if (!socket_wait(nData
))
362 w
= net_write(nData
->handle
, nbp
, nb
);
366 printf("net_write(3) returned %d, errno = %d\n", w
, errno
);
374 * read a response from the server
376 * return 0 if first char doesn't match
377 * return 1 if first char matches
379 static int readresp(char c
, netbuf
*nControl
)
382 if (readline(nControl
->response
,RESPONSE_BUFSIZ
,nControl
) == -1)
385 perror("Control socket read failed");
388 if (ftplib_debug
> 1)
389 fprintf(stderr
,"%s",nControl
->response
);
390 if (nControl
->respcb
)
391 nControl
->respcb(nControl
, nControl
->response
);
392 if (nControl
->response
[3] == '-')
394 strncpy(match
,nControl
->response
,3);
399 if (readline(nControl
->response
,RESPONSE_BUFSIZ
,nControl
) == -1)
402 perror("Control socket read failed");
405 if (ftplib_debug
> 1)
406 fprintf(stderr
,"%s",nControl
->response
);
408 while (strncmp(nControl
->response
,match
,4));
410 if (nControl
->response
[0] == c
)
416 * FtpInit for stupid operating systems that require it (Windows NT)
418 GLOBALDEF
void FtpInit(void)
421 WORD wVersionRequested
;
424 wVersionRequested
= MAKEWORD(1,1);
425 if ((err
= WSAStartup(wVersionRequested
,&wsadata
)) != 0)
426 fprintf(stderr
,"Network failed to start: %d\n",err
);
431 * FtpLastResponse - return a pointer to the last response received
433 GLOBALDEF
char *FtpLastResponse(netbuf
*nControl
)
435 if ((nControl
) && (nControl
->dir
== FTPLIB_CONTROL
))
436 return nControl
->response
;
441 * FtpConnect - connect to remote server
443 * return 1 if connected, 0 if not
445 GLOBALDEF
int FtpConnect(const char *host
, FtpResponseCallback respcb
, netbuf
**nControl
)
448 struct sockaddr_in sin
;
454 memset(&sin
,0,sizeof(sin
));
455 sin
.sin_family
= AF_INET
;
456 lhost
= strdup(host
);
457 pnum
= strchr(lhost
,':');
462 if (isdigit((int)*pnum
))
463 sin
.sin_port
= htons(atoi(pnum
));
469 char tmpbuf
[TMP_BUFSIZ
];
471 if ( ( i
= getservbyname_r(pnum
,"tcp",&se
,tmpbuf
,TMP_BUFSIZ
,&pse
) ) != 0 )
475 perror("getservbyname_r");
480 if ((pse
= getservbyname(pnum
,"tcp") ) == NULL
)
483 perror("getservbyname");
488 sin
.sin_port
= pse
->s_port
;
490 if ((sin
.sin_addr
.s_addr
= inet_addr(lhost
)) == INADDR_NONE
)
495 char tmpbuf
[TMP_BUFSIZ
];
497 if ( ( i
= gethostbyname_r( lhost
, &he
, tmpbuf
, TMP_BUFSIZ
, &phe
, &herr
) ) != 0 )
500 fprintf(stderr
, "gethostbyname: %s\n", hstrerror(herr
));
505 if ((phe
= gethostbyname(lhost
)) == NULL
)
508 fprintf(stderr
, "gethostbyname: %i\n", hstrerror(h_errno
));
513 memcpy((char *)&sin
.sin_addr
, phe
->h_addr
, phe
->h_length
);
516 sControl
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
523 if (setsockopt(sControl
,SOL_SOCKET
,SO_REUSEADDR
,
524 SETSOCKOPT_OPTVAL_TYPE
&on
, sizeof(on
)) == -1)
527 perror("setsockopt");
531 if (connect(sControl
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1)
538 ctrl
= calloc(1,sizeof(netbuf
));
546 ctrl
->buf
= malloc(FTPLIB_BUFSIZ
);
547 if (ctrl
->buf
== NULL
)
555 ctrl
->respcb
= respcb
;
557 ctrl
->handle
= sControl
;
558 ctrl
->dir
= FTPLIB_CONTROL
;
560 ctrl
->cmode
= FTPLIB_DEFMODE
;
562 ctrl
->idletime
.tv_sec
= ctrl
->idletime
.tv_usec
= 0;
563 ctrl
->idlearg
= NULL
;
567 if (readresp('2', ctrl
) == 0)
578 GLOBALDEF
int FtpSetResponseCallback(FtpResponseCallback respcb
, netbuf
*nControl
)
580 nControl
->respcb
= respcb
;
583 GLOBALDEF
int FtpSetSendCmdCallback(FtpResponseCallback sendcb
, netbuf
*nControl
)
585 nControl
->sendcb
= sendcb
;
589 GLOBALDEF
int FtpSetCallback(const FtpCallbackOptions
*opt
, netbuf
*nControl
)
591 nControl
->idlecb
= opt
->cbFunc
;
592 nControl
->idlearg
= opt
->cbArg
;
593 nControl
->idletime
.tv_sec
= opt
->idleTime
/ 1000;
594 nControl
->idletime
.tv_usec
= (opt
->idleTime
% 1000) * 1000;
595 nControl
->cbbytes
= opt
->bytesXferred
;
598 GLOBALDEF
int FtpClearCallback(netbuf
*nControl
)
600 nControl
->idlecb
= NULL
;
601 nControl
->idlearg
= NULL
;
602 nControl
->idletime
.tv_sec
= 0;
603 nControl
->idletime
.tv_usec
= 0;
604 nControl
->cbbytes
= 0;
608 * FtpOptions - change connection options
610 * returns 1 if successful, 0 on error
612 GLOBALDEF
int FtpOptions(int opt
, long val
, netbuf
*nControl
)
617 case FTPLIB_CONNMODE
:
619 if ((v
== FTPLIB_PASSIVE
) || (v
== FTPLIB_PORT
))
625 case FTPLIB_CALLBACK
:
626 nControl
->idlecb
= (FtpCallback
) val
;
629 case FTPLIB_IDLETIME
:
632 nControl
->idletime
.tv_sec
= v
/ 1000;
633 nControl
->idletime
.tv_usec
= (v
% 1000) * 1000;
635 case FTPLIB_CALLBACKARG
:
637 nControl
->idlearg
= (void *) val
;
639 case FTPLIB_CALLBACKBYTES
:
641 nControl
->cbbytes
= (int) val
;
648 * FtpSendCmd - send a command and wait for expected response
650 * return 1 if proper response received, 0 otherwise
652 static int FtpSendCmd(const char *cmd
, char expresp
, netbuf
*nControl
)
654 char buf
[TMP_BUFSIZ
];
655 if (nControl
->dir
!= FTPLIB_CONTROL
)
657 if (ftplib_debug
> 2)
658 fprintf(stderr
,"%s\n",cmd
);
659 if ((strlen(cmd
) + 3) > sizeof(buf
))
661 sprintf(buf
,"%s\r\n",cmd
);
662 if (nControl
->sendcb
)
663 nControl
->sendcb(nControl
, buf
);
664 if (net_write(nControl
->handle
,buf
,strlen(buf
)) <= 0)
670 return readresp(expresp
, nControl
);
674 * FtpLogin - log in to remote server
676 * return 1 if logged in, 0 otherwise
678 GLOBALDEF
int FtpLogin(const char *user
, const char *pass
, netbuf
*nControl
)
682 if (((strlen(user
) + 7) > sizeof(tempbuf
)) ||
683 ((strlen(pass
) + 7) > sizeof(tempbuf
)))
685 sprintf(tempbuf
,"USER %s",user
);
686 if (!FtpSendCmd(tempbuf
,'3',nControl
))
688 if (nControl
->response
[0] == '2')
692 sprintf(tempbuf
,"PASS %s",pass
);
693 return FtpSendCmd(tempbuf
,'2',nControl
);
697 * FtpOpenPort - set up data connection
699 * return 1 if successful, 0 otherwise
701 static int FtpOpenPort(netbuf
*nControl
, netbuf
**nData
, int mode
, int dir
)
706 struct sockaddr_in in
;
708 struct linger lng
= { 0, 0 };
714 char buf
[TMP_BUFSIZ
];
716 if (nControl
->dir
!= FTPLIB_CONTROL
)
718 if ((dir
!= FTPLIB_READ
) && (dir
!= FTPLIB_WRITE
))
720 sprintf(nControl
->response
, "Invalid direction %d\n", dir
);
723 if ((mode
!= FTPLIB_ASCII
) && (mode
!= FTPLIB_IMAGE
))
725 sprintf(nControl
->response
, "Invalid mode %c\n", mode
);
729 if (nControl
->cmode
== FTPLIB_PASSIVE
)
732 sin
.in
.sin_family
= AF_INET
;
733 if (!FtpSendCmd("PASV",'2',nControl
))
735 cp
= strchr(nControl
->response
,'(');
739 sscanf(cp
,"%u,%u,%u,%u,%u,%u",&v
[2],&v
[3],&v
[4],&v
[5],&v
[0],&v
[1]);
740 sin
.sa
.sa_data
[2] = v
[2];
741 sin
.sa
.sa_data
[3] = v
[3];
742 sin
.sa
.sa_data
[4] = v
[4];
743 sin
.sa
.sa_data
[5] = v
[5];
744 if (sin
.sa
.sa_data
[2] == 0 && sin
.sa
.sa_data
[3] == 0 &&
745 sin
.sa
.sa_data
[4] == 0 && sin
.sa
.sa_data
[5] == 0)
747 sin
.sa
.sa_data
[0] = v
[0];
748 sin
.sa
.sa_data
[1] = v
[1];
752 if (getsockname(nControl
->handle
, &sin
.sa
, &l
) < 0)
755 perror("getsockname");
759 sData
= socket(PF_INET
,SOCK_STREAM
,IPPROTO_TCP
);
766 if (setsockopt(sData
,SOL_SOCKET
,SO_REUSEADDR
,
767 SETSOCKOPT_OPTVAL_TYPE
&on
,sizeof(on
)) == -1)
770 perror("setsockopt");
774 if (setsockopt(sData
,SOL_SOCKET
,SO_LINGER
,
775 SETSOCKOPT_OPTVAL_TYPE
&lng
,sizeof(lng
)) == -1)
778 perror("setsockopt");
782 if (nControl
->cmode
== FTPLIB_PASSIVE
)
784 if (connect(sData
, &sin
.sa
, sizeof(sin
.sa
)) == -1)
795 if (bind(sData
, &sin
.sa
, sizeof(sin
)) == -1)
802 if (listen(sData
, 1) < 0)
809 if (getsockname(sData
, &sin
.sa
, &l
) < 0)
811 sprintf(buf
, "PORT %d,%d,%d,%d,%d,%d",
812 (unsigned char) sin
.sa
.sa_data
[2],
813 (unsigned char) sin
.sa
.sa_data
[3],
814 (unsigned char) sin
.sa
.sa_data
[4],
815 (unsigned char) sin
.sa
.sa_data
[5],
816 (unsigned char) sin
.sa
.sa_data
[0],
817 (unsigned char) sin
.sa
.sa_data
[1]);
818 if (!FtpSendCmd(buf
,'2',nControl
))
824 ctrl
= calloc(1,sizeof(netbuf
));
832 if ((mode
== 'A') && ((ctrl
->buf
= malloc(FTPLIB_BUFSIZ
)) == NULL
))
840 ctrl
->handle
= sData
;
842 ctrl
->idletime
= nControl
->idletime
;
843 ctrl
->idlearg
= nControl
->idlearg
;
846 ctrl
->cbbytes
= nControl
->cbbytes
;
847 if (ctrl
->idletime
.tv_sec
|| ctrl
->idletime
.tv_usec
|| ctrl
->cbbytes
)
848 ctrl
->idlecb
= nControl
->idlecb
;
856 * FtpAcceptConnection - accept connection from server
858 * return 1 if successful, 0 otherwise
860 static int FtpAcceptConnection(netbuf
*nData
, netbuf
*nControl
)
863 struct sockaddr addr
;
871 FD_SET(nControl
->handle
, &mask
);
872 FD_SET(nData
->handle
, &mask
);
874 tv
.tv_sec
= ACCEPT_TIMEOUT
;
875 i
= nControl
->handle
;
876 if (i
< nData
->handle
)
878 i
= select(i
+1, &mask
, NULL
, NULL
, &tv
);
881 strncpy(nControl
->response
, strerror(errno
),
882 sizeof(nControl
->response
));
883 net_close(nData
->handle
);
888 strcpy(nControl
->response
, "timed out waiting for connection");
889 net_close(nData
->handle
);
894 if (FD_ISSET(nData
->handle
, &mask
))
897 sData
= accept(nData
->handle
, &addr
, &l
);
899 net_close(nData
->handle
);
903 nData
->handle
= sData
;
907 strncpy(nControl
->response
, strerror(i
),
908 sizeof(nControl
->response
));
912 else if (FD_ISSET(nControl
->handle
, &mask
))
914 net_close(nData
->handle
);
916 readresp('2', nControl
);
923 * FtpAccess - return a handle for a data stream
925 * return 1 if successful, 0 otherwise
927 GLOBALDEF
int FtpAccess(const char *path
, int typ
, int mode
, netbuf
*nControl
,
930 char buf
[TMP_BUFSIZ
];
932 if ((path
== NULL
) &&
933 ((typ
== FTPLIB_FILE_WRITE
) || (typ
== FTPLIB_FILE_READ
)))
935 sprintf(nControl
->response
,
936 "Missing path argument for file transfer\n");
939 sprintf(buf
, "TYPE %c", mode
);
940 if (!FtpSendCmd(buf
, '2', nControl
))
948 case FTPLIB_DIR_VERBOSE
:
952 case FTPLIB_FILE_READ
:
956 case FTPLIB_FILE_WRITE
:
961 sprintf(nControl
->response
, "Invalid open type %d\n", typ
);
968 if ((strlen(path
) + i
+ 1) >= sizeof(buf
))
970 strcpy(&buf
[i
],path
);
972 if (FtpOpenPort(nControl
, nData
, mode
, dir
) == -1)
974 if (!FtpSendCmd(buf
, '1', nControl
))
980 (*nData
)->ctrl
= nControl
;
981 nControl
->data
= *nData
;
982 if (nControl
->cmode
== FTPLIB_PORT
)
984 if (!FtpAcceptConnection(*nData
,nControl
))
988 nControl
->data
= NULL
;
996 * FtpRead - read from a data connection
998 GLOBALDEF
int FtpRead(void *buf
, int max
, netbuf
*nData
)
1001 if (nData
->dir
!= FTPLIB_READ
)
1004 i
= readline(buf
, max
, nData
);
1007 i
= socket_wait(nData
);
1010 i
= net_read(nData
->handle
, buf
, max
);
1015 if (nData
->idlecb
&& nData
->cbbytes
)
1017 nData
->xfered1
+= i
;
1018 if (nData
->xfered1
> nData
->cbbytes
)
1020 if (nData
->idlecb(nData
, nData
->xfered
, nData
->idlearg
) == 0)
1029 * FtpWrite - write to a data connection
1031 GLOBALDEF
int FtpWrite(const void *buf
, int len
, netbuf
*nData
)
1034 if (nData
->dir
!= FTPLIB_WRITE
)
1037 i
= writeline(buf
, len
, nData
);
1041 i
= net_write(nData
->handle
, buf
, len
);
1046 if (nData
->idlecb
&& nData
->cbbytes
)
1048 nData
->xfered1
+= i
;
1049 if (nData
->xfered1
> nData
->cbbytes
)
1051 nData
->idlecb(nData
, nData
->xfered
, nData
->idlearg
);
1059 * FtpClose - close a data connection
1061 GLOBALDEF
int FtpClose(netbuf
*nData
)
1067 /* potential problem - if buffer flush fails, how to notify user? */
1068 if (nData
->buf
!= NULL
)
1069 writeline(NULL
, 0, nData
);
1073 shutdown(nData
->handle
,2);
1074 net_close(nData
->handle
);
1080 if (ctrl
->response
[0] != '4' && ctrl
->response
[0] != 5)
1081 return readresp('2', ctrl
);
1084 case FTPLIB_CONTROL
:
1088 FtpClose(nData
->data
);
1090 net_close(nData
->handle
);
1098 * FtpSite - send a SITE command
1100 * return 1 if command successful, 0 otherwise
1102 GLOBALDEF
int FtpSite(const char *cmd
, netbuf
*nControl
)
1104 char buf
[TMP_BUFSIZ
];
1106 if ((strlen(cmd
) + 7) > sizeof(buf
))
1108 sprintf(buf
,"SITE %s",cmd
);
1109 if (!FtpSendCmd(buf
,'2',nControl
))
1115 * FtpSysType - send a SYST command
1117 * Fills in the user buffer with the remote system type. If more
1118 * information from the response is required, the user can parse
1119 * it out of the response buffer returned by FtpLastResponse().
1121 * return 1 if command successful, 0 otherwise
1123 GLOBALDEF
int FtpSysType(char *buf
, int max
, netbuf
*nControl
)
1128 if (!FtpSendCmd("SYST",'2',nControl
))
1130 s
= &nControl
->response
[4];
1131 while ((--l
) && (*s
!= ' '))
1138 * FtpMkdir - create a directory at server
1140 * return 1 if successful, 0 otherwise
1142 GLOBALDEF
int FtpMkdir(const char *path
, netbuf
*nControl
)
1144 char buf
[TMP_BUFSIZ
];
1146 if ((strlen(path
) + 6) > sizeof(buf
))
1148 sprintf(buf
,"MKD %s",path
);
1149 if (!FtpSendCmd(buf
,'2', nControl
))
1155 * FtpChdir - change path at remote
1157 * return 1 if successful, 0 otherwise
1159 GLOBALDEF
int FtpChdir(const char *path
, netbuf
*nControl
)
1161 char buf
[TMP_BUFSIZ
];
1163 if ((strlen(path
) + 6) > sizeof(buf
))
1165 sprintf(buf
,"CWD %s",path
);
1166 if (!FtpSendCmd(buf
,'2',nControl
))
1172 * FtpCDUp - move to parent directory at remote
1174 * return 1 if successful, 0 otherwise
1176 GLOBALDEF
int FtpCDUp(netbuf
*nControl
)
1178 if (!FtpSendCmd("CDUP",'2',nControl
))
1184 * FtpRmdir - remove directory at remote
1186 * return 1 if successful, 0 otherwise
1188 GLOBALDEF
int FtpRmdir(const char *path
, netbuf
*nControl
)
1190 char buf
[TMP_BUFSIZ
];
1192 if ((strlen(path
) + 6) > sizeof(buf
))
1194 sprintf(buf
,"RMD %s",path
);
1195 if (!FtpSendCmd(buf
,'2',nControl
))
1201 * FtpPwd - get working directory at remote
1203 * return 1 if successful, 0 otherwise
1205 GLOBALDEF
int FtpPwd(char *path
, int max
, netbuf
*nControl
)
1210 if (!FtpSendCmd("PWD",'2',nControl
))
1212 s
= strchr(nControl
->response
, '"');
1216 while ((--l
) && (*s
) && (*s
!= '"'))
1223 * FtpXfer - issue a command and transfer data
1225 * return 1 if successful, 0 otherwise
1227 static int FtpXfer(FtpDataCallback cb
, const char *path
,
1228 netbuf
*nControl
, int typ
, int mode
)
1235 if (!FtpAccess(path
, typ
, mode
, nControl
, &nData
))
1237 dbuf
= malloc(FTPLIB_BUFSIZ
);
1238 if (typ
== FTPLIB_FILE_WRITE
)
1240 while ((l
= cb(dbuf
, FTPLIB_BUFSIZ
)) > 0)
1242 if ((c
= FtpWrite(dbuf
, l
, nData
)) < l
)
1244 printf("short write: passed %d, wrote %d\n", l
, c
);
1252 while ((l
= FtpRead(dbuf
, FTPLIB_BUFSIZ
, nData
)) > 0)
1254 if (cb(dbuf
, l
) == 0)
1257 perror("localfile write");
1269 * FtpNlst - issue an NLST command and write response to output
1271 * return 1 if successful, 0 otherwise
1273 GLOBALDEF
int FtpNlst(FtpDataCallback cb
, const char *path
,
1276 return FtpXfer(cb
, path
, nControl
, FTPLIB_DIR
, FTPLIB_ASCII
);
1280 * FtpDir - issue a LIST command and write response to output
1282 * return 1 if successful, 0 otherwise
1284 GLOBALDEF
int FtpDir(FtpDataCallback cb
, const char *path
, netbuf
*nControl
)
1286 return FtpXfer(cb
, path
, nControl
, FTPLIB_DIR_VERBOSE
, FTPLIB_ASCII
);
1290 * FtpSize - determine the size of a remote file
1292 * return 1 if successful, 0 otherwise
1294 GLOBALDEF
int FtpSize(const char *path
, unsigned int *size
, char mode
, netbuf
*nControl
)
1296 char cmd
[TMP_BUFSIZ
];
1300 if ((strlen(path
) + 7) > sizeof(cmd
))
1302 sprintf(cmd
, "TYPE %c", mode
);
1303 if (!FtpSendCmd(cmd
, '2', nControl
))
1305 sprintf(cmd
,"SIZE %s",path
);
1306 if (!FtpSendCmd(cmd
,'2',nControl
))
1310 if (sscanf(nControl
->response
, "%d %u", &resp
, &sz
) == 2)
1318 #if defined(__UINT64_MAX)
1320 * FtpSizeLong - determine the size of a remote file
1322 * return 1 if successful, 0 otherwise
1324 GLOBALDEF
int FtpSizeLong(const char *path
, fsz_t
*size
, char mode
, netbuf
*nControl
)
1326 char cmd
[TMP_BUFSIZ
];
1330 if ((strlen(path
) + 7) > sizeof(cmd
))
1332 sprintf(cmd
, "TYPE %c", mode
);
1333 if (!FtpSendCmd(cmd
, '2', nControl
))
1335 sprintf(cmd
,"SIZE %s",path
);
1336 if (!FtpSendCmd(cmd
,'2',nControl
))
1340 if (sscanf(nControl
->response
, "%d %" PRIu64
"", &resp
, &sz
) == 2)
1350 * FtpModDate - determine the modification date of a remote file
1352 * return 1 if successful, 0 otherwise
1354 GLOBALDEF
int FtpModDate(const char *path
, char *dt
, int max
, netbuf
*nControl
)
1356 char buf
[TMP_BUFSIZ
];
1359 if ((strlen(path
) + 7) > sizeof(buf
))
1361 sprintf(buf
,"MDTM %s",path
);
1362 if (!FtpSendCmd(buf
,'2',nControl
))
1365 strncpy(dt
, &nControl
->response
[4], max
);
1370 * FtpGet - issue a GET command and write received data to output
1372 * return 1 if successful, 0 otherwise
1374 GLOBALDEF
int FtpGet(FtpDataCallback cb
, const char *path
,
1375 char mode
, netbuf
*nControl
)
1377 return FtpXfer(cb
, path
, nControl
, FTPLIB_FILE_READ
, mode
);
1381 * FtpPut - issue a PUT command and send data from input
1383 * return 1 if successful, 0 otherwise
1385 GLOBALDEF
int FtpPut(FtpDataCallback cb
, const char *path
, char mode
,
1388 return FtpXfer(cb
, path
, nControl
, FTPLIB_FILE_WRITE
, mode
);
1392 * FtpRename - rename a file at remote
1394 * return 1 if successful, 0 otherwise
1396 GLOBALDEF
int FtpRename(const char *src
, const char *dst
, netbuf
*nControl
)
1398 char cmd
[TMP_BUFSIZ
];
1400 if (((strlen(src
) + 7) > sizeof(cmd
)) ||
1401 ((strlen(dst
) + 7) > sizeof(cmd
)))
1403 sprintf(cmd
,"RNFR %s",src
);
1404 if (!FtpSendCmd(cmd
,'3',nControl
))
1406 sprintf(cmd
,"RNTO %s",dst
);
1407 if (!FtpSendCmd(cmd
,'2',nControl
))
1413 * FtpDelete - delete a file at remote
1415 * return 1 if successful, 0 otherwise
1417 GLOBALDEF
int FtpDelete(const char *fnm
, netbuf
*nControl
)
1419 char cmd
[TMP_BUFSIZ
];
1421 if ((strlen(fnm
) + 7) > sizeof(cmd
))
1423 sprintf(cmd
,"DELE %s",fnm
);
1424 if (!FtpSendCmd(cmd
,'2', nControl
))
1430 * FtpQuit - disconnect from remote
1432 * return 1 if successful, 0 otherwise
1434 GLOBALDEF
int FtpQuit(netbuf
*nControl
)
1436 if (nControl
->dir
!= FTPLIB_CONTROL
)
1438 FtpSendCmd("QUIT",'2',nControl
);
1439 net_close(nControl
->handle
);
1440 free(nControl
->buf
);