/data.cgi

#!/usr/bin/env python
"""
data.cgi - Data URI Resolver
Author: Sean B. Palmer, inamidst.com
@@ Autodetect whether to use GET or POST
"""

import sys, os, cgi, urllib

allowed = frozenset(['text/plain', 'text/html', 'image/svg+xml', 
                     'application/xhtml+xml'])

PATH_INFO = os.environ.get('PATH_INFO', '')
QUERY_STRING = os.environ.get('QUERY_STRING', '')
SCRIPT_NAME = os.environ.get('SCRIPT_NAME', '')
REQUEST_URI = os.environ.get('REQUEST_URI', '')
REQUEST_METHOD = os.environ.get('REQUEST_METHOD', '')
script = REQUEST_URI.split('?')[0]

def serve(status, headers, body): 
   sys.stdout.write("Status: %s\r\n" % status)
   for header in headers.iteritems(): 
      sys.stdout.write("%s: %s\r\n" % header)
   sys.stdout.write("\r\n")
   sys.stdout.write(str(body))
   sys.exit()

def ok(mime, body): 
   serve(200, {'Content-Type': mime}, body)

def redirect(uri, temp=False): 
   if not uri.startswith('http:'): 
      from urlparse import urljoin
      domain = os.environ.get('SERVER_NAME', 'localhost')
      uri = urljoin('http://' + domain + REQUEST_URI, uri)

   status = (301, 307)[temp]
   headers = {'Location': uri, 'Content-Type': 'text/html'}
   body = '<a href="%s">Go!</a>' % uri
   serve(status, headers, body)

def error(msg): 
   body = '<p>%s</p>' % cgi.escape(msg)
   serve(501, {'Content-Type': 'text/html'}, body)

def query(): 
   form = cgi.FieldStorage()
   form.__call__ = lambda s: form[s].value

   if not form.has_key('data'): 
      error('Form must have key "data".')
   uridata = urllib.quote(form('data'))

   # Commas, as metachars, need encoding: <mime>,<data>
   uridata = uridata.replace(',', '%2C')

   # @@ It should be done with slashes too, but that breaks Apache...
   # uridata = uridata.replace('/', '%2F')

   if form.has_key('mime'): 
      mime = form('mime')
      if mime != 'text/html': 
         uridata = mime + ',' + uridata
   redirect(script + uridata)

def homepage(): 
   import textwrap
   ok('text/html', textwrap.dedent("""\
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <title>Data URI Resolver - Paste 'n' Go</title>
      <link rel="stylesheet" type="text/css" href="/trove/simple.css" />
      <style type="text/css">
      body { margin: 2.3em 3em; }
      h1 { font-size: 1.8em; color: #353f3f; }
      h1 img { margin-bottom: -8px; }
      form { margin-left: 0.25em; }
      input[type="text"] { border: 2px solid #9bc; }
      textarea { border: 2px solid #9bc; 
                 margin-bottom: 0.25em; 
                 padding: 0.1em 0.2em; }
      select { border: 2px solid #eaa; }
      input[type="submit"] { 
         margin-left: 0.25em; margin-right: 0.25em; 
         border: 2px solid #9ba;
         background-color: #f3faf5;
      }

      h1 { border-top: 2px solid #9bc; 
           margin-top: 0.75em; 
           padding-top: 0.5em; 
           margin-bottom: 0em; 
           padding-bottom: 0.5em; }
      address { font-style: normal; }
      p { margin-right: 3em; }
      </style>
      <script type="text/javascript">
      <!-- 
      function usepost() { 
         document.data.setAttribute('method', 'post');
         document.data.submitter.setAttribute('value', 'POST'); 
         switcher = document.getElementById('switcher'); 
         switcher.style.display = 'none'; 
      }
      window.onload = function(){ 
         setTimeout("document.data.data.focus();", 5); 
      }
      // -->
      </script>
      </head>
      <body>
      <div class="nav">
      </div>
      <form method="get" action="" name="data">
         <div><textarea name="data" rows="17" cols="79"></textarea></div>
         <div>Media Type: 
            <select name="mime">
               <option>text/html</option>
               <option>text/plain</option>
               <option>image/svg+xml</option>
               <option>application/xhtml+xml</option>
            </select>
            <input type="submit" name="submitter" value="GET" />
            <span id="switcher">
            (<a href="javascript:void(0);" onclick="usepost()">Use POST</a>)
            </span>
         </div>
      </form>

      <p>The form above uses GET by default but only allows up to 2,048 bytes. 
      By switching to POST you won't get a bookmarkable URI, but you will be 
      able to send up to 51,200 bytes.</p>

      <h1>
        <a href="/"><img src="/trove/logo" alt="inamidst.com" /></a> 
        Data URI Resolver
      </h1>
      <address>
        By <a href="http://inamidst.com/sbp/">Sean B. Palmer</a>. 
        See the <a href="/inside/data/">script</a> for details.
      </address>
      </body>
      </html>
   """))

def post(): 
   form = cgi.FieldStorage()
   form.__call__ = lambda s: form[s].value

   if not form.has_key('data'): 
      error('Form must have key "data".')
   uridata = urllib.quote(form('data'))
   data('/' + uridata)

def data(path): 
   # path = REQUEST_URI[5:] # len(SCRIPT_NAME.rsplit('/', 1)[0]):]
   if path.startswith('/'): 
      path = path[1:]

   if ',' in path: 
      mime, uridata = path.split(',', 1)
   else: mime, uridata = 'text/html', path

   if mime.endswith(';base64'): 
      import base64
      mime = mime[:-7]
      uridata = base64.b64decode(uridata)

   if mime not in allowed: 
      uridata = mime + ',' + uridata
      mime = 'text/html'
      # error('MIME type "%s" is not allowed.' % mime)

   import urllib
   # @@ Should probably do this above
   uridata = urllib.unquote(uridata)
   if (REQUEST_METHOD == 'GET') and (len(uridata) >= 2048): 
      error('Data length must be less than 2048 bytes.')
   elif (REQUEST_METHOD == 'POST') and (len(uridata) >= 51200): 
      error('Data length must be less than 51,200 bytes.')
   ok(mime, uridata)

def main(): 
   import cgitb
   cgitb.enable()

   if REQUEST_METHOD == 'GET': 
      if QUERY_STRING: query()
      elif PATH_INFO == '/': homepage()
      elif not PATH_INFO: 
         redirect(REQUEST_URI + '/')
      else: data(PATH_INFO)
   else: post()

if __name__=="__main__": 
   main()