X-Git-Url: https://codewiz.org/gitweb?p=geekigeeki.git;a=blobdiff_plain;f=geekigeeki.py;h=12b6b7cdaec57d05e14baadd381c17a6431029bb;hp=190964478d979134e3c699776a5656923f0ed183;hb=75836706224e9f6b8ede6c59deea646e31672924;hpb=01057766514a73e0df07470e71c94b0508b2e5bf diff --git a/geekigeeki.py b/geekigeeki.py index 1909644..12b6b7c 100755 --- a/geekigeeki.py +++ b/geekigeeki.py @@ -1,10 +1,8 @@ #! /usr/bin/env python -"""Quick-quick implementation of WikiWikiWeb in Python -""" # -# Copyright (C) 1999, 2000 Martin Pool -# This version includes additional changes by Gerardo Poggiali (2002) -# This version includes additional changes by Bernardo Innocenti (2007) +# Copyright 1999, 2000 Martin Pool +# Copyright 2002 Gerardo Poggiali +# Copyright 2007 Bernardo Innocenti # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,9 +17,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__version__ = '$Revision: 1.63+gerry+bernie $'[11:-2]; +__version__ = '$Id$'[4:12] -import cgi, sys, string, os, re, errno, time, stat +from time import clock +start_time = clock() + +import cgi, sys, string, os, re, errno, stat from os import path, environ # Regular expression defining a WikiWord @@ -36,11 +37,17 @@ title_done = False # CGI stuff --------------------------------------------------------- -def get_scriptname(): +def script_name(): return environ.get('SCRIPT_NAME', '') +def privileged_path(): + return privileged_url or script_name() + def remote_user(): - return environ.get('REMOTE_USER', 'AnonymousCoward') + user = environ.get('REMOTE_USER', '') + if user is None or user == '' or user == 'anonymous': + user = 'AnonymousCoward' + return user def remote_host(): return environ.get('REMOTE_ADDR', '') @@ -50,7 +57,7 @@ def get_hostname(addr): from socket import gethostbyaddr return gethostbyaddr(addr)[0] + ' (' + addr + ')' except: - return addr; + return addr # Formatting stuff -------------------------------------------------- @@ -59,7 +66,7 @@ def emit_header(type="text/html"): print def send_guru(msg, msg_type): - if msg is None or len(msg) == 0: return + if msg is None or msg == '': return print '
'
     if msg_type == 'error':
         print '    Software Failure.  Press left mouse button to continue.\n'
@@ -67,7 +74,7 @@ def send_guru(msg, msg_type):
     if msg_type == 'error':
         print '      Guru Meditation #DEADBEEF.ABADC0DE'
     print '
