com.caucho.quercus.env.Post Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quercus Show documentation
Show all versions of quercus Show documentation
A PHP engine implemented in 100% Java
/*
* Copyright (c) 1998-2014 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.env;
import com.caucho.quercus.lib.string.StringModule;
import com.caucho.quercus.lib.string.StringUtility;
import com.caucho.quercus.lib.file.FileModule;
import com.caucho.quercus.servlet.api.QuercusHttpServletRequest;
import com.caucho.util.L10N;
import com.caucho.vfs.FilePath;
import com.caucho.vfs.MultipartStream;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.VfsStream;
import com.caucho.vfs.WriteStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
/**
* Handling of POST requests.
*/
public class Post
{
private static final L10N L = new L10N(Post.class);
static void fillPost(Env env,
ArrayValue postArray,
ArrayValue files,
QuercusHttpServletRequest request,
boolean addSlashesToValues,
boolean isAllowUploads)
{
InputStream is = null;
try {
String encoding = request.getCharacterEncoding();
String contentType = request.getHeader("Content-Type");
is = request.getInputStream();
fillPost(env,
postArray,
files,
is,
contentType,
encoding,
Integer.MAX_VALUE,
addSlashesToValues,
isAllowUploads);
if (postArray.getSize() == 0) {
// needs to be last or else this function will consume the inputstream
putRequestMap(env, postArray, files, request,
addSlashesToValues, isAllowUploads);
}
}
catch (IOException e) {
env.warning(e);
}
finally {
try {
if (is != null) {
is.close();
}
}
catch (IOException e) {
}
}
}
static void fillPost(Env env,
ArrayValue postArray,
ArrayValue files,
InputStream is,
String contentType,
String encoding,
int contentLength,
boolean addSlashesToValues,
boolean isAllowUploads)
{
long maxPostSize = env.getIniBytes("post_max_size", 0);
try {
if (encoding == null)
encoding = env.getHttpInputEncoding();
if (contentType != null
&& contentType.startsWith("multipart/form-data")) {
String boundary = getBoundary(contentType);
ReadStream rs = new ReadStream(new VfsStream(is, null));
if (boundary == null) {
env.warning(L.l("multipart/form-data POST is missing boundary"));
return;
}
MultipartStream ms = new MultipartStream(rs, boundary);
if (encoding != null)
ms.setEncoding(encoding);
readMultipartStream(env, ms, postArray, files,
addSlashesToValues, isAllowUploads);
rs.close();
if (rs.getLength() > maxPostSize) {
env.warning(L.l("POST length of {0} exceeds max size of {1}",
rs.getLength(),
maxPostSize));
postArray.clear();
files.clear();
return;
}
}
else {
StringValue bb = env.createBinaryBuilder();
bb.appendReadAll(is, Integer.MAX_VALUE);
if (bb.length() > maxPostSize) {
env.warning(L.l("POST length of {0} exceeds max size of {1}",
bb.length(),
maxPostSize));
return;
}
env.setInputData(bb);
if (contentType != null
&& contentType.startsWith("application/x-www-form-urlencoded"))
StringUtility.parseStr(env, bb, postArray, true, encoding, true);
}
}
catch (IOException e) {
env.warning(e);
}
}
private static void readMultipartStream(Env env,
MultipartStream ms,
ArrayValue postArray,
ArrayValue files,
boolean addSlashesToValues,
boolean isAllowUploads)
throws IOException
{
ReadStream is;
while ((is = ms.openRead()) != null) {
String attr = (String) ms.getAttribute("content-disposition");
if (attr == null || ! attr.startsWith("form-data")) {
// XXX: is this an error?
continue;
}
String name = getAttribute(attr, "name", addSlashesToValues);
String filename = getAttribute(attr, "filename", addSlashesToValues);
if (filename != null) {
int slashIndex = filename.lastIndexOf('/');
int slashIndex2 = filename.lastIndexOf('\\');
slashIndex = Math.max(slashIndex, slashIndex2);
if (slashIndex >= 0)
filename = filename.substring(slashIndex + 1);
}
int bracketIndex = -1;
if (name != null)
bracketIndex = name.lastIndexOf(']');
if (bracketIndex >= 0 && bracketIndex < name.length() - 1) {
// php/085c
}
else if (filename == null) {
StringValue value = env.createStringBuilder();
value.appendReadAll(is, Integer.MAX_VALUE);
if (name != null) {
addFormValue(env, postArray, name, value, null, addSlashesToValues, true);
}
else {
env.warning(L.l("file upload is missing name and filename"));
}
}
else {
if (! isAllowUploads) {
continue;
}
String tmpName = "";
long tmpLength = 0;
// A POST file upload with an empty string as the filename does not
// create a temp file in the upload directory.
if (filename.length() > 0) {
Path tmpPath = env.getUploadDirectory().createTempFile("php", ".tmp");
env.addRemovePath(tmpPath);
WriteStream os = tmpPath.openWrite();
try {
os.writeStream(is);
} finally {
os.close();
}
tmpName = tmpPath.getFullPath();
tmpLength = tmpPath.getLength();
}
// php/0865
//
// A header like "Content-Type: image/gif" indicates the mime type
// for an uploaded file.
String mimeType = getAttribute(attr, "mime-type", addSlashesToValues);
if (mimeType == null) {
mimeType = (String) ms.getAttribute("content-type");
// php/085f
if (mimeType != null && mimeType.endsWith(";"))
mimeType = mimeType.substring(0, mimeType.length() - 1);
}
// php/0864
//
// mime type is empty string when no file is uploaded.
if (filename.length() == 0) {
mimeType = "";
}
long maxFileSize = Long.MAX_VALUE;
Value maxFileSizeV = postArray.get(env.createString("MAX_FILE_SIZE"));
if (! maxFileSizeV.isNull())
maxFileSize = maxFileSizeV.toLong();
if (name != null) {
addFormFile(env, files, name, filename, tmpName,
mimeType, tmpLength, addSlashesToValues, maxFileSize);
}
else {
addFormFile(env, files, filename, tmpName,
mimeType, tmpLength, addSlashesToValues, maxFileSize);
}
}
}
}
private static void addFormFile(Env env,
ArrayValue files,
String fileName,
String tmpName,
String mimeType,
long fileLength,
boolean addSlashesToValues,
long maxFileSize)
{
ArrayValue entry = new ArrayValueImpl();
int error;
// php/1667
long uploadMaxFilesize
= env.getIniBytes("upload_max_filesize", 2 * 1024 * 1024);
// php/085j
fileName = fileName.replace("\u0000", "");
if (fileName.length() == 0)
// php/0864
error = FileModule.UPLOAD_ERR_NO_FILE;
else if (fileLength > uploadMaxFilesize)
error = FileModule.UPLOAD_ERR_INI_SIZE;
else if (fileLength > maxFileSize)
error = FileModule.UPLOAD_ERR_FORM_SIZE;
else
error = FileModule.UPLOAD_ERR_OK;
addFormValue(env, entry, "name", env.createString(fileName),
null, addSlashesToValues, true);
long size;
if (error != FileModule.UPLOAD_ERR_INI_SIZE) {
size = fileLength;
}
else {
mimeType = "";
tmpName = "";
size = 0;
}
if (mimeType != null) {
addFormValue(env, entry, "type", env.createString(mimeType),
null, addSlashesToValues, true);
entry.put("type", mimeType);
}
addFormValue(env, entry, "tmp_name", env.createString(tmpName),
null, addSlashesToValues, true);
addFormValue(env, entry, "error", LongValue.create(error),
null, addSlashesToValues, true);
addFormValue(env, entry, "size", LongValue.create(size),
null, addSlashesToValues, true);
addFormValue(env, files, null, entry, null, addSlashesToValues, true);
}
private static void addFormFile(Env env,
ArrayValue files,
String name,
String fileName,
String tmpName,
String mimeType,
long fileLength,
boolean addSlashesToValues,
long maxFileSize)
{
int p = name.indexOf('[');
String index = "";
if (p >= 0) {
index = name.substring(p);
name = name.substring(0, p);
}
// php/085j
name = name.replace("\u0000", "");
StringValue nameValue = env.createString(name);
Value v = files.get(nameValue).toValue();
ArrayValue entry = null;
if (v instanceof ArrayValue)
entry = (ArrayValue) v;
if (entry == null) {
entry = new ArrayValueImpl();
files.put(nameValue, entry);
}
int error;
// php/1667
long uploadMaxFilesize
= env.getIniBytes("upload_max_filesize", 2 * 1024 * 1024);
// php/085j
fileName = fileName.replace("\u0000", "");
if (fileName.length() == 0)
// php/0864
error = FileModule.UPLOAD_ERR_NO_FILE;
else if (fileLength > uploadMaxFilesize)
error = FileModule.UPLOAD_ERR_INI_SIZE;
else if (fileLength > maxFileSize)
error = FileModule.UPLOAD_ERR_FORM_SIZE;
else
error = FileModule.UPLOAD_ERR_OK;
addFormValue(env, entry, "name" + index, env.createString(fileName),
null, addSlashesToValues, true);
long size;
if (error == FileModule.UPLOAD_ERR_OK) {
size = fileLength;
}
else {
mimeType = "";
tmpName = "";
size = 0;
}
if (mimeType != null) {
addFormValue(env, entry, "type" + index, env.createString(mimeType),
null, addSlashesToValues, true);
}
addFormValue(env, entry, "tmp_name" + index, env.createString(tmpName),
null, addSlashesToValues, true);
addFormValue(env, entry, "error" + index, LongValue.create(error),
null, addSlashesToValues, true);
addFormValue(env, entry, "size" + index, LongValue.create(size),
null, addSlashesToValues, true);
addFormValue(env, files, name, entry, null, addSlashesToValues, true);
}
public static void addFormValue(Env env,
ArrayValue array,
String key,
String []formValueList,
boolean addSlashesToValues,
boolean isReplaceSpacesWithUnderscores)
{
// php/081b
String formValue = formValueList[formValueList.length - 1];
Value value;
if (formValue != null)
value = env.createString(formValue);
else
value = NullValue.NULL;
addFormValue(env, array, key,
value,
formValueList,
addSlashesToValues,
isReplaceSpacesWithUnderscores);
}
public static void addFormValue(Env env,
ArrayValue array,
String key,
Value formValue,
String []formValueList,
boolean addSlashesToValues,
boolean isReplaceSpacesWithUnderscores)
{
int p = -1;
int q = -1;
if (key != null) {
p = key.indexOf('[');
q = key.indexOf(']', p);
}
if (p >= 0 && p < q) {
if (p == 0) {
// php/080e
return;
}
int keyStart = 0;
int keyEnd = p;
while (p > 0 && p < q) {
String currentKey = key.substring(keyStart, keyEnd);
if (keyStart == 0) {
// php/081p
currentKey = currentKey.replace('.', '_');
}
if (isReplaceSpacesWithUnderscores) {
// php/080h
currentKey = currentKey.replace(' ', '_');
// php/080k
isReplaceSpacesWithUnderscores = false;
}
StringValue currentKeyValue = env.createString(currentKey);
Value currentArray = array.get(currentKeyValue);
if (! currentArray.isArray()) {
currentArray = new ArrayValueImpl();
}
if (currentKeyValue.length() == 0) {
array.append(currentArray);
}
else {
array.put(currentKeyValue, currentArray);
}
array = currentArray.toArrayValue(env);
keyStart = p + 1;
keyEnd = q;
p = key.indexOf('[', q + 1);
q = key.indexOf(']', p + 1);
}
if (keyEnd > 0)
key = key.substring(keyStart, keyEnd);
else
key = key.substring(keyStart);
if (isReplaceSpacesWithUnderscores) {
// php/080h
key = key.replace(' ', '_');
}
if (key.length() == 0) {
if (formValueList != null) {
for (int i = 0; i < formValueList.length; i++) {
Value value;
if (formValueList[i] != null)
value = env.createString(formValueList[i]);
else
value = NullValue.NULL;
put(array, null, value, addSlashesToValues);
}
}
else
array.put(formValue);
}
else if ('0' <= key.charAt(0) && key.charAt(0) <= '9') {
put(array,
LongValue.create(StringValue.toLong(key)),
formValue,
addSlashesToValues);
}
else {
put(array, env.createString(key), formValue, addSlashesToValues);
}
}
else {
if (key != null) {
key = key.replace('.', '_');
if (isReplaceSpacesWithUnderscores) {
// php/080h
key = key.replace(' ', '_');
}
put(array, env.createString(key), formValue, addSlashesToValues);
}
else {
put(array, null, formValue, addSlashesToValues);
}
}
}
private static void put(ArrayValue array,
Value key,
Value value,
boolean addSlashes)
{
if (addSlashes && value.isString()) {
value = StringModule.addslashes(value.toStringValue());
}
if (key == null)
array.put(value);
else
array.put(key, value);
}
private static String getBoundary(String contentType)
{
int i = contentType.indexOf("boundary=");
if (i < 0)
return null;
i += "boundary=".length();
int length = contentType.length();
char ch;
if (length <= i)
return null;
else if ((ch = contentType.charAt(i)) == '\'') {
StringBuilder sb = new StringBuilder();
for (
i++;
i < length && (ch = contentType.charAt(i)) != '\'';
i++
) {
sb.append(ch);
}
return sb.toString();
}
else if (ch == '"') {
StringBuilder sb = new StringBuilder();
for (i++; i < length && (ch = contentType.charAt(i)) != '"'; i++) {
sb.append(ch);
}
return sb.toString();
}
else {
StringBuilder sb = new StringBuilder();
for (/* intentionally left empty */;
i < length
&& (ch = contentType.charAt(i)) != ' '
&& ch != ';'
&& ch != ',';
i++) {
sb.append(ch);
}
return sb.toString();
}
}
private static String getAttribute(String attr,
String name,
boolean addSlashesToValues)
{
if (attr == null)
return null;
int length = attr.length();
int i = attr.indexOf(name);
if (i < 0)
return null;
while (true) {
char ch = attr.charAt(i - 1);
if (i > 0 && ch != ' ' && ch != ';') {
i = attr.indexOf(name, i + name.length());
}
else
break;
if (i < 0)
return null;
}
for (i += name.length(); i < length && attr.charAt(i) != '='; i++) {
}
for (i++; i < length && attr.charAt(i) == ' '; i++) {
}
StringBuilder value = new StringBuilder();
if (i < length && attr.charAt(i) == '\'') {
for (i++; i < length && attr.charAt(i) != '\''; i++) {
char ch = attr.charAt(i);
if (ch == '"' && addSlashesToValues)
break;
value.append(ch);
}
}
else if (i < length && attr.charAt(i) == '"') {
for (i++; i < length && attr.charAt(i) != '"'; i++) {
char ch = attr.charAt(i);
if (ch == '\'' && addSlashesToValues)
break;
value.append(ch);
}
}
else if (i < length) {
char ch;
for (; i < length && (ch = attr.charAt(i)) != ' ' && ch != ';'; i++)
value.append(ch);
}
return value.toString();
}
private static void putRequestMap(Env env,
ArrayValue post,
ArrayValue files,
QuercusHttpServletRequest request,
boolean addSlashesToValues,
boolean isAllowUploads)
{
// this call consumes the inputstream
Map map = request.getParameterMap();
if (map == null)
return;
long maxFileSize = Long.MAX_VALUE;
Value maxFileSizeV = post.get(env.createString("MAX_FILE_SIZE"));
if (maxFileSizeV.isNull())
maxFileSize = maxFileSizeV.toLong();
if (isAllowUploads) {
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
int len = key.length();
if (len < 10 || ! key.endsWith(".filename"))
continue;
String name = key.substring(0, len - 9);
String []fileNames = request.getParameterValues(name + ".filename");
String []tmpNames = request.getParameterValues(name + ".file");
String []mimeTypes
= request.getParameterValues(name + ".content-type");
for (int i = 0; i < fileNames.length; i++) {
long fileLength = new FilePath(tmpNames[i]).getLength();
addFormFile(env, files, name, fileNames[i], tmpNames[i],
mimeTypes[i],
fileLength,
addSlashesToValues,
maxFileSize);
}
}
}
ArrayList keys = new ArrayList();
keys.addAll(request.getParameterMap().keySet());
Collections.sort(keys);
for (String key : keys) {
String []value = request.getParameterValues(key);
Post.addFormValue(env, post, key, value, addSlashesToValues, true);
}
}
}