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