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