static void fillPost(Env env, ArrayValue postArray, ArrayValue files, InputStream @is, string contentType, string encoding, int contentLength, bool addSlashesToValues, bool 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); } }
/// <summary> /// Reads the stream, if this part has completed the nextpart is returned /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <param name="nextpart"></param> /// <returns></returns> public int ReadForNextPart(byte[] buffer, int offset, int count, out MultipartPartParser nextpart) { // If we have found our next part we have already finsihed this part and should stop here if (NextPart != null || IsEndPart) { nextpart = NextPart; return(0); } // the search buffer is the place where we will scan for part bounderies. We need it to be just // a bit bigger than than the size requested, to ensure we dont accidnetly send part of a boundary // without realising it byte[] searchBuffer = new byte[count + BoundaryWithNewLinePrepend.Length]; int bytesReadThisCall = 0; // first read from our local buffer int bytesToReadFromLocalBuffer = Math.Min((int)LocalBuffer.Length, searchBuffer.Length); if (bytesToReadFromLocalBuffer > 0) { bytesReadThisCall += LocalBuffer.Read(searchBuffer, bytesReadThisCall, bytesToReadFromLocalBuffer); } // if we could not fill our search buffer with our local buffer then read from the multipart stream int bytesToReadFromStream = searchBuffer.Length - bytesReadThisCall; bytesToReadFromStream = Math.Min(bytesToReadFromStream, (int)MultipartStream.Length - (int)MultipartStream.Position); if (bytesToReadFromStream > 0) { bytesReadThisCall += MultipartStream.Read(searchBuffer, bytesReadThisCall, bytesToReadFromStream); } // the number of bytes returned will be one of three cases // 1. There is still plenty to return so we will return the 'count' they asked for // 2. We have emptied the stream, we will return the bytes read // 3. We have run into a new boundary, we will return the bytes up to the boundary end int bytesReturned; bool isEndOfPart = SearchBytePattern(BoundaryWithNewLinePrepend, searchBuffer, out bytesReturned); // we can only return the parts we know for sure are not part of the next boundary // which is the bytes we read minus the boundary length. This will also ensure we // get back to the count we were originally asked for. We also need to make sure we // return 0 bytes if we can not gaurentee there are no boundaries parts in what we // did manage to read if (!isEndOfPart) { bytesReturned = Math.Max(0, bytesReadThisCall - BoundaryWithNewLinePrepend.Length); } Buffer.BlockCopy(searchBuffer, 0, buffer, offset, bytesReturned); // We need to handle the bytes that did not get returned by putting them back into // the local buffer int bytesNotReturned = bytesReadThisCall - bytesReturned; ReinsertIntoLocalBuffer(searchBuffer, bytesReturned, bytesNotReturned); nextpart = null; if (isEndOfPart) { // the boundary we were looking for had a newline appended to it // we dont want to send the newline to the next part so we will skip LocalBuffer.Position += NewLine.Length; NextPart = new MultipartPartParser(MultipartStream, Encoding, _log, LocalBuffer); // The next part may actually just the be end indicator, if thats the case // we will null it and not return it if (NextPart.IsEndPart) { NextPart = null; } nextpart = NextPart; } return(bytesReturned); }
private static void readMultipartStream(Env env, MultipartStream ms, ArrayValue postArray, ArrayValue files, bool addSlashesToValues, bool isAllowUploads) { 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) { string 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); } } } }