#!/usr/bin/env python import os, re, sys sys.path.extend(["/home/jcgregorio/lib/python2.2/site-packages","/home/jcgregorio/lib/python2.2"]) import time, stat, errno, libxml2 from os import path import piki_conf from atom_common import * import base64, commands ENTRY_FORM = """Content-type: application/atom+xml; charset=utf-8 Status: 200 Ok %(wikiword)s tag:dev.bitworking.org,2004:%(wikiword)s %(issued)s %(content)s """ def wikiwordExists(wikiword): return wikiword and path.exists(getpath(wikiword)) def writeWordDef(wikiword, content): f = file(getpath(wikiword), "w") f.write(content) f.close() def writeImageDef(wikiword, type, content): # write a tmp file wikiword.tmp.type tmpfilepath = getimagepath(wikiword+'.tmp', type) f = file(tmpfilepath, "wb") f.write(content) f.close() # resize to 200x200 for a Small image if necessary. identify_output = commands.getoutput('identify -verbose ' + tmpfilepath) (width, height) = re.search("Geometry: ([0-9]+)x([0-9]+)", identify_output).groups() width = int(width) height = int(height) if width > 200 or height > 200: command = 'convert -sample 200x200 ' else: command = 'cp ' os.system(command + tmpfilepath + ' ' + getimagepath(wikiword+'Small', type)) # resize to 500x500 for a Full image if necessary. if (width > 500) or (height > 500): command = 'convert -sample 500x500 ' else: command = 'cp ' os.system(command + tmpfilepath + ' ' + getimagepath(wikiword, type)) # clean up the temporary file. os.unlink(tmpfilepath) def wikifyText(text): nonLetterRegex = re.compile("[^a-zA-Z]") return "".join(nonLetterRegex.split(text)) def extractWikiWord(body): """Returns either a wiki word, or an empty string if no wiki word could be found or created from the XML of the Atom entry passed in.""" word_anchored_re = re.compile(WIKIWORD_RE) wikiword = "" doc = libxml2.parseDoc(body) ctxt = doc.xpathNewContext() ctxt.xpathRegisterNs('atom', 'http://purl.org/atom/ns#') title_nodes = ctxt.xpathEval('//atom:entry/atom:title') if title_nodes: wikiword = wikifyText(title_nodes[0].content) if not word_anchored_re.match(wikiword): wikiword = "" return wikiword def append_editlog(wikiword, host): f = open(piki_conf.editlog_name, 'a+') try: f.seek(0, 2) # to end f.write("\t".join([wikiword, host, str(time.time())]) + "\n") finally: f.close() def get_atom_entry(wikiword): filename = getpath(wikiword) base_uri = piki_conf.base_uri if path.exists(filename): issued = last_modified_iso(filename) content = file(filename, 'r').read() else: issued = currentISOTime() content = "Create this page." return (200, ENTRY_FORM % vars()) def put_atom_entry(wikiword, content): ret = report_status(200, "OK", "Entry successfully updated.") doc = libxml2.parseDoc(content) ctxt = doc.xpathNewContext() ctxt.xpathRegisterNs('atom', 'http://purl.org/atom/ns#') text_plain_content_nodes = ctxt.xpathEval('/atom:entry/atom:content[@type="text/plain" or not(@type)]') base64_content_nodes = ctxt.xpathEval('/atom:entry/atom:content[@mode="base64"]') all_content_nodes = ctxt.xpathEval('/atom:entry/atom:content') content = "" if len(text_plain_content_nodes) > 0: content = text_plain_content_nodes[0].content if len(base64_content_nodes) > 0: content = ctxt.xpathEval('/atom:entry/atom:summary')[0].content mime_type = ctxt.xpathEval('/atom:entry/atom:content/@type')[0].content binarycontent = base64.decodestring(base64_content_nodes[0].content) writeImageDef(wikiword, mime_type, binarycontent) writeWordDef(wikiword, content + "\n\n$" + wikiword) append_editlog(wikiword, os.environ.get('REMOTE_ADDR', '')) elif len(text_plain_content_nodes) > 0 or len(all_content_nodes) == 0: writeWordDef(wikiword, content) append_editlog(wikiword, os.environ.get('REMOTE_ADDR', '')) else: # If there are 'content' elements but of some unknown type ret = report_status(415, "Unsupported Media Type", "This wiki only supports plain text, please don't try to upload pictures or other media.") return ret def delete_atom_entry(wikiword): ret = report_status(200, "OK", "Delete successful.") if wikiwordExists(wikiword): try: os.unlink(getpath(wikiword)) except: ret = report_status(500, "Internal Server Error", "Can't remove the file associated with that word.") return ret CREATED_RESP = """Content-type: text/plain Status: 201 Created Location: %(atom_base_uri)s/%(wikiword)s New entry created at %(base_uri)s/%(wikiword)s""" def create_atom_entry(body): wikiword = extractWikiWord(body) if wikiword: if wikiwordExists(wikiword): ret = report_status(409, "Conflict", "An entry with that name already exists. Please either choose a different title, or edit the already existing entry.") else: ret = put_atom_entry(wikiword, body) if (ret[0] == 200): ret = (201, CREATED_RESP % {'base_uri': base_uri, 'atom_base_uri': atom_base_uri, 'wikiword': wikiword}) else: ret = report_status(409, "Conflict", "Not enough information to form a wiki word. Please add a title that forms a wiki word, which consists of MultipleCapitalizedWordsConcatenatedTogether.") return ret def main(body): method = os.environ.get('REQUEST_METHOD', '') wikiword = os.environ.get('PATH_INFO', '/') wikiword = wikiword.split("/", 1)[1] # Remove any slashes from the wikiword wikiword = wikiword.strip() word_anchored_re = re.compile(WIKIWORD_RE) if method == 'POST': ret = create_atom_entry(body) elif word_anchored_re.match(wikiword): # If this is a valid WikiWord if method in ['GET', 'HEAD']: ret = get_atom_entry(wikiword) elif method == 'PUT': ret = put_atom_entry(wikiword, body) elif method == 'DELETE': ret = delete_atom_entry(wikiword) else: ret = report_status(405, "Method not allowed", "") else: ret = report_status(400, "Not a valid WikiWord", "The WikiWord you referred to is invalid. WikiWords can only contain ascii letters.") return ret[1] if __name__ == "__main__": print main(sys.stdin.read())