Encode URLs into ASCII.
authorW. Trevor King <wking@drexel.edu>
Wed, 21 Jul 2010 19:12:19 +0000 (15:12 -0400)
committerW. Trevor King <wking@drexel.edu>
Wed, 21 Jul 2010 20:52:32 +0000 (16:52 -0400)
URL encoding is undefined.  See
  http://www.cherrypy.org/ticket/825
  http://www.w3schools.com/TAGS/ref_urlencode.asp
  http://www.rfc-editor.org/rfc/rfc2396.txt
Therefore, safe URLs should be in a subset of ASCII.  We convert our
URLs to UTF-8, and then use urllib.quote_plus() to escape the UTF-8
into ASCII.

cookbook/cookbook.py
cookbook/server.py
cookbook/template/recipes.html

index 2c395c5b942d059acc4b5e92fad251824db321d1..de3a84b278869a2f58876ec0fef12d9b9bc27fa7 100644 (file)
@@ -27,6 +27,7 @@ import os
 import os.path
 import textwrap
 import types
+from urllib import quote_plus
 
 import yaml
 
@@ -439,12 +440,12 @@ class Recipe (object):
         self.url = url
         self.tags = tags
 
-    def clean_name(self):
+    def clean_name(self, ascii=False):
         name = self.name
-        for from_,to in [(' ','_'), ('/', '_'),
-                         (',', ''), (u'\xe2\x80\x99', ''),
-                         ('&', 'and')]:
+        for from_,to in [(' ','_'), ('/', '_'), (',', ''), ('&', 'and')]:
             name = name.replace(from_, to)
+        if ascii == True:
+            return quote_plus(name.encode('utf-8'))
         return name
 
     def matches_tags(self, tags):
@@ -540,6 +541,7 @@ class Cookbook (list):
         for recipe in self:
             self.index[recipe.name] = recipe
             self.index[recipe.clean_name()] = recipe
+            self.index[recipe.clean_name(ascii=True)] = recipe
 
     def tags(self):
         """List all tags used in this cookbook.
index 94f70f6c10c1456f6127a3fa76352130d2e3c26b..fb5a61de8a45db901cb61388937198bff27998e8 100644 (file)
@@ -63,8 +63,8 @@ class Server (object):
         if name == None:
             recipe = random.choice(self.cookbook)
         else:
-            if type(name) == types.StringType:
-                name = unicode(name, encoding='utf-8')
+            if isinstance(name, types.StringType):
+                name = unicode(name, 'utf-8')
             recipe = self.cookbook.index[name]
         template = self.env.get_template('recipe.html')
         return template.render(cookbook=self.cookbook, recipe=recipe)
@@ -72,8 +72,8 @@ class Server (object):
     @cherrypy.expose
     def add_tag(self, name, tag):
         """Add a tag to a single recipe."""
-        if type(name) == types.StringType:
-            name = unicode(name, encoding='utf-8')
+        if isinstance(name, types.StringType):
+            name = unicode(name, 'utf-8')
         recipe = self.cookbook.index[name]
         if recipe.tags == None:
             recipe.tags = []
@@ -83,13 +83,13 @@ class Server (object):
             with open(recipe.path, 'w') as f:
                 recipe.save(f)
         raise cherrypy.HTTPRedirect(
-            'recipe?name=%s' % recipe.clean_name(), status=302)
+            u'recipe?name=%s' % recipe.clean_name(ascii=True), status=302)
 
     @cherrypy.expose
     def remove_tag(self, name, tag):
         """Remove a tag from a single recipe."""
-        if type(name) == types.StringType:
-            name = unicode(name, encoding='utf-8')
+        if isinstance(name, types.StringType):
+            name = unicode(name, 'utf-8')
         recipe = self.cookbook.index[name]
         if recipe.tags == None:
             return
@@ -99,7 +99,7 @@ class Server (object):
             with open(recipe.path, 'w') as f:
                 recipe.save(f)
         raise cherrypy.HTTPRedirect(
-            'recipe?name=%s' % recipe.clean_name(), status=302)
+            u'recipe?name=%s' % recipe.clean_name(ascii=True), status=302)
 
     def _clean_tag(self, tag):
         """Sanitize tag."""
index a9bfc073453871265711af16b5931b58fc243b65..c362e660dd9e25b6e7bc090fecba7cfbb550174a 100644 (file)
@@ -15,7 +15,7 @@
     </form>
     <ul id="recipe-list">
     {% for recipe in recipes %}
-        <li><a href="recipe?name={{ recipe.clean_name() }}">
+        <li><a href="recipe?name={{ recipe.clean_name(ascii=True) }}">
            {{ recipe.name }}</li>
     {% endfor %}
     </ul>