' - # FIXME: This simple JS code is harder to pass than ACID 3.0 + # FIXME: This simple JS snippet is harder to pass than ACID 3.0 print """ """ -def send_title(name, text="Limbo", msg=None, msg_type = 'error'): +def send_title(name, text="Limbo", msg=None, msg_type='error'): global title_done if title_done: return # Head emit_header() - print """ - -""" + print '' + print '' + + site_name = globals().get('site_name', 'Unconfigured Site') print "%s: %s" % (site_name, text) - print ' ' + print ' ' if not name: - print ' ' - if css_url: + print ' ' + if globals().has_key('css_url'): print ' ' % css_url print '' # Body - if name and allow_edit: - print '' + if name and privileged_url is not None: + print '' else: print '' + title_done = True send_guru(msg, msg_type) # Navbar - print '' -def link_tag(params, text=None, ss_class=None): +def link_tag(params, text=None, ss_class=None, authentication=False): if text is None: text = params # default classattr = '' - # Prevent crawlers from following links to generated pages - # and links added by potential spammers - if ss_class == 'external' or ss_class == 'navlink': - classattr += 'rel="nofollow" ' if ss_class: classattr += 'class="%s" ' % ss_class - return '%s' % (classattr, get_scriptname(), - params, text) + # Prevent crawlers from following links potentially added by spammers or to generated pages + if ss_class == 'external' or ss_class == 'navlink': + classattr += 'rel="nofollow" ' + if authentication: + path = privileged_path() + else: + path = script_name() + return '%s' % (classattr, path, params, text) # Search --------------------------------------------------- @@ -179,13 +190,12 @@ def do_fullsearch(needle): for (count, page_name) in hits: print '
  • ' + Page(page_name).link_to() print ' . . . . ' + `count` - print ['match', 'matches'][count <> 1] + print ['match', 'matches'][count != 1] print '

  • ' print "" print_search_stats(len(hits), len(all_pages)) - def do_titlesearch(needle): # TODO: check needle is legal -- but probably we can just accept any RE send_title(None, "Title search for \"" + needle + '"') @@ -201,10 +211,10 @@ def do_titlesearch(needle): print_search_stats(len(hits), len(all_pages)) - def print_search_stats(hits, searched): print "

    %d hits out of %d pages searched.

    " % (hits, searched) +#TODO: merge into do_savepage() def do_edit(pagename): Page(pagename).send_editor() @@ -212,9 +222,6 @@ def do_raw(pagename): Page(pagename).send_raw() def do_savepage(pagename): - if not allow_edit: - raise 'editing disallowed for ' + pagename - global form pg = Page(pagename) if 'preview' in form: @@ -223,7 +230,7 @@ def do_savepage(pagename): pg.save_text(form['savetext'].value) pg.send_page() elif 'cancel' in form: - pg.msg = 'Editing cancelled' + pg.msg = 'Editing canceled' pg.msg_type = 'notice' pg.send_page() else: @@ -237,24 +244,29 @@ def make_index_key(): s = s + '

    ' return s - def page_list(): - return filter(word_re.match, os.listdir(text_dir)) - + return filter(word_re.match, os.listdir(data_dir)) def send_footer(name, mod_string=None): - print '" + print '

    ' # ---------------------------------------------------------- class Page: @@ -510,18 +569,18 @@ class Page: return re.sub('([a-z])([A-Z])', r'\1 \2', self.page_name) def _text_filename(self): - return path.join(text_dir, self.page_name) + return path.join(data_dir, self.page_name) def _tmp_filename(self): - return path.join(text_dir, ('#' + self.page_name + '.' + `os.getpid()` + '#')) + return path.join(data_dir, ('#' + self.page_name + '.' + `os.getpid()` + '#')) def exists(self): try: os.stat(self._text_filename()) - return 1 + return True except OSError, er: if er.errno == errno.ENOENT: - return 0 + return False else: raise er @@ -530,21 +589,15 @@ class Page: if self.exists(): return link_tag(word, word, 'wikilink') else: - if nonexist_qm: - return link_tag(word, '?', 'nonexistent') + word - else: - return link_tag(word, word, 'nonexistent') - + return link_tag(word, nonexist_pfx + word, 'nonexistent') def get_raw_body(self): try: return open(self._text_filename(), 'rt').read() except IOError, er: if er.errno == errno.ENOENT: - # just doesn't exist, use default - return 'Describe %s here.' % self.page_name - else: - raise er + return '' # just doesn't exist, use default + raise er def get_attrs(self): if self.attrs: @@ -563,44 +616,61 @@ class Page: raise er return self.attrs - def can_edit(self): + def can(self, action, default=True): attrs = self.get_attrs() try: # SomeUser:read,write All:read acl = attrs["acl"] for rule in acl.split(): - (user,perms) = acl.split(':') + (user,perms) = rule.split(':') if user == remote_user() or user == "All": - if 'write' in perms.split(','): + if action in perms.split(','): return True + else: + return False return False - except: + except Exception, er: pass - return True + return default + + def can_write(self): + return self.can("write", True) + + def can_read(self): + return self.can("read", True) def send_page(self): page_name = None - if self.can_edit(): + if self.can_write(): page_name = self.page_name send_title(page_name, self.split_title(), msg=self.msg, msg_type=self.msg_type) - PageFormatter(self.get_raw_body()).print_html() + if self.can_read(): + PageFormatter(self.get_raw_body()).print_html() + else: + send_guru("Read access denied by ACLs", "notice") send_footer(page_name, self._last_modified()) def _last_modified(self): - if not self.exists(): + try: + from time import localtime, strftime + modtime = localtime(os.stat(self._text_filename())[stat.ST_MTIME]) + except OSError, er: + if er.errno != errno.ENOENT: + raise er return None - from time import localtime, strftime - modtime = localtime(os.stat(self._text_filename())[stat.ST_MTIME]) return strftime(datetime_fmt, modtime) def send_editor(self, preview=None): send_title(None, 'Edit ' + self.split_title(), msg=self.msg, msg_type=self.msg_type) + if not self.can_write(): + send_guru("Write access denied by ACLs", "error") + return print ('

    Editing ' + self.page_name + ' for ' + cgi.escape(remote_user()) + ' from ' + cgi.escape(get_hostname(remote_host())) + '

    ') - print '
    ' % (get_scriptname(), self.page_name) + print '
    ' % (script_name(), self.page_name) print '' % (self.page_name) print """""" % (preview or self.get_raw_body()) print """ @@ -616,8 +686,12 @@ class Page: print "
    " PageFormatter(preview).print_html() print "
    " + send_footer(self.page_name) def send_raw(self): + if not self.can_read(): + send_title(None, msg='Read access denied by ACLs', msg_type='notice') + return emit_header("text/plain") print self.get_raw_body() @@ -630,16 +704,21 @@ class Page: try: os.remove(text) except OSError, er: - if er.errno <> errno.ENOENT: raise er + if er.errno != errno.ENOENT: raise er os.rename(tmp_filename, text) def save_text(self, newtext): + if not self.can_write(): + self.msg = 'Write access denied by ACLs' + self.msg_type = 'error' + return + self._write_file(newtext) rc = 0 if post_edit_hook: # FIXME: what's the std way to perform shell quoting in python? cmd = ( post_edit_hook - + " '" + text_dir + '/' + self.page_name + + " '" + data_dir + '/' + self.page_name + "' '" + remote_user() + "' '" + remote_host() + "'" ) @@ -652,29 +731,19 @@ class Page: if msg: self.msg += 'Output follows:\n' + msg else: - self.msg = 'Thankyou for your contribution. Your attention to detail is appreciated.' + self.msg = 'Thank you for your contribution. Your attention to detail is appreciated.' self.msg_type = 'success' +#TODO: merge into send_raw() def send_verbatim(filename, mime_type='application/octet-stream'): - pathname = path.join(text_dir, filename) + pathname = path.join(data_dir, filename) data = open(pathname, 'rb').read() emit_header(mime_type) sys.stdout.write(data) -# --------------------------------------------------------------- +# Main --------------------------------------------------------------- try: - # Configuration values - data_dir = '/home/bernie/public_html/wiki' - text_dir = path.join(data_dir, 'text') - allow_edit = True # Is it possible to edit pages? - site_name = 'codewiz.org' - changed_time_fmt = '[%I:%M %p] ' - date_fmt = '%a %d %b %Y' - datetime_fmt = '%a %d %b %Y %I:%M %p' - show_hosts = True # show hostnames? - css_url = '../wikidata/geekigeeki.css' # optional stylesheet link - nonexist_qm = False # show '?' for nonexistent? - post_edit_hook = './post_edit_hook.sh' + execfile("geekigeeki.conf.py") form = cgi.FieldStorage() @@ -699,11 +768,12 @@ try: if word_re.match(query): Page(query).send_page() elif img_re.match(query): + #FIXME: use correct mime type send_verbatim(query, 'image/jpeg') else: send_verbatim(query) else: - # TODO: return 404? + print "Status: 404 Not Found" send_title(None, msg='Can\'t work out query: ' + query) except: import traceback