paul@102 | 1 | #!/usr/bin/env python |
paul@102 | 2 | |
paul@102 | 3 | """ |
paul@102 | 4 | Handle Confluence wiki requests employing special identifiers that should map to |
paul@115 | 5 | wiki pages. This script also handles export actions because they also use page |
paul@115 | 6 | identifiers. |
paul@102 | 7 | """ |
paul@102 | 8 | |
paul@107 | 9 | from urllib import quote |
paul@102 | 10 | import cgi, os, sys |
paul@102 | 11 | |
paul@102 | 12 | # Location of the mapping. |
paul@102 | 13 | |
paul@102 | 14 | MAPPING_ID_TO_PAGE = "mapping-id-to-page.txt" |
paul@102 | 15 | |
paul@102 | 16 | # An empty string means that the wiki is anchored at the site root. |
paul@102 | 17 | |
paul@102 | 18 | URL_PREFIX = "" |
paul@139 | 19 | DOWNLOAD_SCRIPT = "/download.py" |
paul@134 | 20 | EXPORT_SCRIPT = "/export.py" |
paul@135 | 21 | EXPORT_PDF_SCRIPT = "/exportpdf.py" |
paul@102 | 22 | |
paul@139 | 23 | # Conversion of tiny identifiers to verbose identifiers. |
paul@102 | 24 | |
paul@102 | 25 | from base64 import b64decode |
paul@102 | 26 | from struct import unpack |
paul@102 | 27 | |
paul@102 | 28 | def identifier(s): |
paul@139 | 29 | |
paul@139 | 30 | "Extract an identifier from the given string 's'." |
paul@139 | 31 | |
paul@139 | 32 | # Isolate numeric identifiers for things like downloads: |
paul@139 | 33 | # /download/attachment/<pageid>/<filename>?<args> |
paul@139 | 34 | |
paul@139 | 35 | if s.split("/")[0].isdigit(): |
paul@139 | 36 | return s.split("/")[0] |
paul@139 | 37 | |
paul@139 | 38 | # Reject strings that are too long for tiny identifiers. |
paul@139 | 39 | |
paul@102 | 40 | if len(s) > 6: |
paul@102 | 41 | return None |
paul@139 | 42 | |
paul@139 | 43 | # Attempt to unpack tiny identifiers. |
paul@139 | 44 | |
paul@102 | 45 | bytes = b64decode(s.replace("-", "/").replace("_", "+") + "=" * (6 - len(s))) |
paul@102 | 46 | return str(unpack("<I", bytes + "\x00" * (4 - len(bytes)))[0]) |
paul@102 | 47 | |
paul@102 | 48 | # Utility functions. |
paul@102 | 49 | |
paul@102 | 50 | def fail(pageid): |
paul@102 | 51 | print """\ |
paul@102 | 52 | Status: 404 Page not found |
paul@102 | 53 | Content-Type: text/html |
paul@102 | 54 | |
paul@102 | 55 | <html> |
paul@102 | 56 | <head><title>Bad Page Identifier</title></head> |
paul@102 | 57 | <body> |
paul@102 | 58 | <h1>Bad Page Identifier</h1> |
paul@102 | 59 | <p>The identifier given in the URL%s does not seem to refer to a page in this wiki.</p> |
paul@102 | 60 | </body> |
paul@102 | 61 | </html> |
paul@102 | 62 | """ % (pageid and " (%s)" % pageid or "") |
paul@102 | 63 | sys.exit(0) |
paul@102 | 64 | |
paul@139 | 65 | def redirect(pagename, download=None, export=False): |
paul@139 | 66 | location = "%s/%s%s" % ( |
paul@139 | 67 | URL_PREFIX, quote(pagename), |
paul@139 | 68 | download and "?action=AttachFile&do=get&target=%s" % download |
paul@139 | 69 | or export and "?action=ExportPDF" |
paul@139 | 70 | or "" |
paul@139 | 71 | ) |
paul@102 | 72 | |
paul@102 | 73 | print """\ |
paul@102 | 74 | Status: 302 Redirect to page |
paul@102 | 75 | Location: %s |
paul@102 | 76 | Content-Type: text/html |
paul@102 | 77 | |
paul@102 | 78 | <html> |
paul@102 | 79 | <head><title>Redirecting to Page</title></head> |
paul@102 | 80 | <body> |
paul@102 | 81 | <h1>Redirecting to Page</h1> |
paul@102 | 82 | <p>If you see this message, try following <a href="%s">this link</a>.</p> |
paul@102 | 83 | </body> |
paul@102 | 84 | </html> |
paul@102 | 85 | """ % (location, cgi.escape(location, True)) |
paul@102 | 86 | sys.exit(0) |
paul@102 | 87 | |
paul@102 | 88 | def find(f, pageid): |
paul@102 | 89 | for line in f.xreadlines(): |
paul@102 | 90 | columns = line.strip().split("\t") |
paul@102 | 91 | if columns[0] == pageid: |
paul@115 | 92 | return columns[1] |
paul@115 | 93 | return None |
paul@102 | 94 | |
paul@102 | 95 | def main(): |
paul@102 | 96 | args = cgi.parse_qs(os.environ.get("QUERY_STRING", "")) |
paul@102 | 97 | path = os.environ.get("PATH_INFO", "").strip("/") |
paul@115 | 98 | script = os.environ.get("SCRIPT_NAME", "") |
paul@102 | 99 | |
paul@102 | 100 | pageid = args.get("pageId", [None])[0] or identifier(path) |
paul@102 | 101 | if pageid is None: |
paul@102 | 102 | fail(pageid) |
paul@102 | 103 | |
paul@139 | 104 | download = (script.endswith(DOWNLOAD_SCRIPT) or |
paul@139 | 105 | script.endswith("/download/attachment") |
paul@139 | 106 | ) and path.rsplit("/", 1)[1] or None |
paul@139 | 107 | |
paul@135 | 108 | export = (script.endswith(EXPORT_SCRIPT) or |
paul@135 | 109 | script.endswith("/pages/doexportpage.action") |
paul@135 | 110 | ) and args.get("type", [""])[0] == "TYPE_PDF" or \ |
paul@135 | 111 | (script.endswith(EXPORT_PDF_SCRIPT) or |
paul@135 | 112 | script.endswith("/spaces/flyingpdf/pdfpageexport.action") |
paul@135 | 113 | ) |
paul@115 | 114 | |
paul@102 | 115 | f = open(MAPPING_ID_TO_PAGE) |
paul@102 | 116 | try: |
paul@102 | 117 | # With an identifier, find the corresponding page name. |
paul@102 | 118 | |
paul@115 | 119 | pagename = find(f, pageid) |
paul@102 | 120 | |
paul@102 | 121 | # Didn't find the page. |
paul@102 | 122 | |
paul@115 | 123 | if not pagename: |
paul@115 | 124 | fail(pageid) # exits |
paul@115 | 125 | |
paul@115 | 126 | # Redirect to the page. |
paul@115 | 127 | |
paul@139 | 128 | redirect(pagename, download, export) |
paul@102 | 129 | |
paul@102 | 130 | finally: |
paul@102 | 131 | f.close() |
paul@102 | 132 | |
paul@102 | 133 | if __name__ == "__main__": |
paul@102 | 134 | main() |
paul@102 | 135 | |
paul@102 | 136 | # vim: tabstop=4 expandtab shiftwidth=4 |