Commits

Anonymous committed f65a7ac

0.12.2dev: Don't quote unreserved characters in generated URLs.

Closes #9965.

  • Participants
  • Parent commits 15e34fa
  • Branches 0.12-stable

Comments (0)

Files changed (6)

File trac/ticket/query.py

 from trac.util.datefmt import format_datetime, from_utimestamp, parse_date, \
                               to_timestamp, to_utimestamp, utc
 from trac.util.presentation import Paginator
-from trac.util.text import empty, shorten_line, unicode_unquote
+from trac.util.text import empty, shorten_line
 from trac.util.translation import _, tag_
 from trac.web import arg_list_to_args, parse_arg_list, IRequestHandler
 from trac.web.href import Href
         Note: for now, this is an "exploded" query href, but ideally should be
         expressed in TracQuery language.
         """
-        query_string = unicode_unquote(self.get_href(Href('')))
-        if query_string and '?' in query_string:
-            query_string = query_string.split('?', 1)[1]
+        query_string = self.get_href(Href(''))
+        query_string = query_string.split('?', 1)[-1]
         return 'query:?' + query_string.replace('&', '\n&\n')
 
     def get_sql(self, req=None, cached_ids=None, authname=None, tzinfo=None):

File trac/ticket/tests/wikisyntax.py

 <a class="query" href="/query?status=new&amp;status=reopened&amp;order=priority">query:status=new|reopened</a>
 </p>
 <p>
-<a class="query" href="/query?milestone=%21&amp;order=priority">query:milestone!=</a>
+<a class="query" href="/query?milestone=!&amp;order=priority">query:milestone!=</a>
 </p>
 <p>
 <a class="query" href="/query?owner=me&amp;milestone=1.0&amp;milestone=2.0&amp;order=priority">query:milestone=1.0|2.0&amp;owner=me</a>

File trac/util/text.py

     """A unicode aware version of urllib.quote"""
     return quote(value.encode('utf-8'), safe)
 
-def unicode_quote_plus(value):
-    """A unicode aware version of urllib.quote"""
-    return quote_plus(value.encode('utf-8'))
+def unicode_quote_plus(value, safe=''):
+    """A unicode aware version of urllib.quote_plus"""
+    return quote_plus(value.encode('utf-8'), safe)
 
 def unicode_unquote(value):
     """A unicode aware version of urllib.unquote.
     """
     return unquote(value).decode('utf-8')
 
-def unicode_urlencode(params):
+def unicode_urlencode(params, safe=''):
     """A unicode aware version of urllib.urlencode.
     
     Values set to `empty` are converted to the key alone, without the
         params = params.iteritems()
     l = []
     for k, v in params:
-        k = quote_plus(str(k))
+        k = quote_plus(str(k), safe)
         if v is empty:
             l.append(k)
         elif isinstance(v, unicode):
-            l.append(k + '=' + quote_plus(v.encode('utf-8')))
+            l.append(k + '=' + quote_plus(v.encode('utf-8'), safe))
         else:
-            l.append(k + '=' + quote_plus(str(v)))
+            l.append(k + '=' + quote_plus(str(v), safe))
     return '&'.join(l)
 
 def to_utf8(text, charset='iso-8859-15'):

File trac/web/href.py

 class Href(object):
     """Implements a callable that constructs URLs with the given base. The
     function can be called with any number of positional and keyword
-    arguments which than are used to assemble the URL.
+    arguments which then are used to assemble the URL.
 
     Positional arguments are appended as individual segments to
     the path of the URL:
     '/trac/ticket/540'
     >>> href.browser('/trunk/README.txt', format='txt')
     '/trac/browser/trunk/README.txt?format=txt'
+    
+    The path_safe argument specifies the characters that don't need to be
+    quoted in the path arguments. Likewise, the query_safe argument specifies
+    the characters that don't need to be quoted in the query string:
+
+    >>> href = Href('')
+    >>> href.milestone('<look,here>', param='<here,too>')
+    '/milestone/%3Clook%2Chere%3E?param=%3Chere%2Ctoo%3E'
+
+    >>> href = Href('', path_safe='/<,', query_safe=',>')
+    >>> href.milestone('<look,here>', param='<here,too>')
+    '/milestone/<look,here%3E?param=%3Chere,too>'
     """
 
-    def __init__(self, base):
+    def __init__(self, base, path_safe="/!~*'()", query_safe="!~*'()"):
         self.base = base.rstrip('/')
+        self.path_safe = path_safe
+        self.query_safe = query_safe
         self._derived = {}
 
     def __call__(self, *args, **kw):
                 args = args[:-1]
 
         # build the path
-        path = '/'.join([unicode_quote(unicode(arg).strip('/')) for arg in args
-                         if arg is not None])
+        path = '/'.join(unicode_quote(unicode(arg).strip('/'), self.path_safe)
+                        for arg in args if arg is not None)
         if path:
             href += '/' + path
         elif not href:
         # assemble the query string
         for k, v in kw.items():
             add_param(k.endswith('_') and k[:-1] or k, v)
-
         if params:
-            href += '?' + unicode_urlencode(params)
+            href += '?' + unicode_urlencode(params, self.query_safe)
 
         return href
 

File trac/wiki/tests/wiki-tests.txt

 &lt;<a class="odd resolver" href="/thing/1">link:1</a>&gt;
 </p>
 <p>
-Thing&lt;<a class="text resolver" href="/stuff/2%20and%20%283%29">link:2 and (3)</a>&gt;
+Thing&lt;<a class="text resolver" href="/stuff/2%20and%20(3)">link:2 and (3)</a>&gt;
 </p>
 ------------------------------
 ============================== Escaping links resolvers, alternative short form
 [[HelloWorld(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ]
 ------------------------------
 <p>
-<a class="missing wiki" href="/wiki/HelloWorld%28hej%20hopp%29%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ]
+<a class="missing wiki" href="/wiki/HelloWorld(hej%20hopp)%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ]
 </p>
 ------------------------------
-<a class="missing wiki" href="/wiki/HelloWorld%28hej%20hopp%29%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ]
+<a class="missing wiki" href="/wiki/HelloWorld(hej%20hopp)%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ]
 ============================== Another bad macro call
 [[HelloWorld(hej hopp))]] # Extra right brace and still executed
 ------------------------------

File trac/wiki/tests/wikisyntax.py

 <a class="wiki" href="/wiki/TestPage">TestPage</a>
 <a class="wiki" href="/wiki/TestPage">/TestPage</a>
 <a class="wiki" href="/wiki/Space%201%2023">wiki:"Space 1 23"</a>
-<a class="wiki" href="/wiki/C%27est%20l%27%C3%A9t%C3%A9">wiki:"C'est l'\xe9t\xe9"</a>
+<a class="wiki" href="/wiki/C'est%20l'%C3%A9t%C3%A9">wiki:"C'est l'\xe9t\xe9"</a>
 <a class="missing wiki" href="/wiki/MissingPage" rel="nofollow">wiki:MissingPage?</a>
 <a class="missing wiki" href="/wiki/12" rel="nofollow">wiki:12?</a>
 <a class="missing wiki" href="/wiki/abc" rel="nofollow">wiki:abc?</a>