#!/usr/bin/awk -f
################################################################################
# awkiawki - wikiwiki clone written in (n|g|m)awk
# $Id: awki.cgi,v 1.45 2004/07/13 16:34:45 olt Exp $
################################################################################
# Copyright (c) 2002 Oliver Tonnhofer (olt@bogosoft.com)
# See the file `COPYING' for copyright notice.
################################################################################
BEGIN {
	#            --- default options ---
	#    --- use awki.conf to override default settings ---
	#
	#	img_tag: HTML img tag  for picture in page header.
	localconf["img_tag"] = "
"
	#	datadir: Directory for raw pagedata (must be writeable for the script).
	localconf["datadir"] = "./data/"
	#	parser: Parsing script.
	localconf["parser"] = "./parser.awk"
	#   special_parser: Parser for special_* functions.
	localconf["special_parser"] = "./special_parser.awk"
	#	default_page: Name of the default_page.
	localconf["default_page"] = "FrontPage"
	#	show_changes: Number of changes listed by RecentChanges
	localconf["show_changes"] = 10
	#	max_post: Bytes accept by POST requests (to avoid DOS).
	localconf["max_post"] = 100000
	#	write_protection: Regex for write protected files
	#   	e.g.: "*", "PageOne|PageTwo|^.*NonEditable" 
	#		HINT: to edit these protected pages, upload a .htaccess 
	#		      protected awki.cgi script with write_protection = ""
	localconf["write_protection"] = ""
	#   css: HTTP URL for external CSS file.
	localconf["css"] = ""
	#   always_convert_spaces: If true, convert runs of 8 spaces to tab automatical.
	localconf["always_convert_spaces"] = 0
	#	date_cmd: Command for current date.
	localconf["date_cmd"] = "date '+%e %b. %G %R:%S %Z'"
	#	rcs: If true, rcs is used for revisioning.
	localconf["rcs"] = 0
	#	path: add path to PATH environment
	localconf["path"] = ""
	#            --- default options ---
	scriptname = ENVIRON["SCRIPT_NAME"]
	
	if (localconf["path"])
		ENVIRON["PATH"] = localconf["path"] ":" ENVIRON["PATH"]
	#load external configfile
	load_config(scriptname)
	
	# PATH_INFO contains page name
	if (ENVIRON["PATH_INFO"]) {	
		query["page"] = ENVIRON["PATH_INFO"]
	}
	
	if (ENVIRON["REQUEST_METHOD"] == "POST") {
        if (ENVIRON["CONTENT_TYPE"] == "application/x-www-form-urlencoded") {
			if (ENVIRON["CONTENT_LENGTH"] < localconf["max_post"])
				bytes = ENVIRON["CONTENT_LENGTH"]
			else
				bytes = localconf["max_post"]
            cmd = "dd ibs=1 count=" bytes " 2>/dev/null"
            cmd | getline query_str
            close (cmd)
		}
		if (ENVIRON["QUERY_STRING"]) 
			query_str = query_str "&" ENVIRON["QUERY_STRING"]
	} else {
		if (ENVIRON["QUERY_STRING"])	
			query_str = ENVIRON["QUERY_STRING"]
	}
	
	n = split(query_str, querys, "&")
	for (i=1; i<=n; i++) {
		split(querys[i], data, "=")
		query[data[1]] = data[2]
	}
	# (IMPORTANT for security!)
	query["page"] = clear_pagename(query["page"])
	query["revision"] = clear_revision(query["revision"])
	query["revision2"] = clear_revision(query["revision2"])
	query["string"] = clear_str(decode(query["string"]))
	
	if (!localconf["rcs"])
		query["revision"] = 0
	if (query["page"] == "")
		query["page"] = localconf["default_page"]
	
	query["filename"] = localconf["datadir"] query["page"]
	
	#check if page is editable
	special_pages = "FullSearch|PageList|RecentChanges"
	
	if (query["page"] ~ "("special_pages")") {
		special_page = 1
	} else if (!localconf["write_protection"] || query["page"] !~ "("localconf["write_protection"]")") {
		page_editable = 1
	}
	header(query["page"])
	
	if (query["edit"] && page_editable)
		edit(query["page"], query["filename"], query["revision"])
	else if (query["save"] && query["text"] && page_editable)
		save(query["page"], query["text"], query["string"], query["filename"])
	else if (query["page"] ~ "PageList")
		special_index(localconf["datadir"])
	else if (query["page"] ~ "RecentChanges")
		special_changes(localconf["datadir"])
	else if (query["page"] ~ "FullSearch")
		special_search(query["string"],localconf["datadir"])
	else if (query["page"] && query["history"])
		special_history(query["page"], query["filename"])
	else if (query["page"] && query["diff"] && query["revision"])
		special_diff(query["page"], query["filename"], query["revision"], query["revision2"])
	else 
		parse(query["page"], query["filename"], query["revision"])
	footer(query["page"])
	
}
# print header
function header(page) {
	print "Content-type: text/html\n"
	print "\n
If you save previous versions, you'll overwrite the current page." print "
" print "Emphases: ''italic''; '''bold'''; \ '''''bold italic'''''; ''mixed '''bold'''\ and italic''current date:", date "
" system("ls -tlL "datadir" | " localconf["special_parser"] " -v special_changes=" localconf["show_changes"]) } function special_search(name,datadir) { system("grep -il '"name"' "datadir"* | " localconf["special_parser"] " -v special_search=yes") } function special_history(name, filename) { print "
last changes on "name"
" system("rlog " filename " | " localconf["special_parser"] " -v special_history="name) print "
Show diff between:" print "
" } # remove '"` characters from string # *** !Important for Security! *** function clear_str(str) { gsub(/['`"]/, "", str) if (length(str) > 80) return substr(str, 1, 80) else return str } # retrun the pagename # *** !Important for Security! *** function clear_pagename(str) { if (match(str, /[A-Z][a-z]+[A-Z][A-Za-z]*/)) return substr(str, RSTART, RLENGTH) else return "" } # return revision numbers # *** !Important for Security! *** function clear_revision(str) { if (match(str, /[1-9]\.[0-9]+/)) return substr(str, RSTART, RLENGTH) else return "" } # decode urlencoded string function decode(text, hex, i, hextab, decoded, len, c, c1, c2, code) { split("0 1 2 3 4 5 6 7 8 9 a b c d e f", hex, " ") for (i=0; i<16; i++) hextab[hex[i+1]] = i # urldecode function from Heiner Steven # http://www.shelldorado.com/scripts/cmds/urldecode # decode %xx to ASCII char decoded = "" i = 1 len = length(text) while ( i <= len ) { c = substr (text, i, 1) if ( c == "%" ) { if ( i+2 <= len ) { c1 = tolower(substr(text, i+1, 1)) c2 = tolower(substr(text, i+2, 1)) if ( hextab [c1] != "" || hextab [c2] != "" ) { if ( (c1 >= 2 && c1 != 7) || (c1 == 7 && c2 != "f") || (c1 == 0 && c2 ~ "[9acd]") ){ code = 0 + hextab [c1] * 16 + hextab [c2] + 0 c = sprintf ("%c", code) } else { c = " " } i = i + 2 } } } else if ( c == "+" ) { # special handling: "+" means " " c = " " } decoded = decoded c ++i } # change linebreaks to \n gsub(/\r\n/, "\n", decoded) # remove last linebreak sub(/[\n\r]*$/,"",decoded) return decoded } #load configfile function load_config(script, configfile,key,value) { configfile = script #remove trailing / ('/awki/awki.cgi/' -> '/awki/awki.cgi') sub(/\/$/, "", configfile) #remove path ('/awki/awki.cgi' -> 'awki.cgi') sub(/^.*\//, "", configfile) #remove suffix ('awki.cgi' -> 'awki') sub(/\.[^.]*$/,"", configfile) # append .conf suffix configfile = configfile ".conf" #read configfile while((getline < configfile) >0) { if ($0 ~ /^#/) continue #ignore comments if (match($0,/[ \t]*=[ \t]*/)) { key = substr($0, 1, RSTART-1) sub(/^[ \t]*/, "", key) value = substr($0, RSTART+RLENGTH) sub(/[ \t]*$/, "", value) if (sub(/^"/, "", value)) sub(/"$/, "", value) #set localconf variables localconf[key] = value } } }