]> git.llucax.com Git - software/blitiri.git/blobdiff - blitiri.cgi
Properly translate HTML entities in user inputs when rendering
[software/blitiri.git] / blitiri.cgi
index f08247ee827feb87032cd8b2ba92c7300486ce17..4552ed1a3cd87df7daf56128fd6f81c529e0dae6 100755 (executable)
@@ -50,6 +50,9 @@ import urllib
 import cgi
 from docutils.core import publish_parts
 
+# Before importing the config, add our cwd to the Python path
+sys.path.append(os.getcwd())
+
 # Load the config file, if there is one
 try:
        from config import *
@@ -57,17 +60,22 @@ except:
        pass
 
 
+# Pimp *_path config variables to support relative paths
+data_path = os.path.realpath(data_path)
+templates_path = os.path.realpath(templates_path)
+
 # Default template
 
-default_main_header = """
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+default_main_header = """\
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
 <link rel="alternate" title="%(title)s" href="%(fullurl)s/atom"
        type="application/atom+xml" />
-<link href="%(css_url)s" rel="stylesheet"
-       type="text/css" />
+<link href="%(css_url)s" rel="stylesheet" type="text/css" />
 <title>%(title)s</title>
 </head>
 
@@ -79,8 +87,7 @@ default_main_header = """
 """
 
 default_main_footer = """
-</div><p/>
-<hr/><br/>
+</div>
 <div class="footer">
   %(showyear)s: %(monthlinks)s<br/>
   years: %(yearlinks)s<br/>
@@ -124,16 +131,17 @@ default_css = """
 body {
        font-family: sans-serif;
        font-size: small;
+       width: 52em;
 }
 
 div.content {
-       width: 50%;
+       width: 96%;
 }
 
 h1 {
        font-size: large;
        border-bottom: 2px solid #99F;
-       width: 60%;
+       width: 100%;
        margin-bottom: 1em;
 }
 
@@ -171,15 +179,11 @@ div.article {
        margin-bottom: 2em;
 }
 
-hr {
-       float: left;
-       height: 2px;
-       border: 0;
-       background-color: #99F;
-       width: 60%;
-}
-
 div.footer {
+       margin-top: 1em;
+       padding-top: 0.4em;
+       width: 100%;
+       border-top: 2px solid #99F;
        font-size: x-small;
 }
 
