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);
                    }
                }
            }
        }