/* * Copyright (c) 1998-2008 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.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 javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Set; /** * Handling of POST requests. */ public class Post { static void fillPost(Env env, ArrayValue postArray, ArrayValue files, HttpServletRequest request, boolean addSlashesToValues) { InputStream is = null; try { if (request.getMethod().equals("POST")) { String encoding = request.getCharacterEncoding(); if (encoding == null) encoding = env.getHttpInputEncoding(); String contentType = request.getHeader("Content-Type"); is = request.getInputStream(); if (contentType != null && contentType.startsWith("multipart/form-data")) { String boundary = getBoundary(contentType); ReadStream rs = new ReadStream(new VfsStream(is, null)); MultipartStream ms = new MultipartStream(rs, boundary); if (encoding != null) ms.setEncoding(encoding); readMultipartStream(env, ms, postArray, files, addSlashesToValues, encoding); rs.close(); } else { StringValue bb = env.createBinaryBuilder(); bb.appendReadAll(is, Integer.MAX_VALUE); env.setInputData(bb); if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) StringUtility.parseStr(env, bb, postArray, false, encoding); } if (postArray.getSize() == 0) { // needs to be last or else this function will consume the inputstream putRequestMap(env, postArray, files, request, addSlashesToValues, encoding); } } } catch (IOException e) { env.warning(e); } finally { try { if (is != null) is.close(); } catch (IOException e) { } } } private static void readMultipartStream(Env env, MultipartStream ms, ArrayValue postArray, ArrayValue files, boolean addSlashesToValues, String encoding) 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"); String filename = getAttribute(attr, "filename"); if (filename == null) { byte[] buf = new byte[1]; int ch, buf_len = 0; while ((ch = is.read()) >= 0) { if (buf_len >= buf.length) { byte[] new_buf = new byte[buf.length * 2]; System.arraycopy(buf, 0, new_buf, 0, buf.length); buf = new_buf; } buf[buf_len++] = (byte)ch; } addFormValue(env, postArray, name, env.isUnicodeSemantics() ? new UnicodeBuilderValue( new String(buf, 0, buf_len, encoding)): new StringBuilderValue(buf, 0, buf_len), null, addSlashesToValues, encoding); } else { 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"); if (mimeType == null) { mimeType = (String) ms.getAttribute("content-type"); } // php/0864 // // mime type is empty string when no file is uploaded. if (filename.length() == 0) { mimeType = ""; } addFormFile(env, files, name, filename, tmpName, mimeType, tmpLength, addSlashesToValues, encoding); } } } private static void addFormFile(Env env, ArrayValue files, String name, String fileName, String tmpName, String mimeType, long fileLength, boolean addSlashesToValues, String encoding) { int p = name.indexOf('['); String index = ""; if (p >= 0) { index = name.substring(p); name = name.substring(0, p); } StringValue nameValue = env.createString(name, encoding); 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); if (fileName.length() == 0) // php/0864 error = FileModule.UPLOAD_ERR_NO_FILE; else if (fileLength > uploadMaxFilesize) error = FileModule.UPLOAD_ERR_INI_SIZE; else error = FileModule.UPLOAD_ERR_OK; addFormValue(env, entry, "name" + index, env.createString(fileName, encoding), null, addSlashesToValues, encoding); long size; if (error != FileModule.UPLOAD_ERR_INI_SIZE) { size = fileLength; } else { mimeType = ""; tmpName = ""; size = 0; } if (mimeType != null) { addFormValue(env, entry, "type" + index, env.createString(mimeType, encoding), null, addSlashesToValues, encoding); entry.put("type", mimeType); } addFormValue(env, entry, "tmp_name" + index, env.createString(tmpName, encoding), null, addSlashesToValues, encoding); addFormValue(env, entry, "error" + index, LongValue.create(error), null, addSlashesToValues, encoding); addFormValue(env, entry, "size" + index, LongValue.create(size), null, addSlashesToValues, encoding); addFormValue(env, files, name, entry, null, addSlashesToValues, encoding); } public static void addFormValue(Env env, ArrayValue array, String key, String []formValueList, boolean addSlashesToValues, String encoding) { // php/081b String formValue = formValueList[formValueList.length - 1]; Value value; if (formValue != null) value = env.createString(formValue, encoding); else value = NullValue.NULL; addFormValue(env, array, key, value, formValueList, addSlashesToValues, encoding); } public static void addFormValue(Env env, ArrayValue array, String key, Value formValue, String []formValueList, boolean addSlashesToValues, String encoding) { int p = key.indexOf('['); int q = key.indexOf(']', p); if (p >= 0 && p < q) { String index = key; Value keyValue; Value existingValue; if (p > 0) { key = key.substring(0, p); key = key.replaceAll("\\.", "_"); keyValue = env.createString(key, encoding); existingValue = array.get(keyValue); if (existingValue == null || ! existingValue.isset()) { existingValue = new ArrayValueImpl(); array.put(keyValue, existingValue); } else if (! existingValue.isArray()) { //existing is overwritten // php/115g existingValue = new ArrayValueImpl(); array.put(keyValue, existingValue); } array = (ArrayValue) existingValue; } int p1; while ((p1 = index.indexOf('[', q)) > 0) { key = index.substring(p + 1, q); if (key.equals("")) { existingValue = new ArrayValueImpl(); array.put(existingValue); } else { keyValue = env.createString(key, encoding); existingValue = array.get(keyValue); if (existingValue == null || ! existingValue.isset()) { existingValue = new ArrayValueImpl(); array.put(keyValue, existingValue); } else if (! existingValue.isArray()) { existingValue = new ArrayValueImpl().put(existingValue); array.put(keyValue, existingValue); } } array = (ArrayValue) existingValue; p = p1; q = index.indexOf(']', p); } if (q > 0) index = index.substring(p + 1, q); else index = index.substring(p + 1); if (index.equals("")) { if (formValueList != null) { for (int i = 0; i < formValueList.length; i++) { Value value; if (formValueList[i] != null) value = env.createString(formValueList[i], encoding); else value = NullValue.NULL; put(env, array, null, value, addSlashesToValues); } } else array.put(formValue); } else if ('0' <= index.charAt(0) && index.charAt(0) <= '9') put(env, array, LongValue.create(StringValue.toLong(index)), formValue, addSlashesToValues); else put(env, array, env.createString(index, encoding), formValue, addSlashesToValues); } else { key = key.replaceAll("\\.", "_"); put(env, array, env.createString(key, encoding), formValue, addSlashesToValues); } } private static void put(Env env, ArrayValue array, Value key, Value value, boolean addSlashes) { if (addSlashes && (value instanceof StringValue)) { value = StringModule.addslashes(value.toStringValue(env)); } 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 (; i < length && (ch = contentType.charAt(i)) != ' ' && ch != ';'; i++) { sb.append(ch); } return sb.toString(); } } private static String getAttribute(String attr, String name) { if (attr == null) return null; int length = attr.length(); int i = attr.indexOf(name); 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++) value.append(attr.charAt(i)); } else if (i < length && attr.charAt(i) == '"') { for (i++; i < length && attr.charAt(i) != '"'; i++) value.append(attr.charAt(i)); } else if (i < length) { char ch; for (; i < length && (ch = attr.charAt(i)) != ' ' && ch != ';'; i++) value.append(ch); } return value.toString(); } @SuppressWarnings("unchecked") private static void putRequestMap(Env env, ArrayValue post, ArrayValue files, HttpServletRequest request, boolean addSlashesToValues, String encoding) { // this call consumes the inputstream Map<String,String[]> map = (Map<String,String[]>)request.getParameterMap(); if (map == null) return; for (Map.Entry<String,String[]> 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, encoding); } } ArrayList<String> keys = new ArrayList<String>(); keys.addAll((Set<String>)request.getParameterMap().keySet()); Collections.sort(keys); for (String key : keys) { String []value = request.getParameterValues(key); Post.addFormValue(env, post, key, value, addSlashesToValues, encoding); } } }