@@ -198,6 +202,22 @@ div.section h1 {
 
 """
 
+# helper functions
+def rst_to_html(rst):
+       settings = {
+               'input_encoding': encoding,
+               'output_encoding': 'utf8',
+       }
+       parts = publish_parts(rst, settings_overrides = settings,
+                               writer_name = "html")
+       return parts['body'].encode('utf8')
+
+def sanitize(obj):
+       if isinstance(obj, basestring):
+               return cgi.escape(obj, True)
+       return obj
+
+
 # find out our URL, needed for syndication
 try:
        n = os.environ['SERVER_NAME']
@@ -231,86 +251,38 @@ class Templates (object):
                        'yearlinks': ' '.join(db.get_year_links()),
                }
 
-       def get_main_header(self):
-               p = self.tpath + '/header.html'
+       def get_template(self, page_name, default_template, extra_vars = None):
+               if extra_vars is None:
+                       vars = self.vars
+               else:
+                       vars = self.vars.copy()
+                       vars.update(extra_vars)
+
+               p = '%s/%s.html' % (self.tpath, page_name)
                if os.path.isfile(p):
-                       return open(p).read() % self.vars
-               return default_main_header % self.vars
+                       return open(p).read() % vars
+               return default_template % vars
+
+       def get_main_header(self):
+               return self.get_template('header', default_main_header)
 
        def get_main_footer(self):
-               p = self.tpath + '/footer.html'
-               if os.path.isfile(p):
-                       return open(p).read() % self.vars
-               return default_main_footer % self.vars
+               return self.get_template('footer', default_main_footer)
 
        def get_article_header(self, article):
-               avars = self.vars.copy()
-               avars.update( {
-                       'arttitle': article.title,
-                       'author': article.author,
-                       'date': article.created.isoformat(' '),
-                       'uuid': article.uuid,
-                       'created': article.created.isoformat(' '),
-                       'updated': article.updated.isoformat(' '),
-                       'tags': article.get_tags_links(),
-
-                       'cyear': article.created.year,
-                       'cmonth': article.created.month,
-                       'cday': article.created.day,
-                       'chour': article.created.hour,
-                       'cminute': article.created.minute,
-                       'csecond': article.created.second,
-
-                       'uyear': article.updated.year,
-                       'umonth': article.updated.month,
-                       'uday': article.updated.day,
-                       'uhour': article.updated.hour,
-                       'uminute': article.updated.minute,
-                       'usecond': article.updated.second,
-               } )
-
-               p = self.tpath + '/art_header.html'
-               if os.path.isfile(p):
-                       return open(p).read() % avars
-               return default_article_header % avars
+               return self.get_template(
+                       'art_header', default_article_header, article.to_vars())
 
        def get_article_footer(self, article):
-               avars = self.vars.copy()
-               avars.update( {
-                       'arttitle': article.title,
-                       'author': article.author,
-                       'date': article.created.isoformat(' '),
-                       'uuid': article.uuid,
-                       'created': article.created.isoformat(' '),
-                       'updated': article.updated.isoformat(' '),
-                       'tags': article.get_tags_links(),
-
-                       'cyear': article.created.year,
-                       'cmonth': article.created.month,
-                       'cday': article.created.day,
-                       'chour': article.created.hour,
-                       'cminute': article.created.minute,
-                       'csecond': article.created.second,
-
-                       'uyear': article.updated.year,
-                       'umonth': article.updated.month,
-                       'uday': article.updated.day,
-                       'uhour': article.updated.hour,
-                       'uminute': article.updated.minute,
-                       'usecond': article.updated.second,
-               } )
-
-               p = self.tpath + '/art_footer.html'
-               if os.path.isfile(p):
-                       return open(p).read() % avars
-               return default_article_footer % avars
+               return self.get_template(
+                       'art_footer', default_article_footer, article.to_vars())
 
 
 class Article (object):
-       def __init__(self, path):
+       def __init__(self, path, created = None, updated = None):
                self.path = path
-               self.created = None
-               self.updated = None
+               self.created = created
+               self.updated = updated
                self.uuid = "%08x" % zlib.crc32(self.path)
 
                self.loaded = False
@@ -373,9 +345,9 @@ class Article (object):
                        if ':' in l:
                                name, value = l.split(':', 1)
                                if name.lower() == 'title':
-                                       self._title = value
+                                       self._title = value.strip()
                                elif name.lower() == 'author':
-                                       self._author = value
+                                       self._author = value.strip()
                                elif name.lower() == 'tags':
                                        ts = value.split(',')
                                        ts = [t.strip() for t in ts]
@@ -388,20 +360,34 @@ class Article (object):
                self.loaded = True
 
        def to_html(self):
-               try:
-                       raw = open(data_path + '/' + self.path).readlines()
-               except:
-                       return "Can't open post file<p>"
-               raw = raw[raw.index('\n'):]
-
-               settings = {
-                       'input_encoding': encoding,
-                       'output_encoding': 'utf8',
+               return rst_to_html(self.raw_content)
+
+       def to_vars(self):
+               return {
+                       'arttitle': sanitize(self.title),
+                       'author': sanitize(self.author),
+                       'date': self.created.isoformat(' '),
+                       'uuid': self.uuid,
+                       'tags': self.get_tags_links(),
+
+                       'created': self.created.isoformat(' '),
+                       'ciso': self.created.isoformat(),
+                       'cyear': self.created.year,
+                       'cmonth': self.created.month,
+                       'cday': self.created.day,
+                       'chour': self.created.hour,
+                       'cminute': self.created.minute,
+                       'csecond': self.created.second,
+
+                       'updated': self.updated.isoformat(' '),
+                       'uiso': self.updated.isoformat(),
+                       'uyear': self.updated.year,
+                       'umonth': self.updated.month,
+                       'uday': self.updated.day,
+                       'uhour': self.updated.hour,
+                       'uminute': self.updated.minute,
+                       'usecond': self.updated.second,
                }
-               parts = publish_parts(self.raw_content,
-                               settings_overrides = settings,
-                               writer_name = "html")
-               return parts['body'].encode('utf8')
 
        def get_tags_links(self):
                l = []
@@ -409,7 +395,7 @@ class Article (object):
                tags.sort()
                for t in tags:
                        l.append('<a class="tag" href="%s/tag/%s">%s</a>' % \
-                               (blog_url, urllib.quote(t), t) )
+                               (blog_url, urllib.quote(t), sanitize(t) ))
                return ', '.join(l)
 
 
@@ -453,11 +439,9 @@ class DB (object):
                        except:
                                continue
 
-                       a = Article(l[0])
-                       a.created = datetime.datetime.fromtimestamp(
-                                               float(l[1]) )
-                       a.updated = datetime.datetime.fromtimestamp(
-                                               float(l[2]))
+                       a = Article(l[0],
+                               datetime.datetime.fromtimestamp(float(l[1])),
+                               datetime.datetime.fromtimestamp(float(l[2])))
                        self.uuids[a.uuid] = a
                        self.actyears.add(a.created.year)
                        self.actmonths.add((a.created.year, a.created.month))
@@ -545,6 +529,11 @@ def render_atom(articles):
        }
 
        for a in articles:
+               vars = a.to_vars()
+               vars.update( {
+                       'url': full_url,
+                       'contents': a.to_html(),
+               } )
                print """
   <entry>
     <title>%(arttitle)s</title>
@@ -552,29 +541,20 @@ def render_atom(articles):
     <link href="%(url)s/post/%(uuid)s" />
     <id>%(url)s/post/%(uuid)s</id>
     <summary>%(arttitle)s</summary>
-    <published>%(created)sZ</published>
-    <updated>%(updated)sZ</updated>
+    <published>%(ciso)sZ</published>
+    <updated>%(uiso)sZ</updated>
     <content type="xhtml">
       <div xmlns="http://www.w3.org/1999/xhtml"><p>
 %(contents)s
       </p></div>
     </content>
   </entry>
-               """ % {
-                       'arttitle': a.title,
-                       'author': a.author,
-                       'uuid': a.uuid,
-                       'url': full_url,
-                       'created': a.created.isoformat(),
-                       'updated': a.updated.isoformat(),
-                       'contents': a.to_html(),
-               }
-
+               """ % vars
        print "</feed>"
 
 
 def render_style():
-       print 'Content-type: text/plain\n'
+       print 'Content-type: text/css\r\n\r\n',
        print default_css
 
 def handle_cgi():
@@ -662,14 +642,13 @@ def handle_cmd():
        db = DB(data_path + '/db')
 
        if cmd == 'add':
-               article = Article(art_path)
+               article = Article(art_path, datetime.datetime.now(),
+                                       datetime.datetime.now())
                for a in db.articles:
                        if a == article:
                                print 'Error: article already exists'
                                return 1
                db.articles.append(article)
-               article.created = datetime.datetime.now()
-               article.updated = datetime.datetime.now()
                db.save()
        elif cmd == 'rm':
                article = Article(art_path)