"""FieldStorage.py

This module defines a subclass of the standard Python cgi.FieldStorage class
with an extra method that will allow a FieldStorage to parse a query string
even in a POST request.

"""

import cgi, os, urllib


class FieldStorage(cgi.FieldStorage):
    """Modified FieldStorage class for POST requests with query strings.

    Parameters in the query string which have not been sent via POST are
    appended to the field list. This is different from the behavior of
    Python versions before 2.6 which completely ignored the query string in
    POST request, but it's also different from the behavior of the later Python
    versions which append values from the query string to values sent via POST
    for parameters with the same name. With other words, our FieldStorage class
    overrides the query string parameters with the parameters sent via POST.

    """

    def __init__(self, fp=None, headers=None, outerboundary='',
            environ=os.environ, keep_blank_values=False, strict_parsing=False):
        method = environ.get('REQUEST_METHOD', 'GET').upper()
        qs_on_post = method not in ('GET', 'HEAD') and environ.get(
            'QUERY_STRING', None) or None
        if qs_on_post:
            environ['QUERY_STRING'] = ''
        try:
            cgi.FieldStorage.__init__(self, fp, headers, outerboundary,
                environ, keep_blank_values, strict_parsing)
        finally:
            if qs_on_post:
                environ['QUERY_STRING'] = qs_on_post
        if qs_on_post:
            self.add_qs(qs_on_post)

    def add_qs(self, qs):
        """Add all non-existing parameters from the given query string."""
        r = {}
        for name_value in qs.split('&'):
            nv = name_value.split('=', 2)
            if len(nv) != 2:
                if self.strict_parsing:
                    raise ValueError('bad query field: %r' % (name_value,))
                continue
            name = urllib.unquote(nv[0].replace('+', ' '))
            value = urllib.unquote(nv[1].replace('+', ' '))
            if len(value) or self.keep_blank_values:
                if name in r:
                    r[name].append(value)
                else:
                    r[name] = [value]
        if self.list is None:
            # This makes sure self.keys() are available, even
            # when valid POST data wasn't encountered.
            self.list = []
        for key in r:
            if key not in self:
                # Only append values that aren't already the FieldStorage;
                # this makes POSTed vars override vars on the query string.
                for value in r[key]:
                    self.list.append(cgi.MiniFieldStorage(key, value))
