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