def parse_qs(qstr, ignore_exc=True, encoding=DEFAULT_ENCODING): ''' Parse a url encoded string (a=b&c=d) into a QueryString object. @param url_enc_str: The string to parse @return: A QueryString object (a dict wrapper). >>> parse_qs('id=3') QueryString({u'id': [u'3']}) >>> parse_qs('id=3&id=4') QueryString({u'id': [u'3', u'4']}) >>> parse_qs('id=3&ff=4&id=5') QueryString({u'id': [u'3', u'5'], u'ff': [u'4']}) >>> parse_qs('pname') QueryString({u'pname': [u'']}) >>> parse_qs(u'%B1%D0%B1%D1=%B1%D6%B1%D7', encoding='euc-jp') QueryString({u'\u9834\u82f1': [u'\u75ab\u76ca']}) >>> parse_qs('%B1%D0%B1%D1=%B1%D6%B1%D7', encoding='euc-jp') QueryString({u'\u9834\u82f1': [u'\u75ab\u76ca']}) ''' qs = QueryString(encoding=encoding) if qstr: # convert to string if unicode if isinstance(qstr, unicode): qstr = qstr.encode(encoding, 'ignore') try: odict = OrderedDict() for name, value in urlparse.parse_qsl(qstr, keep_blank_values=True, strict_parsing=False): if name in odict: odict[name].append(value) else: odict[name] = [value] except Exception: if not ignore_exc: raise w3afException('Error while parsing "%r"' % (qstr,)) else: def decode(item): return ( item[0].decode(encoding, 'ignore'), [e.decode(encoding, 'ignore') for e in item[1]] ) qs.update((decode(item) for item in odict.items())) return qs
def create_fuzzable_request(req_url, method='GET', post_data='', add_headers=None): ''' Creates a fuzzable request based on the input parameters. @param req_url: Either a url_object that represents the URL or a HTTPRequest instance. If the latter is the case the `method` and `post_data` values are taken from the HTTPRequest object as well as the values in `add_headers` will be merged with the request's headers. @param method: A string that represents the method ('GET', 'POST', etc) @param post_data: A string that represents the postdata. @param add_headers: A dict that holds the headers. If `req_url` is a request then this dict will be merged with the request's headers. ''' if isinstance(req_url, HTTPRequest): url = req_url.url_object post_data = str(req_url.get_data() or '') method = req_url.get_method() headers = dict(req_url.headers) headers.update(add_headers or {}) else: url = req_url headers = add_headers or {} # Just a query string request! No postdata if not post_data: req = HTTPQSRequest(url, method, headers) else: # Seems to be something that has post data data = {} conttype = '' for hname in headers.keys(): # '.keys()' is just fine. Don't hnamelow = hname.lower() # remove it. if hnamelow == 'content-length': del headers[hname] elif hnamelow == 'content-type': conttype = headers.get('content-type', '').lower() # Case #1 - JSON request try: data = json.loads(post_data) except: pass if data: req = JSONPostDataRequest(url, method, headers, dc=data) # Case #2 - XMLRPC request elif all(map(lambda stop: stop in post_data.lower(), XMLRPC_WORDS)): req = XMLRPCRequest(post_data, url, method, headers) else: # Case #3 - multipart form data - prepare data container if conttype.startswith('multipart/form-data'): pdict = cgi.parse_header(conttype)[1] try: dc = cgi.parse_multipart(StringIO(post_data), pdict) except: om.out.debug('Multipart form data is invalid, the browser ' 'sent something weird.') else: data = QueryString() data.update(dc) # We process multipart requests as x-www-form-urlencoded # TODO: We need native support of multipart requests! headers['content-type'] = \ 'application/x-www-form-urlencoded' # Case #4 - a typical post request else: try: data = parse_qs(post_data) except: om.out.debug('Failed to create a data container that ' 'can store this data: "' + post_data + '".') # Finally create request req = httpPostDataRequest(url, method, headers, dc=data) return req