in hphp/runtime/server/upload.cpp [710:1176]
void rfc1867PostHandler(Transport* transport,
Array& post,
Array& files,
size_t content_length,
const void*& data, size_t& size,
const std::string boundary) {
char *s=nullptr, *start_arr=nullptr;
std::string array_index, abuf;
char *lbuf=nullptr;
int total_bytes=0, cancel_upload=0, is_arr_upload=0, array_len=0;
int max_file_size=0, skip_upload=0, anonindex=0;
std::set<std::string> &uploaded_files = s_rfc1867_data->rfc1867UploadedFiles;
multipart_buffer *mbuff;
int fd=-1;
void *event_extra_data = nullptr;
unsigned int llen = 0;
int upload_count = RuntimeOption::MaxFileUploads;
/* Initialize the buffer */
if (!(mbuff = multipart_buffer_new(transport,
(const char *)data, size, boundary))) {
Logger::Warning("Unable to initialize the input buffer");
return;
}
/* Initialize $_FILES[] */
s_rfc1867_data->rfc1867ProtectedVariables.clear();
uploaded_files.clear();
int (*php_rfc1867_callback)(apc_rfc1867_data *rfc1867ApcData,
unsigned int event, void *event_data,
void **extra) = s_rfc1867_data->rfc1867Callback;
if (php_rfc1867_callback != nullptr) {
multipart_event_start event_start;
event_start.content_length = content_length;
if (php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_START, &event_start,
&event_extra_data) == FAILURE) {
goto fileupload_done;
}
}
while (!multipart_buffer_eof(mbuff)) {
std::string temp_filename;
char buff[FILLUNIT];
char *cd=nullptr,*param=nullptr,*filename=nullptr, *tmp=nullptr;
size_t blen=0, wlen=0;
off_t offset;
header_list header;
if (!multipart_buffer_headers(mbuff, header)) {
goto fileupload_done;
}
if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) {
char *pair=nullptr;
int end=0;
while (isspace(*cd)) {
++cd;
}
while (*cd && (pair = php_ap_getword(&cd, ';')))
{
char *key=nullptr, *word = pair;
while (isspace(*cd)) {
++cd;
}
if (strchr(pair, '=')) {
key = php_ap_getword(&pair, '=');
if (!strcasecmp(key, "name")) {
if (param) {
free(param);
}
param = php_ap_getword_conf(&pair);
} else if (!strcasecmp(key, "filename")) {
if (filename) {
free(filename);
}
filename = php_ap_getword_conf(&pair);
}
}
if (key) free(key);
free(word);
}
/* Normal form variable, safe to read all data into memory */
if (!filename && param) {
unsigned int value_len;
char *value = multipart_buffer_read_body(mbuff, &value_len);
unsigned int new_val_len; /* Dummy variable */
if (!value) {
value = strdup("");
}
new_val_len = value_len;
if (php_rfc1867_callback != nullptr) {
multipart_event_formdata event_formdata;
size_t newlength = new_val_len;
event_formdata.post_bytes_processed = mbuff->read_post_bytes;
event_formdata.name = param;
event_formdata.value = &value;
event_formdata.length = new_val_len;
event_formdata.newlength = &newlength;
if (php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_FORMDATA, &event_formdata,
&event_extra_data) == FAILURE) {
free(param);
free(value);
continue;
}
new_val_len = newlength;
}
String val(value, new_val_len, CopyString);
safe_php_register_variable(param, val, post, 0);
if (!strcasecmp(param, "MAX_FILE_SIZE")) {
max_file_size = atol(value);
}
free(param);
free(value);
continue;
}
/* If file_uploads=off, skip the file part */
if (!RuntimeOption::EnableFileUploads) {
skip_upload = 1;
} else if (upload_count <= 0) {
Logger::Warning(
"Maximum number of allowable file uploads has been exceeded"
);
skip_upload = 1;
}
/* Return with an error if the posted data is garbled */
if (!param && !filename) {
Logger::Warning("File Upload Mime headers garbled");
goto fileupload_done;
}
if (!param) {
param = (char*)malloc(MAX_SIZE_ANONNAME);
snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++);
}
/* New Rule: never repair potential malicious user input */
if (!skip_upload) {
tmp = param;
long c = 0;
while (*tmp) {
if (*tmp == '[') {
c++;
} else if (*tmp == ']') {
c--;
if (tmp[1] && tmp[1] != '[') {
skip_upload = 1;
break;
}
}
if (c < 0) {
skip_upload = 1;
break;
}
tmp++;
}
}
total_bytes = cancel_upload = 0;
if (!skip_upload) {
/* Handle file */
char path[PATH_MAX];
// open a temporary file
snprintf(path, sizeof(path), "%s/XXXXXX",
RuntimeOption::UploadTmpDir.c_str());
fd = mkstemp(path);
upload_count--;
if (fd == -1) {
Logger::Warning("Unable to open temporary file");
Logger::Warning("File upload error - unable to create a "
"temporary file");
cancel_upload = UPLOAD_ERROR_E;
}
temp_filename = path;
}
if (!skip_upload && php_rfc1867_callback != nullptr) {
multipart_event_file_start event_file_start;
event_file_start.post_bytes_processed = mbuff->read_post_bytes;
event_file_start.name = param;
event_file_start.filename = &filename;
if (php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_FILE_START,
&event_file_start,
&event_extra_data) == FAILURE) {
if (!temp_filename.empty()) {
if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */
close(fd);
unlink(temp_filename.c_str());
}
}
temp_filename="";
free(param);
free(filename);
continue;
}
}
if (skip_upload) {
free(param);
free(filename);
continue;
}
if (strlen(filename) == 0) {
Logger::Verbose("No file uploaded");
cancel_upload = UPLOAD_ERROR_D;
}
offset = 0;
end = 0;
while (!cancel_upload &&
(blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end)))
{
if (php_rfc1867_callback != nullptr) {
multipart_event_file_data event_file_data;
event_file_data.post_bytes_processed = mbuff->read_post_bytes;
event_file_data.offset = offset;
event_file_data.data = buff;
event_file_data.length = blen;
event_file_data.newlength = &blen;
if (php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_FILE_DATA,
&event_file_data,
&event_extra_data) == FAILURE) {
cancel_upload = UPLOAD_ERROR_X;
continue;
}
}
if (VirtualHost::GetUploadMaxFileSize() > 0 &&
total_bytes > VirtualHost::GetUploadMaxFileSize()) {
Logger::Verbose("upload_max_filesize of %" PRId64 " bytes exceeded "
"- file [%s=%s] not saved",
VirtualHost::GetUploadMaxFileSize(),
param, filename);
cancel_upload = UPLOAD_ERROR_A;
} else if (max_file_size && (total_bytes > max_file_size)) {
Logger::Verbose("MAX_FILE_SIZE of %d bytes exceeded - "
"file [%s=%s] not saved",
max_file_size, param, filename);
cancel_upload = UPLOAD_ERROR_B;
} else if (blen > 0) {
wlen = folly::writeFull(fd, buff, blen);
if (wlen < blen) {
Logger::Verbose("Only %zd bytes were written, expected to "
"write %zd", wlen, blen);
cancel_upload = UPLOAD_ERROR_F;
} else {
total_bytes += wlen;
}
offset += wlen;
}
}
if (fd!=-1) { /* may not be initialized if file could not be created */
close(fd);
}
if (!cancel_upload && !end) {
Logger::Verbose("Missing mime boundary at the end of the data for "
"file %s", strlen(filename) > 0 ? filename : "");
cancel_upload = UPLOAD_ERROR_C;
}
if (strlen(filename) > 0 && total_bytes == 0 && !cancel_upload) {
Logger::Verbose("Uploaded file size 0 - file [%s=%s] not saved",
param, filename);
cancel_upload = 5;
}
if (php_rfc1867_callback != nullptr) {
multipart_event_file_end event_file_end;
event_file_end.post_bytes_processed = mbuff->read_post_bytes;
event_file_end.temp_filename = temp_filename.c_str();
event_file_end.cancel_upload = cancel_upload;
if (php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_FILE_END,
&event_file_end,
&event_extra_data) == FAILURE) {
cancel_upload = UPLOAD_ERROR_X;
}
}
if (cancel_upload && cancel_upload != UPLOAD_ERROR_C) {
if (!temp_filename.empty()) {
if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */
unlink(temp_filename.c_str());
}
}
temp_filename="";
} else {
s_rfc1867_data->rfc1867UploadedFiles.insert(temp_filename);
}
/* is_arr_upload is true when name of file upload field
* ends in [.*]
* start_arr is set to point to 1st [
*/
is_arr_upload = (start_arr = strchr(param,'[')) &&
(param[strlen(param)-1] == ']');
if (is_arr_upload) {
array_len = strlen(start_arr);
array_index = std::string(start_arr+1, array_len-2);
}
/* Add $foo_name */
if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) {
llen = strlen(param);
lbuf = (char *) realloc(lbuf, llen + MAX_SIZE_OF_INDEX + 1);
llen += MAX_SIZE_OF_INDEX + 1;
}
if (is_arr_upload) {
abuf = std::string(param, strlen(param)-array_len);
snprintf(lbuf, llen, "%s_name[%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s_name", param);
}
/* The \ check should technically be needed for win32 systems only
* where it is a valid path separator. However, IE in all it's wisdom
* always sends the full path of the file on the user's filesystem,
* which means that unless the user does basename() they get a bogus
* file name. Until IE's user base drops to nill or problem is fixed
* this code must remain enabled for all systems.
*/
s = strrchr(filename, '\\');
if ((tmp = strrchr(filename, '/')) > s) {
s = tmp;
}
/* Add $foo[name] */
if (is_arr_upload) {
snprintf(lbuf, llen, "%s[name][%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s[name]", param);
}
if (s) {
String val(s+1, strlen(s+1), CopyString);
safe_php_register_variable(lbuf, val, files, 0);
} else {
String val(filename, strlen(filename), CopyString);
safe_php_register_variable(lbuf, val, files, 0);
}
free(filename);
s = nullptr;
/* Possible Content-Type: */
if ((cancel_upload && cancel_upload != UPLOAD_ERROR_C) ||
!(cd = php_mime_get_hdr_value(header, "Content-Type"))) {
cd = "";
} else {
/* fix for Opera 6.01 */
s = strchr(cd, ';');
if (s != nullptr) {
*s = '\0';
}
}
/* Add $foo[type] */
if (is_arr_upload) {
snprintf(lbuf, llen, "%s[type][%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s[type]", param);
}
String val(cd, strlen(cd), CopyString);
safe_php_register_variable(lbuf, val, files, 0);
/* Restore Content-Type Header */
if (s != nullptr) {
*s = ';';
}
s = "";
/* Initialize variables */
add_protected_variable(param);
Variant tempFileName(temp_filename);
/* Add $foo[tmp_name] */
if (is_arr_upload) {
snprintf(lbuf, llen, "%s[tmp_name][%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s[tmp_name]", param);
}
add_protected_variable(lbuf);
safe_php_register_variable(lbuf, tempFileName, files, 1);
Variant file_size, error_type;
error_type = cancel_upload;
/* Add $foo[error] */
if (cancel_upload) {
file_size = 0;
} else {
file_size = total_bytes;
}
if (is_arr_upload) {
snprintf(lbuf, llen, "%s[error][%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s[error]", param);
}
safe_php_register_variable(lbuf, error_type, files, 0);
/* Add $foo[size] */
if (is_arr_upload) {
snprintf(lbuf, llen, "%s[size][%s]",
abuf.c_str(), array_index.c_str());
} else {
snprintf(lbuf, llen, "%s[size]", param);
}
safe_php_register_variable(lbuf, file_size, files, 0);
free(param);
}
}
fileupload_done:
data = mbuff->post_data;
size = mbuff->post_size;
if (php_rfc1867_callback != nullptr) {
multipart_event_end event_end;
event_end.post_bytes_processed = mbuff->read_post_bytes;
php_rfc1867_callback(&s_rfc1867_data->rfc1867ApcData,
MULTIPART_EVENT_END, &event_end, &event_extra_data);
}
if (lbuf) free(lbuf);
s_rfc1867_data->rfc1867ProtectedVariables.clear();
if (mbuff->boundary_next) free(mbuff->boundary_next);
if (mbuff->boundary) free(mbuff->boundary);
if (mbuff->buffer) free(mbuff->buffer);
if (mbuff) free(mbuff);
}