Return to ftp_fopen_wrapper.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / standard |
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: Rasmus Lerdorf <rasmus@php.net> | ! 16: | Jim Winstead <jimw@php.net> | ! 17: | Hartmut Holzgraefe <hholzgra@php.net> | ! 18: | Sara Golemon <pollita@php.net> | ! 19: +----------------------------------------------------------------------+ ! 20: */ ! 21: /* $Id: ftp_fopen_wrapper.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 22: ! 23: #include "php.h" ! 24: #include "php_globals.h" ! 25: #include "php_network.h" ! 26: #include "php_ini.h" ! 27: ! 28: #include <stdio.h> ! 29: #include <stdlib.h> ! 30: #include <errno.h> ! 31: #include <sys/types.h> ! 32: #include <sys/stat.h> ! 33: #include <fcntl.h> ! 34: ! 35: #ifdef PHP_WIN32 ! 36: #include <winsock2.h> ! 37: #define O_RDONLY _O_RDONLY ! 38: #include "win32/param.h" ! 39: #else ! 40: #include <sys/param.h> ! 41: #endif ! 42: ! 43: #include "php_standard.h" ! 44: ! 45: #include <sys/types.h> ! 46: #if HAVE_SYS_SOCKET_H ! 47: #include <sys/socket.h> ! 48: #endif ! 49: ! 50: #ifdef PHP_WIN32 ! 51: #include <winsock2.h> ! 52: #elif defined(NETWARE) && defined(USE_WINSOCK) ! 53: #include <novsock2.h> ! 54: #else ! 55: #include <netinet/in.h> ! 56: #include <netdb.h> ! 57: #if HAVE_ARPA_INET_H ! 58: #include <arpa/inet.h> ! 59: #endif ! 60: #endif ! 61: ! 62: #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) ! 63: #undef AF_UNIX ! 64: #endif ! 65: ! 66: #if defined(AF_UNIX) ! 67: #include <sys/un.h> ! 68: #endif ! 69: ! 70: #include "php_fopen_wrappers.h" ! 71: ! 72: #define FTPS_ENCRYPT_DATA 1 ! 73: #define GET_FTP_RESULT(stream) get_ftp_result((stream), tmp_line, sizeof(tmp_line) TSRMLS_CC) ! 74: ! 75: typedef struct _php_ftp_dirstream_data { ! 76: php_stream *datastream; ! 77: php_stream *controlstream; ! 78: php_stream *dirstream; ! 79: } php_ftp_dirstream_data; ! 80: ! 81: /* {{{ get_ftp_result ! 82: */ ! 83: static inline int get_ftp_result(php_stream *stream, char *buffer, size_t buffer_size TSRMLS_DC) ! 84: { ! 85: while (php_stream_gets(stream, buffer, buffer_size-1) && ! 86: !(isdigit((int) buffer[0]) && isdigit((int) buffer[1]) && ! 87: isdigit((int) buffer[2]) && buffer[3] == ' ')); ! 88: return strtol(buffer, NULL, 10); ! 89: } ! 90: /* }}} */ ! 91: ! 92: /* {{{ php_stream_ftp_stream_stat ! 93: */ ! 94: static int php_stream_ftp_stream_stat(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) ! 95: { ! 96: /* For now, we return with a failure code to prevent the underlying ! 97: * file's details from being used instead. */ ! 98: return -1; ! 99: } ! 100: /* }}} */ ! 101: ! 102: /* {{{ php_stream_ftp_stream_close ! 103: */ ! 104: static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC) ! 105: { ! 106: php_stream *controlstream = stream->wrapperthis; ! 107: int ret = 0; ! 108: ! 109: if (controlstream) { ! 110: if (strpbrk(stream->mode, "wa+")) { ! 111: char tmp_line[512]; ! 112: int result; ! 113: ! 114: /* For write modes close data stream first to signal EOF to server */ ! 115: result = GET_FTP_RESULT(controlstream); ! 116: if (result != 226 && result != 250) { ! 117: php_error_docref(NULL TSRMLS_CC, E_WARNING, "FTP server error %d:%s", result, tmp_line); ! 118: ret = EOF; ! 119: } ! 120: } ! 121: ! 122: php_stream_write_string(controlstream, "QUIT\r\n"); ! 123: php_stream_close(controlstream); ! 124: stream->wrapperthis = NULL; ! 125: } ! 126: ! 127: return ret; ! 128: } ! 129: /* }}} */ ! 130: ! 131: /* {{{ php_ftp_fopen_connect ! 132: */ ! 133: static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context, ! 134: php_stream **preuseid, php_url **presource, int *puse_ssl, int *puse_ssl_on_data TSRMLS_DC) ! 135: { ! 136: php_stream *stream = NULL, *reuseid = NULL; ! 137: php_url *resource = NULL; ! 138: int result, use_ssl, use_ssl_on_data = 0, tmp_len; ! 139: char tmp_line[512]; ! 140: char *transport; ! 141: int transport_len; ! 142: ! 143: resource = php_url_parse(path); ! 144: if (resource == NULL || resource->path == NULL) { ! 145: if (resource && presource) { ! 146: *presource = resource; ! 147: } ! 148: return NULL; ! 149: } ! 150: ! 151: use_ssl = resource->scheme && (strlen(resource->scheme) > 3) && resource->scheme[3] == 's'; ! 152: ! 153: /* use port 21 if one wasn't specified */ ! 154: if (resource->port == 0) ! 155: resource->port = 21; ! 156: ! 157: transport_len = spprintf(&transport, 0, "tcp://%s:%d", resource->host, resource->port); ! 158: stream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL); ! 159: efree(transport); ! 160: if (stream == NULL) { ! 161: result = 0; /* silence */ ! 162: goto connect_errexit; ! 163: } ! 164: ! 165: php_stream_context_set(stream, context); ! 166: php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0); ! 167: ! 168: /* Start talking to ftp server */ ! 169: result = GET_FTP_RESULT(stream); ! 170: if (result > 299 || result < 200) { ! 171: php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result); ! 172: goto connect_errexit; ! 173: } ! 174: ! 175: if (use_ssl) { ! 176: ! 177: /* send the AUTH TLS request name */ ! 178: php_stream_write_string(stream, "AUTH TLS\r\n"); ! 179: ! 180: /* get the response */ ! 181: result = GET_FTP_RESULT(stream); ! 182: if (result != 234) { ! 183: /* AUTH TLS not supported try AUTH SSL */ ! 184: php_stream_write_string(stream, "AUTH SSL\r\n"); ! 185: ! 186: /* get the response */ ! 187: result = GET_FTP_RESULT(stream); ! 188: if (result != 334) { ! 189: use_ssl = 0; ! 190: } else { ! 191: /* we must reuse the old SSL session id */ ! 192: /* if we talk to an old ftpd-ssl */ ! 193: reuseid = stream; ! 194: } ! 195: } else { ! 196: /* encrypt data etc */ ! 197: ! 198: ! 199: } ! 200: ! 201: } ! 202: ! 203: if (use_ssl) { ! 204: if (php_stream_xport_crypto_setup(stream, ! 205: STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ! 206: || php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) { ! 207: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode"); ! 208: php_stream_close(stream); ! 209: stream = NULL; ! 210: goto connect_errexit; ! 211: } ! 212: ! 213: /* set PBSZ to 0 */ ! 214: php_stream_write_string(stream, "PBSZ 0\r\n"); ! 215: ! 216: /* ignore the response */ ! 217: result = GET_FTP_RESULT(stream); ! 218: ! 219: /* set data connection protection level */ ! 220: #if FTPS_ENCRYPT_DATA ! 221: php_stream_write_string(stream, "PROT P\r\n"); ! 222: ! 223: /* get the response */ ! 224: result = GET_FTP_RESULT(stream); ! 225: use_ssl_on_data = (result >= 200 && result<=299) || reuseid; ! 226: #else ! 227: php_stream_write_string(stream, "PROT C\r\n"); ! 228: ! 229: /* get the response */ ! 230: result = GET_FTP_RESULT(stream); ! 231: #endif ! 232: } ! 233: ! 234: #define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \ ! 235: unsigned char *s = val, *e = s + val_len; \ ! 236: while (s < e) { \ ! 237: if (iscntrl(*s)) { \ ! 238: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, err_msg, val); \ ! 239: goto connect_errexit; \ ! 240: } \ ! 241: s++; \ ! 242: } \ ! 243: } ! 244: ! 245: /* send the user name */ ! 246: if (resource->user != NULL) { ! 247: tmp_len = php_raw_url_decode(resource->user, strlen(resource->user)); ! 248: ! 249: PHP_FTP_CNTRL_CHK(resource->user, tmp_len, "Invalid login %s") ! 250: ! 251: php_stream_printf(stream TSRMLS_CC, "USER %s\r\n", resource->user); ! 252: } else { ! 253: php_stream_write_string(stream, "USER anonymous\r\n"); ! 254: } ! 255: ! 256: /* get the response */ ! 257: result = GET_FTP_RESULT(stream); ! 258: ! 259: /* if a password is required, send it */ ! 260: if (result >= 300 && result <= 399) { ! 261: php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0); ! 262: ! 263: if (resource->pass != NULL) { ! 264: tmp_len = php_raw_url_decode(resource->pass, strlen(resource->pass)); ! 265: ! 266: PHP_FTP_CNTRL_CHK(resource->pass, tmp_len, "Invalid password %s") ! 267: ! 268: php_stream_printf(stream TSRMLS_CC, "PASS %s\r\n", resource->pass); ! 269: } else { ! 270: /* if the user has configured who they are, ! 271: send that as the password */ ! 272: char *from_address = php_ini_string("from", sizeof("from"), 0); ! 273: if (from_address[0] != '\0') { ! 274: php_stream_printf(stream TSRMLS_CC, "PASS %s\r\n", from_address); ! 275: } else { ! 276: php_stream_write_string(stream, "PASS anonymous\r\n"); ! 277: } ! 278: } ! 279: ! 280: /* read the response */ ! 281: result = GET_FTP_RESULT(stream); ! 282: ! 283: if (result > 299 || result < 200) { ! 284: php_stream_notify_error(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result); ! 285: } else { ! 286: php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result); ! 287: } ! 288: } ! 289: if (result > 299 || result < 200) { ! 290: goto connect_errexit; ! 291: } ! 292: ! 293: if (puse_ssl) { ! 294: *puse_ssl = use_ssl; ! 295: } ! 296: if (puse_ssl_on_data) { ! 297: *puse_ssl_on_data = use_ssl_on_data; ! 298: } ! 299: if (preuseid) { ! 300: *preuseid = reuseid; ! 301: } ! 302: if (presource) { ! 303: *presource = resource; ! 304: } ! 305: ! 306: return stream; ! 307: ! 308: connect_errexit: ! 309: if (resource) { ! 310: php_url_free(resource); ! 311: } ! 312: ! 313: if (stream) { ! 314: php_stream_close(stream); ! 315: } ! 316: ! 317: return NULL; ! 318: } ! 319: /* }}} */ ! 320: ! 321: /* {{{ php_fopen_do_pasv ! 322: */ ! 323: static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, size_t ip_size, char **phoststart TSRMLS_DC) ! 324: { ! 325: char tmp_line[512]; ! 326: int result, i; ! 327: unsigned short portno; ! 328: char *tpath, *ttpath, *hoststart=NULL; ! 329: ! 330: #ifdef HAVE_IPV6 ! 331: /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */ ! 332: php_stream_write_string(stream, "EPSV\r\n"); ! 333: result = GET_FTP_RESULT(stream); ! 334: ! 335: /* check if we got a 229 response */ ! 336: if (result != 229) { ! 337: #endif ! 338: /* EPSV failed, let's try PASV */ ! 339: php_stream_write_string(stream, "PASV\r\n"); ! 340: result = GET_FTP_RESULT(stream); ! 341: ! 342: /* make sure we got a 227 response */ ! 343: if (result != 227) { ! 344: return 0; ! 345: } ! 346: ! 347: /* parse pasv command (129, 80, 95, 25, 13, 221) */ ! 348: tpath = tmp_line; ! 349: /* skip over the "227 Some message " part */ ! 350: for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++); ! 351: if (!*tpath) { ! 352: return 0; ! 353: } ! 354: /* skip over the host ip, to get the port */ ! 355: hoststart = tpath; ! 356: for (i = 0; i < 4; i++) { ! 357: for (; isdigit((int) *tpath); tpath++); ! 358: if (*tpath != ',') { ! 359: return 0; ! 360: } ! 361: *tpath='.'; ! 362: tpath++; ! 363: } ! 364: tpath[-1] = '\0'; ! 365: memcpy(ip, hoststart, ip_size); ! 366: ip[ip_size-1] = '\0'; ! 367: hoststart = ip; ! 368: ! 369: /* pull out the MSB of the port */ ! 370: portno = (unsigned short) strtoul(tpath, &ttpath, 10) * 256; ! 371: if (ttpath == NULL) { ! 372: /* didn't get correct response from PASV */ ! 373: return 0; ! 374: } ! 375: tpath = ttpath; ! 376: if (*tpath != ',') { ! 377: return 0; ! 378: } ! 379: tpath++; ! 380: /* pull out the LSB of the port */ ! 381: portno += (unsigned short) strtoul(tpath, &ttpath, 10); ! 382: #ifdef HAVE_IPV6 ! 383: } else { ! 384: /* parse epsv command (|||6446|) */ ! 385: for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) { ! 386: if (*tpath == '|') { ! 387: i++; ! 388: if (i == 3) ! 389: break; ! 390: } ! 391: } ! 392: if (i < 3) { ! 393: return 0; ! 394: } ! 395: /* pull out the port */ ! 396: portno = (unsigned short) strtoul(tpath + 1, &ttpath, 10); ! 397: } ! 398: #endif ! 399: if (ttpath == NULL) { ! 400: /* didn't get correct response from EPSV/PASV */ ! 401: return 0; ! 402: } ! 403: ! 404: if (phoststart) { ! 405: *phoststart = hoststart; ! 406: } ! 407: ! 408: return portno; ! 409: } ! 410: /* }}} */ ! 411: ! 412: /* {{{ php_fopen_url_wrap_ftp ! 413: */ ! 414: php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) ! 415: { ! 416: php_stream *stream = NULL, *datastream = NULL; ! 417: php_url *resource = NULL; ! 418: char tmp_line[512]; ! 419: char ip[sizeof("123.123.123.123")]; ! 420: unsigned short portno; ! 421: char *hoststart = NULL; ! 422: int result = 0, use_ssl, use_ssl_on_data=0; ! 423: php_stream *reuseid=NULL; ! 424: size_t file_size = 0; ! 425: zval **tmpzval; ! 426: int allow_overwrite = 0; ! 427: int read_write = 0; ! 428: char *transport; ! 429: int transport_len; ! 430: ! 431: tmp_line[0] = '\0'; ! 432: ! 433: if (strpbrk(mode, "r+")) { ! 434: read_write = 1; /* Open for reading */ ! 435: } ! 436: if (strpbrk(mode, "wa+")) { ! 437: if (read_write) { ! 438: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP does not support simultaneous read/write connections"); ! 439: return NULL; ! 440: } ! 441: if (strchr(mode, 'a')) { ! 442: read_write = 3; /* Open for Appending */ ! 443: } else { ! 444: read_write = 2; /* Open for writting */ ! 445: } ! 446: } ! 447: if (!read_write) { ! 448: /* No mode specified? */ ! 449: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unknown file open mode"); ! 450: return NULL; ! 451: } ! 452: ! 453: if (context && ! 454: php_stream_context_get_option(context, "ftp", "proxy", &tmpzval) == SUCCESS) { ! 455: if (read_write == 1) { ! 456: /* Use http wrapper to proxy ftp request */ ! 457: return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC TSRMLS_CC); ! 458: } else { ! 459: /* ftp proxy is read-only */ ! 460: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP proxy may only be used in read mode"); ! 461: return NULL; ! 462: } ! 463: } ! 464: ! 465: stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data TSRMLS_CC); ! 466: if (!stream) { ! 467: goto errexit; ! 468: } ! 469: ! 470: /* set the connection to be binary */ ! 471: php_stream_write_string(stream, "TYPE I\r\n"); ! 472: result = GET_FTP_RESULT(stream); ! 473: if (result > 299 || result < 200) ! 474: goto errexit; ! 475: ! 476: /* find out the size of the file (verifying it exists) */ ! 477: php_stream_printf(stream TSRMLS_CC, "SIZE %s\r\n", resource->path); ! 478: ! 479: /* read the response */ ! 480: result = GET_FTP_RESULT(stream); ! 481: if (read_write == 1) { ! 482: /* Read Mode */ ! 483: char *sizestr; ! 484: ! 485: /* when reading file, it must exist */ ! 486: if (result > 299 || result < 200) { ! 487: errno = ENOENT; ! 488: goto errexit; ! 489: } ! 490: ! 491: sizestr = strchr(tmp_line, ' '); ! 492: if (sizestr) { ! 493: sizestr++; ! 494: file_size = atoi(sizestr); ! 495: php_stream_notify_file_size(context, file_size, tmp_line, result); ! 496: } ! 497: } else if (read_write == 2) { ! 498: /* when writing file (but not appending), it must NOT exist, unless a context option exists which allows it */ ! 499: if (context && php_stream_context_get_option(context, "ftp", "overwrite", &tmpzval) == SUCCESS) { ! 500: allow_overwrite = Z_LVAL_PP(tmpzval); ! 501: } ! 502: if (result <= 299 && result >= 200) { ! 503: if (allow_overwrite) { ! 504: /* Context permits overwritting file, ! 505: so we just delete whatever's there in preparation */ ! 506: php_stream_printf(stream TSRMLS_CC, "DELE %s\r\n", resource->path); ! 507: result = GET_FTP_RESULT(stream); ! 508: if (result >= 300 || result <= 199) { ! 509: goto errexit; ! 510: } ! 511: } else { ! 512: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Remote file already exists and overwrite context option not specified"); ! 513: errno = EEXIST; ! 514: goto errexit; ! 515: } ! 516: } ! 517: } ! 518: ! 519: /* set up the passive connection */ ! 520: portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart TSRMLS_CC); ! 521: ! 522: if (!portno) { ! 523: goto errexit; ! 524: } ! 525: ! 526: /* Send RETR/STOR command */ ! 527: if (read_write == 1) { ! 528: /* set resume position if applicable */ ! 529: if (context && ! 530: php_stream_context_get_option(context, "ftp", "resume_pos", &tmpzval) == SUCCESS && ! 531: Z_TYPE_PP(tmpzval) == IS_LONG && ! 532: Z_LVAL_PP(tmpzval) > 0) { ! 533: php_stream_printf(stream TSRMLS_CC, "REST %ld\r\n", Z_LVAL_PP(tmpzval)); ! 534: result = GET_FTP_RESULT(stream); ! 535: if (result < 300 || result > 399) { ! 536: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to resume from offset %ld", Z_LVAL_PP(tmpzval)); ! 537: goto errexit; ! 538: } ! 539: } ! 540: ! 541: /* retrieve file */ ! 542: memcpy(tmp_line, "RETR", sizeof("RETR")); ! 543: } else if (read_write == 2) { ! 544: /* Write new file */ ! 545: memcpy(tmp_line, "STOR", sizeof("STOR")); ! 546: } else { ! 547: /* Append */ ! 548: memcpy(tmp_line, "APPE", sizeof("APPE")); ! 549: } ! 550: php_stream_printf(stream TSRMLS_CC, "%s %s\r\n", tmp_line, (resource->path != NULL ? resource->path : "/")); ! 551: ! 552: /* open the data channel */ ! 553: if (hoststart == NULL) { ! 554: hoststart = resource->host; ! 555: } ! 556: transport_len = spprintf(&transport, 0, "tcp://%s:%d", hoststart, portno); ! 557: datastream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL); ! 558: efree(transport); ! 559: if (datastream == NULL) { ! 560: goto errexit; ! 561: } ! 562: ! 563: result = GET_FTP_RESULT(stream); ! 564: if (result != 150 && result != 125) { ! 565: /* Could not retrieve or send the file ! 566: * this data will only be sent to us after connection on the data port was initiated. ! 567: */ ! 568: php_stream_close(datastream); ! 569: datastream = NULL; ! 570: goto errexit; ! 571: } ! 572: ! 573: php_stream_context_set(datastream, context); ! 574: php_stream_notify_progress_init(context, 0, file_size); ! 575: ! 576: if (use_ssl_on_data && (php_stream_xport_crypto_setup(datastream, ! 577: STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 || ! 578: php_stream_xport_crypto_enable(datastream, 1 TSRMLS_CC) < 0)) { ! 579: ! 580: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode"); ! 581: php_stream_close(datastream); ! 582: datastream = NULL; ! 583: goto errexit; ! 584: } ! 585: ! 586: /* remember control stream */ ! 587: datastream->wrapperthis = stream; ! 588: ! 589: php_url_free(resource); ! 590: return datastream; ! 591: ! 592: errexit: ! 593: if (resource) { ! 594: php_url_free(resource); ! 595: } ! 596: if (stream) { ! 597: php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result); ! 598: php_stream_close(stream); ! 599: } ! 600: if (tmp_line[0] != '\0') ! 601: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP server reports %s", tmp_line); ! 602: return NULL; ! 603: } ! 604: /* }}} */ ! 605: ! 606: /* {{{ php_ftp_dirsteam_read ! 607: */ ! 608: static size_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) ! 609: { ! 610: php_stream_dirent *ent = (php_stream_dirent *)buf; ! 611: php_stream *innerstream; ! 612: size_t tmp_len; ! 613: char *basename; ! 614: size_t basename_len; ! 615: ! 616: innerstream = ((php_ftp_dirstream_data *)stream->abstract)->datastream; ! 617: ! 618: if (count != sizeof(php_stream_dirent)) { ! 619: return 0; ! 620: } ! 621: ! 622: if (php_stream_eof(innerstream)) { ! 623: return 0; ! 624: } ! 625: ! 626: if (!php_stream_get_line(innerstream, ent->d_name, sizeof(ent->d_name), &tmp_len)) { ! 627: return 0; ! 628: } ! 629: ! 630: php_basename(ent->d_name, tmp_len, NULL, 0, &basename, &basename_len TSRMLS_CC); ! 631: if (!basename) { ! 632: return 0; ! 633: } ! 634: ! 635: if (!basename_len) { ! 636: efree(basename); ! 637: return 0; ! 638: } ! 639: ! 640: tmp_len = MIN(sizeof(ent->d_name), basename_len - 1); ! 641: memcpy(ent->d_name, basename, tmp_len); ! 642: ent->d_name[tmp_len - 1] = '\0'; ! 643: efree(basename); ! 644: ! 645: /* Trim off trailing whitespace characters */ ! 646: tmp_len--; ! 647: while (tmp_len >= 0 && ! 648: (ent->d_name[tmp_len] == '\n' || ent->d_name[tmp_len] == '\r' || ! 649: ent->d_name[tmp_len] == '\t' || ent->d_name[tmp_len] == ' ')) { ! 650: ent->d_name[tmp_len--] = '\0'; ! 651: } ! 652: ! 653: return sizeof(php_stream_dirent); ! 654: } ! 655: /* }}} */ ! 656: ! 657: /* {{{ php_ftp_dirstream_close ! 658: */ ! 659: static int php_ftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) ! 660: { ! 661: php_ftp_dirstream_data *data = stream->abstract; ! 662: ! 663: /* close control connection */ ! 664: if (data->controlstream) { ! 665: php_stream_close(data->controlstream); ! 666: data->controlstream = NULL; ! 667: } ! 668: /* close data connection */ ! 669: php_stream_close(data->datastream); ! 670: data->datastream = NULL; ! 671: ! 672: efree(data); ! 673: stream->abstract = NULL; ! 674: ! 675: return 0; ! 676: } ! 677: /* }}} */ ! 678: ! 679: /* ftp dirstreams only need to support read and close operations, ! 680: They can't be rewound because the underlying ftp stream can't be rewound. */ ! 681: static php_stream_ops php_ftp_dirstream_ops = { ! 682: NULL, /* write */ ! 683: php_ftp_dirstream_read, /* read */ ! 684: php_ftp_dirstream_close, /* close */ ! 685: NULL, /* flush */ ! 686: "ftpdir", ! 687: NULL, /* rewind */ ! 688: NULL, /* cast */ ! 689: NULL, /* stat */ ! 690: NULL /* set option */ ! 691: }; ! 692: ! 693: /* {{{ php_stream_ftp_opendir ! 694: */ ! 695: php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) ! 696: { ! 697: php_stream *stream, *reuseid, *datastream = NULL; ! 698: php_ftp_dirstream_data *dirsdata; ! 699: php_url *resource = NULL; ! 700: int result = 0, use_ssl, use_ssl_on_data = 0; ! 701: char *hoststart = NULL, tmp_line[512]; ! 702: char ip[sizeof("123.123.123.123")]; ! 703: unsigned short portno; ! 704: ! 705: tmp_line[0] = '\0'; ! 706: ! 707: stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data TSRMLS_CC); ! 708: if (!stream) { ! 709: goto opendir_errexit; ! 710: } ! 711: ! 712: /* set the connection to be ascii */ ! 713: php_stream_write_string(stream, "TYPE A\r\n"); ! 714: result = GET_FTP_RESULT(stream); ! 715: if (result > 299 || result < 200) ! 716: goto opendir_errexit; ! 717: ! 718: /* set up the passive connection */ ! 719: portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart TSRMLS_CC); ! 720: ! 721: if (!portno) { ! 722: goto opendir_errexit; ! 723: } ! 724: ! 725: php_stream_printf(stream TSRMLS_CC, "NLST %s\r\n", (resource->path != NULL ? resource->path : "/")); ! 726: ! 727: /* open the data channel */ ! 728: if (hoststart == NULL) { ! 729: hoststart = resource->host; ! 730: } ! 731: datastream = php_stream_sock_open_host(hoststart, portno, SOCK_STREAM, 0, 0); ! 732: if (datastream == NULL) { ! 733: goto opendir_errexit; ! 734: } ! 735: ! 736: result = GET_FTP_RESULT(stream); ! 737: if (result != 150 && result != 125) { ! 738: /* Could not retrieve or send the file ! 739: * this data will only be sent to us after connection on the data port was initiated. ! 740: */ ! 741: php_stream_close(datastream); ! 742: datastream = NULL; ! 743: goto opendir_errexit; ! 744: } ! 745: ! 746: php_stream_context_set(datastream, context); ! 747: ! 748: if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream, ! 749: STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 || ! 750: php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) { ! 751: ! 752: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode"); ! 753: php_stream_close(datastream); ! 754: datastream = NULL; ! 755: goto opendir_errexit; ! 756: } ! 757: ! 758: php_url_free(resource); ! 759: ! 760: dirsdata = emalloc(sizeof *dirsdata); ! 761: dirsdata->datastream = datastream; ! 762: dirsdata->controlstream = stream; ! 763: dirsdata->dirstream = php_stream_alloc(&php_ftp_dirstream_ops, dirsdata, 0, mode); ! 764: ! 765: return dirsdata->dirstream; ! 766: ! 767: opendir_errexit: ! 768: if (resource) { ! 769: php_url_free(resource); ! 770: } ! 771: if (stream) { ! 772: php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result); ! 773: php_stream_close(stream); ! 774: } ! 775: if (tmp_line[0] != '\0') { ! 776: php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP server reports %s", tmp_line); ! 777: } ! 778: return NULL; ! 779: } ! 780: /* }}} */ ! 781: ! 782: /* {{{ php_stream_ftp_url_stat ! 783: */ ! 784: static int php_stream_ftp_url_stat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) ! 785: { ! 786: php_stream *stream = NULL; ! 787: php_url *resource = NULL; ! 788: int result; ! 789: char tmp_line[512]; ! 790: ! 791: /* If ssb is NULL then someone is misbehaving */ ! 792: if (!ssb) return -1; ! 793: ! 794: stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL TSRMLS_CC); ! 795: if (!stream) { ! 796: goto stat_errexit; ! 797: } ! 798: ! 799: ssb->sb.st_mode = 0644; /* FTP won't give us a valid mode, so aproximate one based on being readable */ ! 800: php_stream_printf(stream TSRMLS_CC, "CWD %s\r\n", (resource->path != NULL ? resource->path : "/")); /* If we can CWD to it, it's a directory (maybe a link, but we can't tell) */ ! 801: result = GET_FTP_RESULT(stream); ! 802: if (result < 200 || result > 299) { ! 803: ssb->sb.st_mode |= S_IFREG; ! 804: } else { ! 805: ssb->sb.st_mode |= S_IFDIR; ! 806: } ! 807: ! 808: php_stream_write_string(stream, "TYPE I\r\n"); /* we need this since some servers refuse to accept SIZE command in ASCII mode */ ! 809: ! 810: result = GET_FTP_RESULT(stream); ! 811: ! 812: if(result < 200 || result > 299) { ! 813: goto stat_errexit; ! 814: } ! 815: ! 816: php_stream_printf(stream TSRMLS_CC, "SIZE %s\r\n", (resource->path != NULL ? resource->path : "/")); ! 817: result = GET_FTP_RESULT(stream); ! 818: if (result < 200 || result > 299) { ! 819: /* Failure either means it doesn't exist ! 820: or it's a directory and this server ! 821: fails on listing directory sizes */ ! 822: if (ssb->sb.st_mode & S_IFDIR) { ! 823: ssb->sb.st_size = 0; ! 824: } else { ! 825: goto stat_errexit; ! 826: } ! 827: } else { ! 828: ssb->sb.st_size = atoi(tmp_line + 4); ! 829: } ! 830: ! 831: php_stream_printf(stream TSRMLS_CC, "MDTM %s\r\n", (resource->path != NULL ? resource->path : "/")); ! 832: result = GET_FTP_RESULT(stream); ! 833: if (result == 213) { ! 834: char *p = tmp_line + 4; ! 835: int n; ! 836: struct tm tm, tmbuf, *gmt; ! 837: time_t stamp; ! 838: ! 839: while (p - tmp_line < sizeof(tmp_line) && !isdigit(*p)) { ! 840: p++; ! 841: } ! 842: ! 843: if (p - tmp_line > sizeof(tmp_line)) { ! 844: goto mdtm_error; ! 845: } ! 846: ! 847: n = sscanf(p, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); ! 848: if (n != 6) { ! 849: goto mdtm_error; ! 850: } ! 851: ! 852: tm.tm_year -= 1900; ! 853: tm.tm_mon--; ! 854: tm.tm_isdst = -1; ! 855: ! 856: /* figure out the GMT offset */ ! 857: stamp = time(NULL); ! 858: gmt = php_gmtime_r(&stamp, &tmbuf); ! 859: if (!gmt) { ! 860: goto mdtm_error; ! 861: } ! 862: gmt->tm_isdst = -1; ! 863: ! 864: /* apply the GMT offset */ ! 865: tm.tm_sec += stamp - mktime(gmt); ! 866: tm.tm_isdst = gmt->tm_isdst; ! 867: ! 868: ssb->sb.st_mtime = mktime(&tm); ! 869: } else { ! 870: /* error or unsupported command */ ! 871: mdtm_error: ! 872: ssb->sb.st_mtime = -1; ! 873: } ! 874: ! 875: ssb->sb.st_ino = 0; /* Unknown values */ ! 876: ssb->sb.st_dev = 0; ! 877: ssb->sb.st_uid = 0; ! 878: ssb->sb.st_gid = 0; ! 879: ssb->sb.st_atime = -1; ! 880: ssb->sb.st_ctime = -1; ! 881: ! 882: ssb->sb.st_nlink = 1; ! 883: ssb->sb.st_rdev = -1; ! 884: #ifdef HAVE_ST_BLKSIZE ! 885: ssb->sb.st_blksize = 4096; /* Guess since FTP won't expose this information */ ! 886: #ifdef HAVE_ST_BLOCKS ! 887: ssb->sb.st_blocks = (int)((4095 + ssb->sb.st_size) / ssb->sb.st_blksize); /* emulate ceil */ ! 888: #endif ! 889: #endif ! 890: php_stream_close(stream); ! 891: php_url_free(resource); ! 892: return 0; ! 893: ! 894: stat_errexit: ! 895: if (resource) { ! 896: php_url_free(resource); ! 897: } ! 898: if (stream) { ! 899: php_stream_close(stream); ! 900: } ! 901: return -1; ! 902: } ! 903: /* }}} */ ! 904: ! 905: /* {{{ php_stream_ftp_unlink ! 906: */ ! 907: static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) ! 908: { ! 909: php_stream *stream = NULL; ! 910: php_url *resource = NULL; ! 911: int result; ! 912: char tmp_line[512]; ! 913: ! 914: stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL TSRMLS_CC); ! 915: if (!stream) { ! 916: if (options & REPORT_ERRORS) { ! 917: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", url); ! 918: } ! 919: goto unlink_errexit; ! 920: } ! 921: ! 922: if (resource->path == NULL) { ! 923: if (options & REPORT_ERRORS) { ! 924: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path provided in %s", url); ! 925: } ! 926: goto unlink_errexit; ! 927: } ! 928: ! 929: /* Attempt to delete the file */ ! 930: php_stream_printf(stream TSRMLS_CC, "DELE %s\r\n", (resource->path != NULL ? resource->path : "/")); ! 931: ! 932: result = GET_FTP_RESULT(stream); ! 933: if (result < 200 || result > 299) { ! 934: if (options & REPORT_ERRORS) { ! 935: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error Deleting file: %s", tmp_line); ! 936: } ! 937: goto unlink_errexit; ! 938: } ! 939: ! 940: php_url_free(resource); ! 941: php_stream_close(stream); ! 942: return 1; ! 943: ! 944: unlink_errexit: ! 945: if (resource) { ! 946: php_url_free(resource); ! 947: } ! 948: if (stream) { ! 949: php_stream_close(stream); ! 950: } ! 951: return 0; ! 952: } ! 953: /* }}} */ ! 954: ! 955: /* {{{ php_stream_ftp_rename ! 956: */ ! 957: static int php_stream_ftp_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) ! 958: { ! 959: php_stream *stream = NULL; ! 960: php_url *resource_from = NULL, *resource_to = NULL; ! 961: int result; ! 962: char tmp_line[512]; ! 963: ! 964: resource_from = php_url_parse(url_from); ! 965: resource_to = php_url_parse(url_to); ! 966: /* Must be same scheme (ftp/ftp or ftps/ftps), same host, and same port ! 967: (or a 21/0 0/21 combination which is also "same") ! 968: Also require paths to/from */ ! 969: if (!resource_from || ! 970: !resource_to || ! 971: !resource_from->scheme || ! 972: !resource_to->scheme || ! 973: strcmp(resource_from->scheme, resource_to->scheme) || ! 974: !resource_from->host || ! 975: !resource_to->host || ! 976: strcmp(resource_from->host, resource_to->host) || ! 977: (resource_from->port != resource_to->port && ! 978: resource_from->port * resource_to->port != 0 && ! 979: resource_from->port + resource_to->port != 21) || ! 980: !resource_from->path || ! 981: !resource_to->path) { ! 982: goto rename_errexit; ! 983: } ! 984: ! 985: stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, NULL, NULL, NULL, NULL, NULL TSRMLS_CC); ! 986: if (!stream) { ! 987: if (options & REPORT_ERRORS) { ! 988: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", resource_from->host); ! 989: } ! 990: goto rename_errexit; ! 991: } ! 992: ! 993: /* Rename FROM */ ! 994: php_stream_printf(stream TSRMLS_CC, "RNFR %s\r\n", (resource_from->path != NULL ? resource_from->path : "/")); ! 995: ! 996: result = GET_FTP_RESULT(stream); ! 997: if (result < 300 || result > 399) { ! 998: if (options & REPORT_ERRORS) { ! 999: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error Renaming file: %s", tmp_line); ! 1000: } ! 1001: goto rename_errexit; ! 1002: } ! 1003: ! 1004: /* Rename TO */ ! 1005: php_stream_printf(stream TSRMLS_CC, "RNTO %s\r\n", (resource_to->path != NULL ? resource_to->path : "/")); ! 1006: ! 1007: result = GET_FTP_RESULT(stream); ! 1008: if (result < 200 || result > 299) { ! 1009: if (options & REPORT_ERRORS) { ! 1010: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error Renaming file: %s", tmp_line); ! 1011: } ! 1012: goto rename_errexit; ! 1013: } ! 1014: ! 1015: php_url_free(resource_from); ! 1016: php_url_free(resource_to); ! 1017: php_stream_close(stream); ! 1018: return 1; ! 1019: ! 1020: rename_errexit: ! 1021: if (resource_from) { ! 1022: php_url_free(resource_from); ! 1023: } ! 1024: if (resource_to) { ! 1025: php_url_free(resource_to); ! 1026: } ! 1027: if (stream) { ! 1028: php_stream_close(stream); ! 1029: } ! 1030: return 0; ! 1031: } ! 1032: /* }}} */ ! 1033: ! 1034: /* {{{ php_stream_ftp_mkdir ! 1035: */ ! 1036: static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC) ! 1037: { ! 1038: php_stream *stream = NULL; ! 1039: php_url *resource = NULL; ! 1040: int result, recursive = options & PHP_STREAM_MKDIR_RECURSIVE; ! 1041: char tmp_line[512]; ! 1042: ! 1043: stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL TSRMLS_CC); ! 1044: if (!stream) { ! 1045: if (options & REPORT_ERRORS) { ! 1046: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", url); ! 1047: } ! 1048: goto mkdir_errexit; ! 1049: } ! 1050: ! 1051: if (resource->path == NULL) { ! 1052: if (options & REPORT_ERRORS) { ! 1053: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path provided in %s", url); ! 1054: } ! 1055: goto mkdir_errexit; ! 1056: } ! 1057: ! 1058: if (!recursive) { ! 1059: php_stream_printf(stream TSRMLS_CC, "MKD %s\r\n", resource->path); ! 1060: result = GET_FTP_RESULT(stream); ! 1061: } else { ! 1062: /* we look for directory separator from the end of string, thus hopefuly reducing our work load */ ! 1063: char *p, *e, *buf; ! 1064: ! 1065: buf = estrdup(resource->path); ! 1066: e = buf + strlen(buf); ! 1067: ! 1068: /* find a top level directory we need to create */ ! 1069: while ((p = strrchr(buf, '/'))) { ! 1070: *p = '\0'; ! 1071: php_stream_printf(stream TSRMLS_CC, "CWD %s\r\n", buf); ! 1072: result = GET_FTP_RESULT(stream); ! 1073: if (result >= 200 && result <= 299) { ! 1074: *p = '/'; ! 1075: break; ! 1076: } ! 1077: } ! 1078: if (p == buf) { ! 1079: php_stream_printf(stream TSRMLS_CC, "MKD %s\r\n", resource->path); ! 1080: result = GET_FTP_RESULT(stream); ! 1081: } else { ! 1082: php_stream_printf(stream TSRMLS_CC, "MKD %s\r\n", buf); ! 1083: result = GET_FTP_RESULT(stream); ! 1084: if (result >= 200 && result <= 299) { ! 1085: if (!p) { ! 1086: p = buf; ! 1087: } ! 1088: /* create any needed directories if the creation of the 1st directory worked */ ! 1089: while (++p != e) { ! 1090: if (*p == '\0' && *(p + 1) != '\0') { ! 1091: *p = '/'; ! 1092: php_stream_printf(stream TSRMLS_CC, "MKD %s\r\n", buf); ! 1093: result = GET_FTP_RESULT(stream); ! 1094: if (result < 200 || result > 299) { ! 1095: if (options & REPORT_ERRORS) { ! 1096: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tmp_line); ! 1097: } ! 1098: break; ! 1099: } ! 1100: } ! 1101: } ! 1102: } ! 1103: } ! 1104: efree(buf); ! 1105: } ! 1106: ! 1107: php_url_free(resource); ! 1108: php_stream_close(stream); ! 1109: ! 1110: if (result < 200 || result > 299) { ! 1111: /* Failure */ ! 1112: return 0; ! 1113: } ! 1114: ! 1115: return 1; ! 1116: ! 1117: mkdir_errexit: ! 1118: if (resource) { ! 1119: php_url_free(resource); ! 1120: } ! 1121: if (stream) { ! 1122: php_stream_close(stream); ! 1123: } ! 1124: return 0; ! 1125: } ! 1126: /* }}} */ ! 1127: ! 1128: /* {{{ php_stream_ftp_rmdir ! 1129: */ ! 1130: static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) ! 1131: { ! 1132: php_stream *stream = NULL; ! 1133: php_url *resource = NULL; ! 1134: int result; ! 1135: char tmp_line[512]; ! 1136: ! 1137: stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL TSRMLS_CC); ! 1138: if (!stream) { ! 1139: if (options & REPORT_ERRORS) { ! 1140: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", url); ! 1141: } ! 1142: goto rmdir_errexit; ! 1143: } ! 1144: ! 1145: if (resource->path == NULL) { ! 1146: if (options & REPORT_ERRORS) { ! 1147: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path provided in %s", url); ! 1148: } ! 1149: goto rmdir_errexit; ! 1150: } ! 1151: ! 1152: php_stream_printf(stream TSRMLS_CC, "RMD %s\r\n", resource->path); ! 1153: result = GET_FTP_RESULT(stream); ! 1154: ! 1155: if (result < 200 || result > 299) { ! 1156: if (options & REPORT_ERRORS) { ! 1157: php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tmp_line); ! 1158: } ! 1159: goto rmdir_errexit; ! 1160: } ! 1161: ! 1162: php_url_free(resource); ! 1163: php_stream_close(stream); ! 1164: ! 1165: return 1; ! 1166: ! 1167: rmdir_errexit: ! 1168: if (resource) { ! 1169: php_url_free(resource); ! 1170: } ! 1171: if (stream) { ! 1172: php_stream_close(stream); ! 1173: } ! 1174: return 0; ! 1175: } ! 1176: /* }}} */ ! 1177: ! 1178: static php_stream_wrapper_ops ftp_stream_wops = { ! 1179: php_stream_url_wrap_ftp, ! 1180: php_stream_ftp_stream_close, /* stream_close */ ! 1181: php_stream_ftp_stream_stat, ! 1182: php_stream_ftp_url_stat, /* stat_url */ ! 1183: php_stream_ftp_opendir, /* opendir */ ! 1184: "ftp", ! 1185: php_stream_ftp_unlink, /* unlink */ ! 1186: php_stream_ftp_rename, /* rename */ ! 1187: php_stream_ftp_mkdir, /* mkdir */ ! 1188: php_stream_ftp_rmdir /* rmdir */ ! 1189: }; ! 1190: ! 1191: PHPAPI php_stream_wrapper php_stream_ftp_wrapper = { ! 1192: &ftp_stream_wops, ! 1193: NULL, ! 1194: 1 /* is_url */ ! 1195: }; ! 1196: ! 1197: ! 1198: /* ! 1199: * Local variables: ! 1200: * tab-width: 4 ! 1201: * c-basic-offset: 4 ! 1202: * End: ! 1203: * vim600: sw=4 ts=4 fdm=marker ! 1204: * vim<600: sw=4 ts=4 ! 1205: */