class Rack::Multipart::Parser

def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)

def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)
  return EMPTY if 0 == content_length
  boundary = parse_boundary content_type
  return EMPTY unless boundary
  io = BoundedIO.new(io, content_length) if content_length
  outbuf = String.new
  parser = new(boundary, tmpfile, bufsize, qp)
  parser.on_read io.read(bufsize, outbuf)
  loop do
    break if parser.state == :DONE
    parser.on_read io.read(bufsize, outbuf)
  end
  io.rewind
  parser.result
end

def self.parse_boundary(content_type)

def self.parse_boundary(content_type)
  return unless content_type
  data = content_type.match(MULTIPART)
  return unless data
  data[1]
end

def consume_boundary

def consume_boundary
  while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
    case read_buffer.strip
    when full_boundary then return :BOUNDARY
    when @end_boundary then return :END_BOUNDARY
    end
    return if @sbuf.eos?
  end
end

def full_boundary; @full_boundary; end

def full_boundary; @full_boundary; end

def get_filename(head)

def get_filename(head)
  filename = nil
  case head
  when RFC2183
    params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
    if filename = params['filename']
      filename = $1 if filename =~ /^"(.*)"$/
    elsif filename = params['filename*']
      encoding, _, filename = filename.split("'", 3)
    end
  when BROKEN
    filename = $1
    filename = $1 if filename =~ /^"(.*)"$/
  end
  return unless filename
  if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
    filename = Utils.unescape_path(filename)
  end
  filename.scrub!
  if filename !~ /\\[^\\"]/
    filename = filename.gsub(/\\(.)/, '\1')
  end
  if encoding
    filename.force_encoding ::Encoding.find(encoding)
  end
  filename
end

def handle_consume_token

def handle_consume_token
  tok = consume_boundary
  # break if we're at the end of a buffer, but not if it is the end of a field
  @state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
    :DONE
  else
    :MIME_HEAD
  end
end

def handle_empty_content!(content)

def handle_empty_content!(content)
  if content.nil? || content.empty?
    raise EOFError
  end
end

def handle_fast_forward

def handle_fast_forward
  if consume_boundary
    @state = :MIME_HEAD
  else
    raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
    :want_read
  end
end

def handle_mime_body

def handle_mime_body
  if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
    body = body_with_boundary.sub(/#{@body_regex}\z/m, '') # remove the boundary from the string
    @collector.on_mime_body @mime_index, body
    @sbuf.pos += body.length + 2 # skip \r\n after the content
    @state = :CONSUME_TOKEN
    @mime_index += 1
  else
    # Save what we have so far
    if @rx_max_size < @sbuf.rest_size
      delta = @sbuf.rest_size - @rx_max_size
      @collector.on_mime_body @mime_index, @sbuf.peek(delta)
      @sbuf.pos += delta
      @sbuf.string = @sbuf.rest
    end
    :want_read
  end
end

def handle_mime_head

def handle_mime_head
  if @sbuf.scan_until(@head_regex)
    head = @sbuf[1]
    content_type = head[MULTIPART_CONTENT_TYPE, 1]
    if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
      name = Rack::Auth::Digest::Params::dequote(name)
    else
      name = head[MULTIPART_CONTENT_ID, 1]
    end
    filename = get_filename(head)
    if name.nil? || name.empty?
      name = filename || "#{content_type || TEXT_PLAIN}[]".dup
    end
    @collector.on_mime_head @mime_index, head, filename, content_type, name
    @state = :MIME_BODY
  else
    :want_read
  end
end

def initialize(boundary, tempfile, bufsize, query_parser)

def initialize(boundary, tempfile, bufsize, query_parser)
  @query_parser   = query_parser
  @params         = query_parser.make_params
  @boundary       = "--#{boundary}"
  @bufsize        = bufsize
  @full_boundary = @boundary
  @end_boundary = @boundary + '--'
  @state = :FAST_FORWARD
  @mime_index = 0
  @collector = Collector.new tempfile
  @sbuf = StringScanner.new("".dup)
  @body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
  @rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
  @head_regex = /(.*?#{EOL})#{EOL}/m
end

def on_read(content)

def on_read(content)
  handle_empty_content!(content)
  @sbuf.concat content
  run_parser
end

def result

def result
  @collector.each do |part|
    part.get_data do |data|
      tag_multipart_encoding(part.filename, part.content_type, part.name, data)
      @query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
    end
  end
  MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
end

def run_parser

def run_parser
  loop do
    case @state
    when :FAST_FORWARD
      break if handle_fast_forward == :want_read
    when :CONSUME_TOKEN
      break if handle_consume_token == :want_read
    when :MIME_HEAD
      break if handle_mime_head == :want_read
    when :MIME_BODY
      break if handle_mime_body == :want_read
    when :DONE
      break
    end
  end
end

def tag_multipart_encoding(filename, content_type, name, body)

def tag_multipart_encoding(filename, content_type, name, body)
  name = name.to_s
  encoding = Encoding::UTF_8
  name.force_encoding(encoding)
  return if filename
  if content_type
    list         = content_type.split(';')
    type_subtype = list.first
    type_subtype.strip!
    if TEXT_PLAIN == type_subtype
      rest = list.drop 1
      rest.each do |param|
        k, v = param.split('=', 2)
        k.strip!
        v.strip!
        v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
        encoding = Encoding.find v if k == CHARSET
      end
    end
  end
  name.force_encoding(encoding)
  body.force_encoding(encoding)
end