/* {{{ php_ftp_fopen_connect */ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, char **opened_path, php_stream_context *context, php_stream **preuseid, php_url **presource, int *puse_ssl, int *puse_ssl_on_data) { php_stream *stream = NULL, *reuseid = NULL; php_url *resource = NULL; int result, use_ssl, use_ssl_on_data = 0, tmp_len; char tmp_line[512]; char *transport; int transport_len; resource = php_url_parse(path); if (resource == NULL || resource->path == NULL) { if (resource && presource) { *presource = resource; } return NULL; } use_ssl = resource->scheme && (strlen(resource->scheme) > 3) && resource->scheme[3] == 's'; /* use port 21 if one wasn't specified */ if (resource->port == 0) resource->port = 21; transport_len = (int)spprintf(&transport, 0, "tcp://%s:%d", resource->host, resource->port); stream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL); efree(transport); if (stream == NULL) { result = 0; /* silence */ goto connect_errexit; } php_stream_context_set(stream, context); php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0); /* Start talking to ftp server */ result = GET_FTP_RESULT(stream); if (result > 299 || result < 200) { php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result); goto connect_errexit; } if (use_ssl) { /* send the AUTH TLS request name */ php_stream_write_string(stream, "AUTH TLS\r\n"); /* get the response */ result = GET_FTP_RESULT(stream); if (result != 234) { /* AUTH TLS not supported try AUTH SSL */ php_stream_write_string(stream, "AUTH SSL\r\n"); /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { use_ssl = 0; } else { /* we must reuse the old SSL session id */ /* if we talk to an old ftpd-ssl */ reuseid = stream; } } else { /* encrypt data etc */ } } if (use_ssl) { if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; } /* set PBSZ to 0 */ php_stream_write_string(stream, "PBSZ 0\r\n"); /* ignore the response */ result = GET_FTP_RESULT(stream); /* set data connection protection level */ #if FTPS_ENCRYPT_DATA php_stream_write_string(stream, "PROT P\r\n"); /* get the response */ result = GET_FTP_RESULT(stream); use_ssl_on_data = (result >= 200 && result<=299) || reuseid; #else php_stream_write_string(stream, "PROT C\r\n"); /* get the response */ result = GET_FTP_RESULT(stream); #endif } #define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \ unsigned char *s = val, *e = s + val_len; \ while (s < e) { \ if (iscntrl(*s)) { \ php_stream_wrapper_log_error(wrapper, options, err_msg, val); \ goto connect_errexit; \ } \ s++; \ } \ } /* send the user name */ if (resource->user != NULL) { tmp_len = (int)php_raw_url_decode(resource->user, (int)strlen(resource->user)); PHP_FTP_CNTRL_CHK(resource->user, tmp_len, "Invalid login %s") php_stream_printf(stream, "USER %s\r\n", resource->user); } else { php_stream_write_string(stream, "USER anonymous\r\n"); } /* get the response */ result = GET_FTP_RESULT(stream); /* if a password is required, send it */ if (result >= 300 && result <= 399) { php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0); if (resource->pass != NULL) { tmp_len = (int)php_raw_url_decode(resource->pass, (int)strlen(resource->pass)); PHP_FTP_CNTRL_CHK(resource->pass, tmp_len, "Invalid password %s") php_stream_printf(stream, "PASS %s\r\n", resource->pass); } else { /* if the user has configured who they are, send that as the password */ if (FG(from_address)) { php_stream_printf(stream, "PASS %s\r\n", FG(from_address)); } else { php_stream_write_string(stream, "PASS anonymous\r\n"); } } /* read the response */ result = GET_FTP_RESULT(stream); if (result > 299 || result < 200) { php_stream_notify_error(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result); } else { php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result); } } if (result > 299 || result < 200) { goto connect_errexit; } if (puse_ssl) { *puse_ssl = use_ssl; } if (puse_ssl_on_data) { *puse_ssl_on_data = use_ssl_on_data; } if (preuseid) { *preuseid = reuseid; } if (presource) { *presource = resource; } return stream; connect_errexit: if (resource) { php_url_free(resource); } if (stream) { php_stream_close(stream); } return NULL; }
FILE *php_fopen_url_wrap_ftp(char *path, char *mode, int options, int *issock, int *socketd, char **opened_path) { FILE *fp=NULL; php_url *resource=NULL; char tmp_line[512]; unsigned short portno; char *scratch; int result; int i; char *tpath, *ttpath; resource = url_parse((char *) path); if (resource == NULL) { php_error(E_WARNING, "Invalid URL specified, %s", path); *issock = BAD_URL; return NULL; } else if (resource->path == NULL) { php_error(E_WARNING, "No file-path specified"); free_url(resource); *issock = BAD_URL; return NULL; } /* use port 21 if one wasn't specified */ if (resource->port == 0) resource->port = 21; *socketd = php_hostconnect(resource->host, resource->port, SOCK_STREAM, 0); if (*socketd == -1) goto errexit; #if 0 if ((fpc = fdopen(*socketd, "r+")) == NULL) { free_url(resource); return NULL; } #ifdef HAVE_SETVBUF if ((setvbuf(fpc, NULL, _IONBF, 0)) != 0) { free_url(resource); fclose(fpc); return NULL; } #endif #endif /* Start talking to ftp server */ result = php_get_ftp_result(*socketd); if (result > 299 || result < 200) goto errexit; /* send the user name */ SOCK_WRITE("USER ", *socketd); if (resource->user != NULL) { php_raw_url_decode(resource->user, strlen(resource->user)); SOCK_WRITE(resource->user, *socketd); } else { SOCK_WRITE("anonymous", *socketd); } SOCK_WRITE("\r\n", *socketd); /* get the response */ result = php_get_ftp_result(*socketd); /* if a password is required, send it */ if (result >= 300 && result <= 399) { SOCK_WRITE("PASS ", *socketd); if (resource->pass != NULL) { php_raw_url_decode(resource->pass, strlen(resource->pass)); SOCK_WRITE(resource->pass, *socketd); } else { /* if the user has configured who they are, send that as the password */ if (cfg_get_string("from", &scratch) == SUCCESS) { SOCK_WRITE(scratch, *socketd); } else { SOCK_WRITE("anonymous", *socketd); } } SOCK_WRITE("\r\n", *socketd); /* read the response */ result = php_get_ftp_result(*socketd); } if (result > 299 || result < 200) goto errexit; /* set the connection to be binary */ SOCK_WRITE("TYPE I\r\n", *socketd); result = php_get_ftp_result(*socketd); if (result > 299 || result < 200) goto errexit; /* find out the size of the file (verifying it exists) */ SOCK_WRITE("SIZE ", *socketd); SOCK_WRITE(resource->path, *socketd); SOCK_WRITE("\r\n", *socketd); /* read the response */ result = php_get_ftp_result(*socketd); if (mode[0] == 'r') { /* when reading file, it must exist */ if (result > 299 || result < 200) { php_error(E_WARNING, "File not found"); free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; errno = ENOENT; return NULL; } } else { /* when writing file, it must NOT exist */ if (result <= 299 && result >= 200) { php_error(E_WARNING, "File already exists"); free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; errno = EEXIST; return NULL; } } /* set up the passive connection */ /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */ SOCK_WRITE("EPSV\r\n", *socketd); while (SOCK_FGETS(tmp_line, sizeof(tmp_line)-1, *socketd) && !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) && isdigit((int) tmp_line[2]) && tmp_line[3] == ' ')); /* check if we got a 229 response */ if (strncmp(tmp_line, "229", 3)) { /* EPSV failed, let's try PASV */ SOCK_WRITE("PASV\r\n", *socketd); while (SOCK_FGETS(tmp_line, sizeof(tmp_line)-1, *socketd) && !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) && isdigit((int) tmp_line[2]) && tmp_line[3] == ' ')); /* make sure we got a 227 response */ if (strncmp(tmp_line, "227", 3)) goto errexit; /* parse pasv command (129,80,95,25,13,221) */ tpath = tmp_line; /* skip over the "227 Some message " part */ for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++); if (!*tpath) goto errexit; /* skip over the host ip, we just assume it's the same */ for (i = 0; i < 4; i++) { for (; isdigit((int) *tpath); tpath++); if (*tpath != ',') goto errexit; tpath++; } /* pull out the MSB of the port */ portno = (unsigned short) strtol(tpath, &ttpath, 10) * 256; if (ttpath == NULL) { /* didn't get correct response from PASV */ goto errexit; } tpath = ttpath; if (*tpath != ',') goto errexit; tpath++; /* pull out the LSB of the port */ portno += (unsigned short) strtol(tpath, &ttpath, 10); } else { /* parse epsv command (|||6446|) */ for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) { if (*tpath == '|') { i++; if (i == 3) break; } } if (i < 3) goto errexit; /* pull out the port */ portno = (unsigned short) strtol(tpath + 1, &ttpath, 10); } if (ttpath == NULL) { /* didn't get correct response from EPSV/PASV */ goto errexit; } if (mode[0] == 'r') { /* retrieve file */ SOCK_WRITE("RETR ", *socketd); } else { /* store file */ SOCK_WRITE("STOR ", *socketd); } if (resource->path != NULL) { SOCK_WRITE(resource->path, *socketd); } else { SOCK_WRITE("/", *socketd); } /* close control connection */ SOCK_WRITE("\r\nQUIT\r\n", *socketd); SOCK_FCLOSE(*socketd); /* open the data channel */ *socketd = php_hostconnect(resource->host, portno, SOCK_STREAM, 0); if (*socketd == -1) goto errexit; #if 0 if (mode[0] == 'r') { if ((fp = fdopen(*socketd, "r+")) == NULL) { free_url(resource); return NULL; } } else { if ((fp = fdopen(*socketd, "w+")) == NULL) { free_url(resource); return NULL; } } #ifdef HAVE_SETVBUF if ((setvbuf(fp, NULL, _IONBF, 0)) != 0) { free_url(resource); fclose(fp); return NULL; } #endif #endif free_url(resource); *issock = 1; return (fp); errexit: free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; }