Annotation of embedaddon/php/ext/ftp/ftp.c, revision 1.1.1.5
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.5 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 6: +----------------------------------------------------------------------+
7: | This source file is subject to version 3.01 of the PHP license, |
8: | that is bundled with this package in the file LICENSE, and is |
9: | available through the world-wide-web at the following url: |
10: | http://www.php.net/license/3_01.txt |
11: | If you did not receive a copy of the PHP license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@php.net so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
15: | Authors: Andrew Skalski <askalski@chek.com> |
16: | Stefan Esser <sesser@php.net> (resume functions) |
17: +----------------------------------------------------------------------+
18: */
19:
1.1.1.2 misho 20: /* $Id$ */
1.1 misho 21:
22: #ifdef HAVE_CONFIG_H
23: #include "config.h"
24: #endif
25:
26: #include "php.h"
27:
28: #if HAVE_FTP
29:
30: #include <stdio.h>
31: #include <ctype.h>
32: #include <stdlib.h>
33: #ifdef HAVE_UNISTD_H
34: #include <unistd.h>
35: #endif
36: #include <fcntl.h>
37: #include <string.h>
38: #include <time.h>
39: #ifdef PHP_WIN32
40: #include <winsock2.h>
41: #elif defined(NETWARE)
1.1.1.4 misho 42: #ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), at least for now */
1.1 misho 43: #include <novsock2.h>
44: #else
45: #include <sys/socket.h>
46: #include <netinet/in.h>
47: #include <netdb.h>
48: #endif
49: #else
50: #ifdef HAVE_SYS_TYPES_H
51: #include <sys/types.h>
52: #endif
53: #include <sys/socket.h>
54: #include <netinet/in.h>
55: #include <arpa/inet.h>
56: #include <netdb.h>
57: #endif
58: #include <errno.h>
59:
60: #if HAVE_SYS_TIME_H
61: #include <sys/time.h>
62: #endif
63:
64: #ifdef HAVE_SYS_SELECT_H
65: #include <sys/select.h>
66: #endif
67:
68: #if HAVE_OPENSSL_EXT
69: #include <openssl/ssl.h>
70: #endif
71:
72: #include "ftp.h"
73: #include "ext/standard/fsock.h"
74:
75: /* Additional headers for NetWare */
76: #if defined(NETWARE) && !defined(USE_WINSOCK)
77: #include <sys/select.h>
78: #endif
79:
80: /* sends an ftp command, returns true on success, false on error.
81: * it sends the string "cmd args\r\n" if args is non-null, or
82: * "cmd\r\n" if args is null
83: */
84: static int ftp_putcmd( ftpbuf_t *ftp,
85: const char *cmd,
86: const char *args);
87:
88: /* wrapper around send/recv to handle timeouts */
89: static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
90: static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
91: static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
92:
93: /* reads a line the socket , returns true on success, false on error */
94: static int ftp_readline(ftpbuf_t *ftp);
95:
96: /* reads an ftp response, returns true on success, false on error */
97: static int ftp_getresp(ftpbuf_t *ftp);
98:
99: /* sets the ftp transfer type */
100: static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
101:
102: /* opens up a data stream */
103: static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);
104:
105: /* accepts the data connection, returns updated data buffer */
106: static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC);
107:
108: /* closes the data connection, returns NULL */
109: static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
110:
111: /* generic file lister */
112: static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);
113:
114: /* IP and port conversion box */
115: union ipbox {
116: struct in_addr ia[2];
117: unsigned short s[4];
118: unsigned char c[8];
119: };
120:
121: /* {{{ ftp_open
122: */
123: ftpbuf_t*
124: ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
125: {
126: ftpbuf_t *ftp;
127: socklen_t size;
128: struct timeval tv;
129:
130:
131: /* alloc the ftp structure */
132: ftp = ecalloc(1, sizeof(*ftp));
133:
134: tv.tv_sec = timeout_sec;
135: tv.tv_usec = 0;
136:
137: ftp->fd = php_network_connect_socket_to_host(host,
138: (unsigned short) (port ? port : 21), SOCK_STREAM,
139: 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
140: if (ftp->fd == -1) {
141: goto bail;
142: }
143:
144: /* Default Settings */
145: ftp->timeout_sec = timeout_sec;
146: ftp->nb = 0;
147:
148: size = sizeof(ftp->localaddr);
149: memset(&ftp->localaddr, 0, size);
150: if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
151: php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
152: goto bail;
153: }
154:
155: if (!ftp_getresp(ftp) || ftp->resp != 220) {
156: goto bail;
157: }
158:
159: return ftp;
160:
161: bail:
162: if (ftp->fd != -1) {
163: closesocket(ftp->fd);
164: }
165: efree(ftp);
166: return NULL;
167: }
168: /* }}} */
169:
170: /* {{{ ftp_close
171: */
172: ftpbuf_t*
173: ftp_close(ftpbuf_t *ftp)
174: {
175: if (ftp == NULL) {
176: return NULL;
177: }
178: if (ftp->data) {
179: data_close(ftp, ftp->data);
180: }
1.1.1.5 ! misho 181: if (ftp->stream && ftp->closestream) {
! 182: TSRMLS_FETCH();
! 183: php_stream_close(ftp->stream);
! 184: }
1.1 misho 185: if (ftp->fd != -1) {
186: #if HAVE_OPENSSL_EXT
187: if (ftp->ssl_active) {
188: SSL_shutdown(ftp->ssl_handle);
1.1.1.4 misho 189: SSL_free(ftp->ssl_handle);
1.1 misho 190: }
191: #endif
192: closesocket(ftp->fd);
193: }
194: ftp_gc(ftp);
195: efree(ftp);
196: return NULL;
197: }
198: /* }}} */
199:
200: /* {{{ ftp_gc
201: */
202: void
203: ftp_gc(ftpbuf_t *ftp)
204: {
205: if (ftp == NULL) {
206: return;
207: }
208: if (ftp->pwd) {
209: efree(ftp->pwd);
210: ftp->pwd = NULL;
211: }
212: if (ftp->syst) {
213: efree(ftp->syst);
214: ftp->syst = NULL;
215: }
216: }
217: /* }}} */
218:
219: /* {{{ ftp_quit
220: */
221: int
222: ftp_quit(ftpbuf_t *ftp)
223: {
224: if (ftp == NULL) {
225: return 0;
226: }
227:
228: if (!ftp_putcmd(ftp, "QUIT", NULL)) {
229: return 0;
230: }
231: if (!ftp_getresp(ftp) || ftp->resp != 221) {
232: return 0;
233: }
234:
235: if (ftp->pwd) {
236: efree(ftp->pwd);
237: ftp->pwd = NULL;
238: }
239:
240: return 1;
241: }
242: /* }}} */
243:
244: /* {{{ ftp_login
245: */
246: int
247: ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
248: {
249: #if HAVE_OPENSSL_EXT
250: SSL_CTX *ctx = NULL;
1.1.1.2 misho 251: long ssl_ctx_options = SSL_OP_ALL;
1.1 misho 252: #endif
253: if (ftp == NULL) {
254: return 0;
255: }
256:
257: #if HAVE_OPENSSL_EXT
258: if (ftp->use_ssl && !ftp->ssl_active) {
259: if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
260: return 0;
261: }
262: if (!ftp_getresp(ftp)) {
263: return 0;
264: }
265:
266: if (ftp->resp != 234) {
267: if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
268: return 0;
269: }
270: if (!ftp_getresp(ftp)) {
271: return 0;
272: }
273:
274: if (ftp->resp != 334) {
275: return 0;
276: } else {
277: ftp->old_ssl = 1;
278: ftp->use_ssl_for_data = 1;
279: }
280: }
281:
282: ctx = SSL_CTX_new(SSLv23_client_method());
283: if (ctx == NULL) {
284: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
285: return 0;
286: }
287:
1.1.1.2 misho 288: #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
289: ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
290: #endif
291: SSL_CTX_set_options(ctx, ssl_ctx_options);
1.1 misho 292:
293: ftp->ssl_handle = SSL_new(ctx);
294: if (ftp->ssl_handle == NULL) {
295: php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
296: SSL_CTX_free(ctx);
297: return 0;
298: }
299:
300: SSL_set_fd(ftp->ssl_handle, ftp->fd);
301:
302: if (SSL_connect(ftp->ssl_handle) <= 0) {
303: php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
304: SSL_shutdown(ftp->ssl_handle);
1.1.1.4 misho 305: SSL_free(ftp->ssl_handle);
1.1 misho 306: return 0;
307: }
308:
309: ftp->ssl_active = 1;
310:
311: if (!ftp->old_ssl) {
312:
313: /* set protection buffersize to zero */
314: if (!ftp_putcmd(ftp, "PBSZ", "0")) {
315: return 0;
316: }
317: if (!ftp_getresp(ftp)) {
318: return 0;
319: }
320:
321: /* enable data conn encryption */
322: if (!ftp_putcmd(ftp, "PROT", "P")) {
323: return 0;
324: }
325: if (!ftp_getresp(ftp)) {
326: return 0;
327: }
328:
329: ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
330: }
331: }
332: #endif
333:
334: if (!ftp_putcmd(ftp, "USER", user)) {
335: return 0;
336: }
337: if (!ftp_getresp(ftp)) {
338: return 0;
339: }
340: if (ftp->resp == 230) {
341: return 1;
342: }
343: if (ftp->resp != 331) {
344: return 0;
345: }
346: if (!ftp_putcmd(ftp, "PASS", pass)) {
347: return 0;
348: }
349: if (!ftp_getresp(ftp)) {
350: return 0;
351: }
352: return (ftp->resp == 230);
353: }
354: /* }}} */
355:
356: /* {{{ ftp_reinit
357: */
358: int
359: ftp_reinit(ftpbuf_t *ftp)
360: {
361: if (ftp == NULL) {
362: return 0;
363: }
364:
365: ftp_gc(ftp);
366:
367: ftp->nb = 0;
368:
369: if (!ftp_putcmd(ftp, "REIN", NULL)) {
370: return 0;
371: }
372: if (!ftp_getresp(ftp) || ftp->resp != 220) {
373: return 0;
374: }
375:
376: return 1;
377: }
378: /* }}} */
379:
380: /* {{{ ftp_syst
381: */
382: const char*
383: ftp_syst(ftpbuf_t *ftp)
384: {
385: char *syst, *end;
386:
387: if (ftp == NULL) {
388: return NULL;
389: }
390:
391: /* default to cached value */
392: if (ftp->syst) {
393: return ftp->syst;
394: }
395: if (!ftp_putcmd(ftp, "SYST", NULL)) {
396: return NULL;
397: }
398: if (!ftp_getresp(ftp) || ftp->resp != 215) {
399: return NULL;
400: }
401: syst = ftp->inbuf;
402: while (*syst == ' ') {
403: syst++;
404: }
405: if ((end = strchr(syst, ' '))) {
406: *end = 0;
407: }
408: ftp->syst = estrdup(syst);
409: if (end) {
410: *end = ' ';
411: }
412: return ftp->syst;
413: }
414: /* }}} */
415:
416: /* {{{ ftp_pwd
417: */
418: const char*
419: ftp_pwd(ftpbuf_t *ftp)
420: {
421: char *pwd, *end;
422:
423: if (ftp == NULL) {
424: return NULL;
425: }
426:
427: /* default to cached value */
428: if (ftp->pwd) {
429: return ftp->pwd;
430: }
431: if (!ftp_putcmd(ftp, "PWD", NULL)) {
432: return NULL;
433: }
434: if (!ftp_getresp(ftp) || ftp->resp != 257) {
435: return NULL;
436: }
437: /* copy out the pwd from response */
438: if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
439: return NULL;
440: }
441: if ((end = strrchr(++pwd, '"')) == NULL) {
442: return NULL;
443: }
444: ftp->pwd = estrndup(pwd, end - pwd);
445:
446: return ftp->pwd;
447: }
448: /* }}} */
449:
450: /* {{{ ftp_exec
451: */
452: int
453: ftp_exec(ftpbuf_t *ftp, const char *cmd)
454: {
455: if (ftp == NULL) {
456: return 0;
457: }
458: if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
459: return 0;
460: }
461: if (!ftp_getresp(ftp) || ftp->resp != 200) {
462: return 0;
463: }
464:
465: return 1;
466: }
467: /* }}} */
468:
469: /* {{{ ftp_raw
470: */
471: void
472: ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
473: {
474: if (ftp == NULL || cmd == NULL) {
475: RETURN_NULL();
476: }
477: if (!ftp_putcmd(ftp, cmd, NULL)) {
478: RETURN_NULL();
479: }
480: array_init(return_value);
481: while (ftp_readline(ftp)) {
482: add_next_index_string(return_value, ftp->inbuf, 1);
483: if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
484: return;
485: }
486: }
487: }
488: /* }}} */
489:
490: /* {{{ ftp_chdir
491: */
492: int
493: ftp_chdir(ftpbuf_t *ftp, const char *dir)
494: {
495: if (ftp == NULL) {
496: return 0;
497: }
498:
499: if (ftp->pwd) {
500: efree(ftp->pwd);
501: ftp->pwd = NULL;
502: }
503:
504: if (!ftp_putcmd(ftp, "CWD", dir)) {
505: return 0;
506: }
507: if (!ftp_getresp(ftp) || ftp->resp != 250) {
508: return 0;
509: }
510: return 1;
511: }
512: /* }}} */
513:
514: /* {{{ ftp_cdup
515: */
516: int
517: ftp_cdup(ftpbuf_t *ftp)
518: {
519: if (ftp == NULL) {
520: return 0;
521: }
522:
523: if (ftp->pwd) {
524: efree(ftp->pwd);
525: ftp->pwd = NULL;
526: }
527:
528: if (!ftp_putcmd(ftp, "CDUP", NULL)) {
529: return 0;
530: }
531: if (!ftp_getresp(ftp) || ftp->resp != 250) {
532: return 0;
533: }
534: return 1;
535: }
536: /* }}} */
537:
538: /* {{{ ftp_mkdir
539: */
540: char*
541: ftp_mkdir(ftpbuf_t *ftp, const char *dir)
542: {
543: char *mkd, *end;
544:
545: if (ftp == NULL) {
546: return NULL;
547: }
548: if (!ftp_putcmd(ftp, "MKD", dir)) {
549: return NULL;
550: }
551: if (!ftp_getresp(ftp) || ftp->resp != 257) {
552: return NULL;
553: }
554: /* copy out the dir from response */
555: if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
556: mkd = estrdup(dir);
557: return mkd;
558: }
559: if ((end = strrchr(++mkd, '"')) == NULL) {
560: return NULL;
561: }
562: *end = 0;
563: mkd = estrdup(mkd);
564: *end = '"';
565:
566: return mkd;
567: }
568: /* }}} */
569:
570: /* {{{ ftp_rmdir
571: */
572: int
573: ftp_rmdir(ftpbuf_t *ftp, const char *dir)
574: {
575: if (ftp == NULL) {
576: return 0;
577: }
578: if (!ftp_putcmd(ftp, "RMD", dir)) {
579: return 0;
580: }
581: if (!ftp_getresp(ftp) || ftp->resp != 250) {
582: return 0;
583: }
584: return 1;
585: }
586: /* }}} */
587:
588: /* {{{ ftp_chmod
589: */
590: int
591: ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
592: {
593: char *buffer;
594:
595: if (ftp == NULL || filename_len <= 0) {
596: return 0;
597: }
598:
599: spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
600:
601: if (!ftp_putcmd(ftp, "SITE", buffer)) {
602: efree(buffer);
603: return 0;
604: }
605:
606: efree(buffer);
607:
608: if (!ftp_getresp(ftp) || ftp->resp != 200) {
609: return 0;
610: }
611:
612: return 1;
613: }
614: /* }}} */
615:
616: /* {{{ ftp_alloc
617: */
618: int
1.1.1.4 misho 619: ftp_alloc(ftpbuf_t *ftp, const long size, char **response)
1.1 misho 620: {
621: char buffer[64];
622:
623: if (ftp == NULL || size <= 0) {
624: return 0;
625: }
626:
1.1.1.4 misho 627: snprintf(buffer, sizeof(buffer) - 1, "%ld", size);
628:
1.1 misho 629: if (!ftp_putcmd(ftp, "ALLO", buffer)) {
630: return 0;
631: }
632:
633: if (!ftp_getresp(ftp)) {
634: return 0;
635: }
636:
1.1.1.5 ! misho 637: if (response) {
1.1 misho 638: *response = estrdup(ftp->inbuf);
639: }
640:
641: if (ftp->resp < 200 || ftp->resp >= 300) {
642: return 0;
643: }
644:
645: return 1;
646: }
647: /* }}} */
648:
649: /* {{{ ftp_nlist
650: */
651: char**
652: ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
653: {
654: return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
655: }
656: /* }}} */
657:
658: /* {{{ ftp_list
659: */
660: char**
661: ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
662: {
663: return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
664: }
665: /* }}} */
666:
667: /* {{{ ftp_type
668: */
669: int
670: ftp_type(ftpbuf_t *ftp, ftptype_t type)
671: {
672: char typechar[2] = "?";
673:
674: if (ftp == NULL) {
675: return 0;
676: }
677: if (type == ftp->type) {
678: return 1;
679: }
680: if (type == FTPTYPE_ASCII) {
681: typechar[0] = 'A';
682: } else if (type == FTPTYPE_IMAGE) {
683: typechar[0] = 'I';
684: } else {
685: return 0;
686: }
687: if (!ftp_putcmd(ftp, "TYPE", typechar)) {
688: return 0;
689: }
690: if (!ftp_getresp(ftp) || ftp->resp != 200) {
691: return 0;
692: }
693: ftp->type = type;
694:
695: return 1;
696: }
697: /* }}} */
698:
699: /* {{{ ftp_pasv
700: */
701: int
702: ftp_pasv(ftpbuf_t *ftp, int pasv)
703: {
704: char *ptr;
705: union ipbox ipbox;
706: unsigned long b[6];
707: socklen_t n;
708: struct sockaddr *sa;
709: struct sockaddr_in *sin;
710:
711: if (ftp == NULL) {
712: return 0;
713: }
714: if (pasv && ftp->pasv == 2) {
715: return 1;
716: }
717: ftp->pasv = 0;
718: if (!pasv) {
719: return 1;
720: }
721: n = sizeof(ftp->pasvaddr);
722: memset(&ftp->pasvaddr, 0, n);
723: sa = (struct sockaddr *) &ftp->pasvaddr;
724:
725: #if HAVE_IPV6
726: if (getpeername(ftp->fd, sa, &n) < 0) {
727: return 0;
728: }
729: if (sa->sa_family == AF_INET6) {
730: struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
731: char *endptr, delimiter;
732:
733: /* try EPSV first */
734: if (!ftp_putcmd(ftp, "EPSV", NULL)) {
735: return 0;
736: }
737: if (!ftp_getresp(ftp)) {
738: return 0;
739: }
740: if (ftp->resp == 229) {
741: /* parse out the port */
742: for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
743: if (!*ptr) {
744: return 0;
745: }
746: delimiter = *++ptr;
747: for (n = 0; *ptr && n < 3; ptr++) {
748: if (*ptr == delimiter) {
749: n++;
750: }
751: }
752:
753: sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
754: if (ptr == endptr || *endptr != delimiter) {
755: return 0;
756: }
757: ftp->pasv = 2;
758: return 1;
759: }
760: }
761:
762: /* fall back to PASV */
763: #endif
764:
765: if (!ftp_putcmd(ftp, "PASV", NULL)) {
766: return 0;
767: }
768: if (!ftp_getresp(ftp) || ftp->resp != 227) {
769: return 0;
770: }
771: /* parse out the IP and port */
772: for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
773: n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
774: if (n != 6) {
775: return 0;
776: }
777: for (n = 0; n < 6; n++) {
778: ipbox.c[n] = (unsigned char) b[n];
779: }
780: sin = (struct sockaddr_in *) sa;
781: sin->sin_family = AF_INET;
782: sin->sin_addr = ipbox.ia[0];
783: sin->sin_port = ipbox.s[2];
784:
785: ftp->pasv = 2;
786:
787: return 1;
788: }
789: /* }}} */
790:
791: /* {{{ ftp_get
792: */
793: int
1.1.1.4 misho 794: ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
1.1 misho 795: {
796: databuf_t *data = NULL;
797: int lastch;
798: size_t rcvd;
799: char arg[11];
800:
801: if (ftp == NULL) {
802: return 0;
803: }
804: if (!ftp_type(ftp, type)) {
805: goto bail;
806: }
807:
808: if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
809: goto bail;
810: }
811:
812: ftp->data = data;
813:
814: if (resumepos > 0) {
1.1.1.4 misho 815: snprintf(arg, sizeof(arg), "%ld", resumepos);
1.1 misho 816: if (!ftp_putcmd(ftp, "REST", arg)) {
817: goto bail;
818: }
819: if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
820: goto bail;
821: }
822: }
823:
824: if (!ftp_putcmd(ftp, "RETR", path)) {
825: goto bail;
826: }
827: if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
828: goto bail;
829: }
830:
831: if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
832: goto bail;
833: }
834:
835: lastch = 0;
836: while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
837: if (rcvd == -1) {
838: goto bail;
839: }
840:
841: if (type == FTPTYPE_ASCII) {
842: #ifndef PHP_WIN32
843: char *s;
844: #endif
845: char *ptr = data->buf;
846: char *e = ptr + rcvd;
847: /* logic depends on the OS EOL
848: * Win32 -> \r\n
849: * Everything Else \n
850: */
851: #ifdef PHP_WIN32
852: php_stream_write(outstream, ptr, (e - ptr));
853: ptr = e;
854: #else
855: while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
856: php_stream_write(outstream, ptr, (s - ptr));
857: if (*(s + 1) == '\n') {
858: s++;
859: php_stream_putc(outstream, '\n');
860: }
861: ptr = s + 1;
862: }
863: #endif
864: if (ptr < e) {
865: php_stream_write(outstream, ptr, (e - ptr));
866: }
867: } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
868: goto bail;
869: }
870: }
871:
872: ftp->data = data = data_close(ftp, data);
873:
874: if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
875: goto bail;
876: }
877:
878: return 1;
879: bail:
880: ftp->data = data_close(ftp, data);
881: return 0;
882: }
883: /* }}} */
884:
885: /* {{{ ftp_put
886: */
887: int
1.1.1.4 misho 888: ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
1.1 misho 889: {
890: databuf_t *data = NULL;
1.1.1.4 misho 891: long size;
1.1 misho 892: char *ptr;
893: int ch;
894: char arg[11];
895:
896: if (ftp == NULL) {
897: return 0;
898: }
899: if (!ftp_type(ftp, type)) {
900: goto bail;
901: }
902: if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
903: goto bail;
904: }
905: ftp->data = data;
906:
907: if (startpos > 0) {
1.1.1.4 misho 908: snprintf(arg, sizeof(arg), "%ld", startpos);
1.1 misho 909: if (!ftp_putcmd(ftp, "REST", arg)) {
910: goto bail;
911: }
912: if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
913: goto bail;
914: }
915: }
916:
917: if (!ftp_putcmd(ftp, "STOR", path)) {
918: goto bail;
919: }
920: if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
921: goto bail;
922: }
923: if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
924: goto bail;
925: }
926:
927: size = 0;
928: ptr = data->buf;
929: while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
930: /* flush if necessary */
931: if (FTP_BUFSIZE - size < 2) {
932: if (my_send(ftp, data->fd, data->buf, size) != size) {
933: goto bail;
934: }
935: ptr = data->buf;
936: size = 0;
937: }
938:
939: if (ch == '\n' && type == FTPTYPE_ASCII) {
940: *ptr++ = '\r';
941: size++;
942: }
943:
944: *ptr++ = ch;
945: size++;
946: }
947:
948: if (size && my_send(ftp, data->fd, data->buf, size) != size) {
949: goto bail;
950: }
951: ftp->data = data = data_close(ftp, data);
952:
953: if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
954: goto bail;
955: }
956: return 1;
957: bail:
958: ftp->data = data_close(ftp, data);
959: return 0;
960: }
961: /* }}} */
962:
963: /* {{{ ftp_size
964: */
1.1.1.4 misho 965: long
1.1 misho 966: ftp_size(ftpbuf_t *ftp, const char *path)
967: {
968: if (ftp == NULL) {
969: return -1;
970: }
971: if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
972: return -1;
973: }
974: if (!ftp_putcmd(ftp, "SIZE", path)) {
975: return -1;
976: }
977: if (!ftp_getresp(ftp) || ftp->resp != 213) {
978: return -1;
979: }
1.1.1.4 misho 980: return atol(ftp->inbuf);
1.1 misho 981: }
982: /* }}} */
983:
984: /* {{{ ftp_mdtm
985: */
986: time_t
987: ftp_mdtm(ftpbuf_t *ftp, const char *path)
988: {
989: time_t stamp;
990: struct tm *gmt, tmbuf;
991: struct tm tm;
992: char *ptr;
993: int n;
994:
995: if (ftp == NULL) {
996: return -1;
997: }
998: if (!ftp_putcmd(ftp, "MDTM", path)) {
999: return -1;
1000: }
1001: if (!ftp_getresp(ftp) || ftp->resp != 213) {
1002: return -1;
1003: }
1004: /* parse out the timestamp */
1005: for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
1006: n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
1007: if (n != 6) {
1008: return -1;
1009: }
1010: tm.tm_year -= 1900;
1011: tm.tm_mon--;
1012: tm.tm_isdst = -1;
1013:
1014: /* figure out the GMT offset */
1015: stamp = time(NULL);
1016: gmt = php_gmtime_r(&stamp, &tmbuf);
1017: if (!gmt) {
1018: return -1;
1019: }
1020: gmt->tm_isdst = -1;
1021:
1022: /* apply the GMT offset */
1023: tm.tm_sec += stamp - mktime(gmt);
1024: tm.tm_isdst = gmt->tm_isdst;
1025:
1026: stamp = mktime(&tm);
1027:
1028: return stamp;
1029: }
1030: /* }}} */
1031:
1032: /* {{{ ftp_delete
1033: */
1034: int
1035: ftp_delete(ftpbuf_t *ftp, const char *path)
1036: {
1037: if (ftp == NULL) {
1038: return 0;
1039: }
1040: if (!ftp_putcmd(ftp, "DELE", path)) {
1041: return 0;
1042: }
1043: if (!ftp_getresp(ftp) || ftp->resp != 250) {
1044: return 0;
1045: }
1046:
1047: return 1;
1048: }
1049: /* }}} */
1050:
1051: /* {{{ ftp_rename
1052: */
1053: int
1054: ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
1055: {
1056: if (ftp == NULL) {
1057: return 0;
1058: }
1059: if (!ftp_putcmd(ftp, "RNFR", src)) {
1060: return 0;
1061: }
1062: if (!ftp_getresp(ftp) || ftp->resp != 350) {
1063: return 0;
1064: }
1065: if (!ftp_putcmd(ftp, "RNTO", dest)) {
1066: return 0;
1067: }
1068: if (!ftp_getresp(ftp) || ftp->resp != 250) {
1069: return 0;
1070: }
1071: return 1;
1072: }
1073: /* }}} */
1074:
1075: /* {{{ ftp_site
1076: */
1077: int
1078: ftp_site(ftpbuf_t *ftp, const char *cmd)
1079: {
1080: if (ftp == NULL) {
1081: return 0;
1082: }
1083: if (!ftp_putcmd(ftp, "SITE", cmd)) {
1084: return 0;
1085: }
1086: if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
1087: return 0;
1088: }
1089:
1090: return 1;
1091: }
1092: /* }}} */
1093:
1094: /* static functions */
1095:
1096: /* {{{ ftp_putcmd
1097: */
1098: int
1099: ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
1100: {
1101: int size;
1102: char *data;
1103:
1104: if (strpbrk(cmd, "\r\n")) {
1105: return 0;
1106: }
1107: /* build the output buffer */
1108: if (args && args[0]) {
1109: /* "cmd args\r\n\0" */
1110: if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
1111: return 0;
1112: }
1113: if (strpbrk(args, "\r\n")) {
1114: return 0;
1115: }
1116: size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
1117: } else {
1118: /* "cmd\r\n\0" */
1119: if (strlen(cmd) + 3 > FTP_BUFSIZE) {
1120: return 0;
1121: }
1122: size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
1123: }
1124:
1125: data = ftp->outbuf;
1126:
1127: /* Clear the extra-lines buffer */
1128: ftp->extra = NULL;
1129:
1130: if (my_send(ftp, ftp->fd, data, size) != size) {
1131: return 0;
1132: }
1133: return 1;
1134: }
1135: /* }}} */
1136:
1137: /* {{{ ftp_readline
1138: */
1139: int
1140: ftp_readline(ftpbuf_t *ftp)
1141: {
1.1.1.4 misho 1142: long size, rcvd;
1.1 misho 1143: char *data, *eol;
1144:
1145: /* shift the extra to the front */
1146: size = FTP_BUFSIZE;
1147: rcvd = 0;
1148: if (ftp->extra) {
1149: memmove(ftp->inbuf, ftp->extra, ftp->extralen);
1150: rcvd = ftp->extralen;
1151: }
1152:
1153: data = ftp->inbuf;
1154:
1155: do {
1156: size -= rcvd;
1157: for (eol = data; rcvd; rcvd--, eol++) {
1158: if (*eol == '\r') {
1159: *eol = 0;
1160: ftp->extra = eol + 1;
1161: if (rcvd > 1 && *(eol + 1) == '\n') {
1162: ftp->extra++;
1163: rcvd--;
1164: }
1165: if ((ftp->extralen = --rcvd) == 0) {
1166: ftp->extra = NULL;
1167: }
1168: return 1;
1169: } else if (*eol == '\n') {
1170: *eol = 0;
1171: ftp->extra = eol + 1;
1172: if ((ftp->extralen = --rcvd) == 0) {
1173: ftp->extra = NULL;
1174: }
1175: return 1;
1176: }
1177: }
1178:
1179: data = eol;
1180: if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
1181: return 0;
1182: }
1183: } while (size);
1184:
1185: return 0;
1186: }
1187: /* }}} */
1188:
1189: /* {{{ ftp_getresp
1190: */
1191: int
1192: ftp_getresp(ftpbuf_t *ftp)
1193: {
1194: char *buf;
1195:
1196: if (ftp == NULL) {
1197: return 0;
1198: }
1199: buf = ftp->inbuf;
1200: ftp->resp = 0;
1201:
1202: while (1) {
1203:
1204: if (!ftp_readline(ftp)) {
1205: return 0;
1206: }
1207:
1208: /* Break out when the end-tag is found */
1209: if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
1210: break;
1211: }
1212: }
1213:
1214: /* translate the tag */
1215: if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
1216: return 0;
1217: }
1218:
1219: ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
1220:
1221: memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
1222:
1223: if (ftp->extra) {
1224: ftp->extra -= 4;
1225: }
1226: return 1;
1227: }
1228: /* }}} */
1229:
1230: /* {{{ my_send
1231: */
1232: int
1233: my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1234: {
1.1.1.4 misho 1235: long size, sent;
1236: int n;
1.1 misho 1237:
1238: size = len;
1239: while (size) {
1240: n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
1241:
1242: if (n < 1) {
1243:
1244: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1245: if (n == 0) {
1246: errno = ETIMEDOUT;
1247: }
1248: #endif
1249: return -1;
1250: }
1251:
1252: #if HAVE_OPENSSL_EXT
1253: if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1254: sent = SSL_write(ftp->ssl_handle, buf, size);
1255: } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1256: sent = SSL_write(ftp->data->ssl_handle, buf, size);
1257: } else {
1258: #endif
1259: sent = send(s, buf, size, 0);
1260: #if HAVE_OPENSSL_EXT
1261: }
1262: #endif
1263: if (sent == -1) {
1264: return -1;
1265: }
1266:
1267: buf = (char*) buf + sent;
1268: size -= sent;
1269: }
1270:
1271: return len;
1272: }
1273: /* }}} */
1274:
1275: /* {{{ my_recv
1276: */
1277: int
1278: my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1279: {
1280: int n, nr_bytes;
1281:
1282: n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1283: if (n < 1) {
1284: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1285: if (n == 0) {
1286: errno = ETIMEDOUT;
1287: }
1288: #endif
1289: return -1;
1290: }
1291:
1292: #if HAVE_OPENSSL_EXT
1293: if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1294: nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
1295: } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1296: nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
1297: } else {
1298: #endif
1299: nr_bytes = recv(s, buf, len, 0);
1300: #if HAVE_OPENSSL_EXT
1301: }
1302: #endif
1303: return (nr_bytes);
1304: }
1305: /* }}} */
1306:
1307: /* {{{ data_available
1308: */
1309: int
1310: data_available(ftpbuf_t *ftp, php_socket_t s)
1311: {
1312: int n;
1313:
1314: n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
1315: if (n < 1) {
1316: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1317: if (n == 0) {
1318: errno = ETIMEDOUT;
1319: }
1320: #endif
1321: return 0;
1322: }
1323:
1324: return 1;
1325: }
1326: /* }}} */
1327: /* {{{ data_writeable
1328: */
1329: int
1330: data_writeable(ftpbuf_t *ftp, php_socket_t s)
1331: {
1332: int n;
1333:
1334: n = php_pollfd_for_ms(s, POLLOUT, 1000);
1335: if (n < 1) {
1336: #ifndef PHP_WIN32
1337: if (n == 0) {
1338: errno = ETIMEDOUT;
1339: }
1340: #endif
1341: return 0;
1342: }
1343:
1344: return 1;
1345: }
1346: /* }}} */
1347:
1348: /* {{{ my_accept
1349: */
1350: int
1351: my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
1352: {
1353: int n;
1354:
1355: n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1356: if (n < 1) {
1357: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1358: if (n == 0) {
1359: errno = ETIMEDOUT;
1360: }
1361: #endif
1362: return -1;
1363: }
1364:
1365: return accept(s, addr, addrlen);
1366: }
1367: /* }}} */
1368:
1369: /* {{{ ftp_getdata
1370: */
1371: databuf_t*
1372: ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
1373: {
1374: int fd = -1;
1375: databuf_t *data;
1376: php_sockaddr_storage addr;
1377: struct sockaddr *sa;
1378: socklen_t size;
1379: union ipbox ipbox;
1380: char arg[sizeof("255, 255, 255, 255, 255, 255")];
1381: struct timeval tv;
1382:
1383:
1384: /* ask for a passive connection if we need one */
1385: if (ftp->pasv && !ftp_pasv(ftp, 1)) {
1386: return NULL;
1387: }
1388: /* alloc the data structure */
1389: data = ecalloc(1, sizeof(*data));
1390: data->listener = -1;
1391: data->fd = -1;
1392: data->type = ftp->type;
1393:
1394: sa = (struct sockaddr *) &ftp->localaddr;
1395: /* bind/listen */
1396: if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
1397: php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
1398: goto bail;
1399: }
1400:
1401: /* passive connection handler */
1402: if (ftp->pasv) {
1403: /* clear the ready status */
1404: ftp->pasv = 1;
1405:
1406: /* connect */
1407: /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
1408: size = php_sockaddr_size(&ftp->pasvaddr);
1409: tv.tv_sec = ftp->timeout_sec;
1410: tv.tv_usec = 0;
1411: if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
1412: php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
1413: goto bail;
1414: }
1415:
1416: data->fd = fd;
1417:
1418: ftp->data = data;
1419: return data;
1420: }
1421:
1422:
1423: /* active (normal) connection */
1424:
1425: /* bind to a local address */
1426: php_any_addr(sa->sa_family, &addr, 0);
1427: size = php_sockaddr_size(&addr);
1428:
1429: if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
1430: php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
1431: goto bail;
1432: }
1433:
1434: if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
1435: php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
1436: goto bail;
1437: }
1438:
1439: if (listen(fd, 5) != 0) {
1440: php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
1441: goto bail;
1442: }
1443:
1444: data->listener = fd;
1445:
1446: #if HAVE_IPV6 && HAVE_INET_NTOP
1447: if (sa->sa_family == AF_INET6) {
1448: /* need to use EPRT */
1449: char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
1450: char out[INET6_ADDRSTRLEN];
1451: inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
1452: snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
1453:
1454: if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
1455: goto bail;
1456: }
1457:
1458: if (!ftp_getresp(ftp) || ftp->resp != 200) {
1459: goto bail;
1460: }
1461:
1462: ftp->data = data;
1463: return data;
1464: }
1465: #endif
1466:
1467: /* send the PORT */
1468: ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
1469: ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
1470: snprintf(arg, sizeof(arg), "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);
1471:
1472: if (!ftp_putcmd(ftp, "PORT", arg)) {
1473: goto bail;
1474: }
1475: if (!ftp_getresp(ftp) || ftp->resp != 200) {
1476: goto bail;
1477: }
1478:
1479: ftp->data = data;
1480: return data;
1481:
1482: bail:
1483: if (fd != -1) {
1484: closesocket(fd);
1485: }
1486: efree(data);
1487: return NULL;
1488: }
1489: /* }}} */
1490:
1491: /* {{{ data_accept
1492: */
1493: databuf_t*
1494: data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
1495: {
1496: php_sockaddr_storage addr;
1497: socklen_t size;
1498:
1499: #if HAVE_OPENSSL_EXT
1500: SSL_CTX *ctx;
1.1.1.2 misho 1501: long ssl_ctx_options = SSL_OP_ALL;
1.1 misho 1502: #endif
1503:
1504: if (data->fd != -1) {
1505: goto data_accepted;
1506: }
1507: size = sizeof(addr);
1508: data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
1509: closesocket(data->listener);
1510: data->listener = -1;
1511:
1512: if (data->fd == -1) {
1513: efree(data);
1514: return NULL;
1515: }
1516:
1517: data_accepted:
1518: #if HAVE_OPENSSL_EXT
1519:
1520: /* now enable ssl if we need to */
1521: if (ftp->use_ssl && ftp->use_ssl_for_data) {
1522: ctx = SSL_CTX_new(SSLv23_client_method());
1523: if (ctx == NULL) {
1524: php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
1525: return 0;
1526: }
1527:
1.1.1.2 misho 1528: #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
1529: ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
1530: #endif
1531: SSL_CTX_set_options(ctx, ssl_ctx_options);
1.1 misho 1532:
1533: data->ssl_handle = SSL_new(ctx);
1534: if (data->ssl_handle == NULL) {
1535: php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
1536: SSL_CTX_free(ctx);
1537: return 0;
1538: }
1539:
1540:
1541: SSL_set_fd(data->ssl_handle, data->fd);
1542:
1543: if (ftp->old_ssl) {
1544: SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
1545: }
1546:
1547: if (SSL_connect(data->ssl_handle) <= 0) {
1548: php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
1549: SSL_shutdown(data->ssl_handle);
1.1.1.4 misho 1550: SSL_free(data->ssl_handle);
1.1 misho 1551: return 0;
1552: }
1553:
1554: data->ssl_active = 1;
1555: }
1556:
1557: #endif
1558:
1559: return data;
1560: }
1561: /* }}} */
1562:
1563: /* {{{ data_close
1564: */
1565: databuf_t*
1566: data_close(ftpbuf_t *ftp, databuf_t *data)
1567: {
1.1.1.4 misho 1568: #if HAVE_OPENSSL_EXT
1569: SSL_CTX *ctx;
1570: #endif
1.1 misho 1571: if (data == NULL) {
1572: return NULL;
1573: }
1574: if (data->listener != -1) {
1575: #if HAVE_OPENSSL_EXT
1576: if (data->ssl_active) {
1.1.1.4 misho 1577:
1578: ctx = SSL_get_SSL_CTX(data->ssl_handle);
1579: SSL_CTX_free(ctx);
1580:
1.1 misho 1581: SSL_shutdown(data->ssl_handle);
1.1.1.4 misho 1582: SSL_free(data->ssl_handle);
1.1 misho 1583: data->ssl_active = 0;
1584: }
1585: #endif
1586: closesocket(data->listener);
1587: }
1588: if (data->fd != -1) {
1589: #if HAVE_OPENSSL_EXT
1590: if (data->ssl_active) {
1.1.1.4 misho 1591: ctx = SSL_get_SSL_CTX(data->ssl_handle);
1592: SSL_CTX_free(ctx);
1593:
1.1 misho 1594: SSL_shutdown(data->ssl_handle);
1.1.1.4 misho 1595: SSL_free(data->ssl_handle);
1.1 misho 1596: data->ssl_active = 0;
1597: }
1598: #endif
1599: closesocket(data->fd);
1600: }
1601: if (ftp) {
1602: ftp->data = NULL;
1603: }
1604: efree(data);
1605: return NULL;
1606: }
1607: /* }}} */
1608:
1609: /* {{{ ftp_genlist
1610: */
1611: char**
1612: ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
1613: {
1614: php_stream *tmpstream = NULL;
1615: databuf_t *data = NULL;
1616: char *ptr;
1617: int ch, lastch;
1618: int size, rcvd;
1619: int lines;
1620: char **ret = NULL;
1621: char **entry;
1622: char *text;
1623:
1624:
1625: if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
1626: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
1627: return NULL;
1628: }
1629:
1630: if (!ftp_type(ftp, FTPTYPE_ASCII)) {
1631: goto bail;
1632: }
1633:
1634: if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1635: goto bail;
1636: }
1637: ftp->data = data;
1638:
1639: if (!ftp_putcmd(ftp, cmd, path)) {
1640: goto bail;
1641: }
1642: if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
1643: goto bail;
1644: }
1645:
1646: /* some servers don't open a ftp-data connection if the directory is empty */
1647: if (ftp->resp == 226) {
1648: ftp->data = data_close(ftp, data);
1649: php_stream_close(tmpstream);
1.1.1.5 ! misho 1650: return ecalloc(1, sizeof(char*));
1.1 misho 1651: }
1652:
1653: /* pull data buffer into tmpfile */
1654: if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1655: goto bail;
1656: }
1657: size = 0;
1658: lines = 0;
1659: lastch = 0;
1660: while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1661: if (rcvd == -1) {
1662: goto bail;
1663: }
1664:
1665: php_stream_write(tmpstream, data->buf, rcvd);
1666:
1667: size += rcvd;
1668: for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1669: if (*ptr == '\n' && lastch == '\r') {
1670: lines++;
1671: } else {
1672: size++;
1673: }
1674: lastch = *ptr;
1675: }
1676: }
1677:
1.1.1.5 ! misho 1678: ftp->data = data_close(ftp, data);
1.1 misho 1679:
1680: php_stream_rewind(tmpstream);
1681:
1.1.1.5 ! misho 1682: ret = safe_emalloc((lines + 1), sizeof(char*), size);
1.1 misho 1683:
1684: entry = ret;
1685: text = (char*) (ret + lines + 1);
1686: *entry = text;
1687: lastch = 0;
1688: while ((ch = php_stream_getc(tmpstream)) != EOF) {
1689: if (ch == '\n' && lastch == '\r') {
1690: *(text - 1) = 0;
1691: *++entry = text;
1692: } else {
1693: *text++ = ch;
1694: }
1695: lastch = ch;
1696: }
1697: *entry = NULL;
1698:
1699: php_stream_close(tmpstream);
1700:
1701: if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1702: efree(ret);
1703: return NULL;
1704: }
1705:
1706: return ret;
1707: bail:
1708: ftp->data = data_close(ftp, data);
1709: php_stream_close(tmpstream);
1710: if (ret)
1711: efree(ret);
1712: return NULL;
1713: }
1714: /* }}} */
1715:
1716: /* {{{ ftp_nb_get
1717: */
1718: int
1.1.1.4 misho 1719: ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
1.1 misho 1720: {
1721: databuf_t *data = NULL;
1722: char arg[11];
1723:
1724: if (ftp == NULL) {
1725: return PHP_FTP_FAILED;
1726: }
1727:
1728: if (!ftp_type(ftp, type)) {
1729: goto bail;
1730: }
1731:
1732: if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1733: goto bail;
1734: }
1735:
1736: if (resumepos>0) {
1.1.1.4 misho 1737: snprintf(arg, sizeof(arg), "%ld", resumepos);
1.1 misho 1738: if (!ftp_putcmd(ftp, "REST", arg)) {
1739: goto bail;
1740: }
1741: if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1742: goto bail;
1743: }
1744: }
1745:
1746: if (!ftp_putcmd(ftp, "RETR", path)) {
1747: goto bail;
1748: }
1749: if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1750: goto bail;
1751: }
1752:
1753: if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1754: goto bail;
1755: }
1756:
1757: ftp->data = data;
1758: ftp->stream = outstream;
1759: ftp->lastch = 0;
1760: ftp->nb = 1;
1761:
1762: return (ftp_nb_continue_read(ftp TSRMLS_CC));
1763:
1764: bail:
1765: ftp->data = data_close(ftp, data);
1766: return PHP_FTP_FAILED;
1767: }
1768: /* }}} */
1769:
1770: /* {{{ ftp_nb_continue_read
1771: */
1772: int
1773: ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
1774: {
1775: databuf_t *data = NULL;
1776: char *ptr;
1777: int lastch;
1778: size_t rcvd;
1779: ftptype_t type;
1780:
1781: data = ftp->data;
1782:
1783: /* check if there is already more data */
1784: if (!data_available(ftp, data->fd)) {
1785: return PHP_FTP_MOREDATA;
1786: }
1787:
1788: type = ftp->type;
1789:
1790: lastch = ftp->lastch;
1791: if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1792: if (rcvd == -1) {
1793: goto bail;
1794: }
1795:
1796: if (type == FTPTYPE_ASCII) {
1797: for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1798: if (lastch == '\r' && *ptr != '\n') {
1799: php_stream_putc(ftp->stream, '\r');
1800: }
1801: if (*ptr != '\r') {
1802: php_stream_putc(ftp->stream, *ptr);
1803: }
1804: lastch = *ptr;
1805: }
1806: } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
1807: goto bail;
1808: }
1809:
1810: ftp->lastch = lastch;
1811: return PHP_FTP_MOREDATA;
1812: }
1813:
1814: if (type == FTPTYPE_ASCII && lastch == '\r') {
1815: php_stream_putc(ftp->stream, '\r');
1816: }
1817:
1818: ftp->data = data = data_close(ftp, data);
1819:
1820: if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1821: goto bail;
1822: }
1823:
1824: ftp->nb = 0;
1825: return PHP_FTP_FINISHED;
1826: bail:
1827: ftp->nb = 0;
1828: ftp->data = data_close(ftp, data);
1829: return PHP_FTP_FAILED;
1830: }
1831: /* }}} */
1832:
1833: /* {{{ ftp_nb_put
1834: */
1835: int
1.1.1.4 misho 1836: ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
1.1 misho 1837: {
1838: databuf_t *data = NULL;
1839: char arg[11];
1840:
1841: if (ftp == NULL) {
1842: return 0;
1843: }
1844: if (!ftp_type(ftp, type)) {
1845: goto bail;
1846: }
1847: if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1848: goto bail;
1849: }
1850: if (startpos > 0) {
1.1.1.4 misho 1851: snprintf(arg, sizeof(arg), "%ld", startpos);
1.1 misho 1852: if (!ftp_putcmd(ftp, "REST", arg)) {
1853: goto bail;
1854: }
1855: if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1856: goto bail;
1857: }
1858: }
1859:
1860: if (!ftp_putcmd(ftp, "STOR", path)) {
1861: goto bail;
1862: }
1863: if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1864: goto bail;
1865: }
1866: if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1867: goto bail;
1868: }
1869: ftp->data = data;
1870: ftp->stream = instream;
1871: ftp->lastch = 0;
1872: ftp->nb = 1;
1873:
1874: return (ftp_nb_continue_write(ftp TSRMLS_CC));
1875:
1876: bail:
1877: ftp->data = data_close(ftp, data);
1878: return PHP_FTP_FAILED;
1879: }
1880: /* }}} */
1881:
1882:
1883: /* {{{ ftp_nb_continue_write
1884: */
1885: int
1886: ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
1887: {
1.1.1.4 misho 1888: long size;
1.1 misho 1889: char *ptr;
1890: int ch;
1891:
1892: /* check if we can write more data */
1893: if (!data_writeable(ftp, ftp->data->fd)) {
1894: return PHP_FTP_MOREDATA;
1895: }
1896:
1897: size = 0;
1898: ptr = ftp->data->buf;
1899: while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
1900:
1901: if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
1902: *ptr++ = '\r';
1903: size++;
1904: }
1905:
1906: *ptr++ = ch;
1907: size++;
1908:
1909: /* flush if necessary */
1910: if (FTP_BUFSIZE - size < 2) {
1911: if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1912: goto bail;
1913: }
1914: return PHP_FTP_MOREDATA;
1915: }
1916: }
1917:
1918: if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1919: goto bail;
1920: }
1921: ftp->data = data_close(ftp, ftp->data);
1922:
1923: if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1924: goto bail;
1925: }
1926: ftp->nb = 0;
1927: return PHP_FTP_FINISHED;
1928: bail:
1929: ftp->data = data_close(ftp, ftp->data);
1930: ftp->nb = 0;
1931: return PHP_FTP_FAILED;
1932: }
1933: /* }}} */
1934:
1935: #endif /* HAVE_FTP */
1936:
1937: /*
1938: * Local variables:
1939: * tab-width: 4
1940: * c-basic-offset: 4
1941: * End:
1942: * vim600: sw=4 ts=4 fdm=marker
1943: * vim<600: sw=4 ts=4
1944: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>