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