#!/usr/bin/python
"""
Script: Forest, a simple Python forum script.
Author: Andrew Nelis (andrew.nelis@gmail.com)
OnTheWeb: http://www.triv.org.uk/~nelis/forest
Date: Jun 2010
Version: 1.0.3

A Python CGI script for a basic flat-file based forum.

Getting Started:

* Set up your web server/place forest.py so that it is executed as a CGI script.
  You'll probably have to change the python path at the top of this script and
  chmod this script as appropriate if you're not on Windows.
* Put the stylesheet forest.css somewhere where it will be served by the
  webserver.
* Edit some of the variables below to taste. Most important of all:
    o DATA_PATH - Should point to a writable folder where the posts will be
                  stored.
    o CSS_PATH - Specify where the stylesheet forest.css is.
  (There are other settings within this file, mostly self explanatory.)
* Go to the appropriate URL and post away!

LICENCE:

Copyright (c) 2010 Andrew Nelis

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY C
LAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALING
S IN THE SOFTWARE.
"""


import md5
import os
import time

# Show any errors on the page. You might want to take this out on a live server
# and look in the servers error log instead.
#  Removed for compatibility issues
#import cgitb
#cgitb.enable()

# ============================================================================
# Configuration
# ============================================================================

# Where the threads are stored. This folder must exist.
DATA_PATH = '/opt/publicbox/share/forumspace/'
#Where the forest CGI is located (as a URL).
CGI_URL='/cgi-bin/forest.py'
# Where the main stylesheet is kept (as a URL).
CSS_PATH = '/content/css/forest.css'
# What is the title of the board?
BOARD_TITLE = 'PublicBox Board'
# Simple Description of the board, appears at the top of each page
BOARD_DESCRIPTION = """PublicBox Board. Put media reviews or questions here.<br>
<A HREF="http://publicbox.lan">Click here to go back to the main site</a> """
# How dates are stored (see python time module for details)
DATE_FORMAT = '%d %b %Y %H:%M:%S'
# If no author name is given, then this is the default.
ANON_AUTHOR = 'Anonymous Coward'

# How many entries to show on the index?
INDEX_PAGE_SIZE = 20
# How many entries to show on the thread page?
THREAD_PAGE_SIZE = 20

# Maximum lengths for names, subjects and message bodies.
# (currently we chop them off without warning)
MAX_AUTHOR_LEN = 20
MAX_SUBJECT_LEN = 100
MAX_BODY_LEN = 10000

# ============================================================================
# HTML Elements.
# ============================================================================
HTML_TOP = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>%s</title>
  <link rel="stylesheet" href="%s" type="text/css" />
  <script type="text/javascript">
  function show(elem_id) {
    elem = document.getElementById(elem_id);
    if (elem) elem.style.display = 'block';
  }
  </script>
 </head>
<body>
<h3>%s</h3>
<p class="board_description">%s</p>
''' % (BOARD_TITLE, CSS_PATH, BOARD_TITLE, BOARD_DESCRIPTION)

HTML_BOTTOM = '''
<p class="smallprint" >Powered by the <a href="http://www.triv.org.uk/~nelis/forest">Forest Python Board</a></font>
</body></html>'''
HTML_THREADS_TOP = '''<table width="95%" class="threads_table">
 <tr class="threads_header">
  <th width="60%">Subject</th><th>Author</th><th>Date</th><th>Replies</th><th>Last Reply</th>
 </tr>
'''
HTML_THREADS_ROW = '''
 <tr class="%s">
  <td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>
 </tr>
'''
HTML_NEW_THREAD = '''
<p><a href="javascript:show('hidden_form');">Start a new thread</a></p>
<div id="hidden_form">
<form method="post" action="?new=thread">
<p>Name: <input name="author" maxlength="%s"/></p>
<p>Subject: <input name="subject" maxlength="%s" size="80"/></p>
<p><textarea name="body" rows="10" cols="80"></textarea></p>
<p><input class="submit_button" type="submit" value="New Thread!"/></p>
</form>
</div>
''' % (MAX_AUTHOR_LEN, MAX_SUBJECT_LEN)

HTML_NEW_REPLY = '''
<p><a href="javascript:show('hidden_form')">Reply to this thread</a></p>
<div id="hidden_form">
<form method="post" action="?new=reply&amp;thread=%%s">
<p>Name: <input name="author" maxlength="%s"/></p>
<p><textarea name="body" rows="10" cols="80"></textarea></p>
<p><input class="submit_button" type="submit" value="Reply!"/></p>
</form>
</div>
''' % (MAX_AUTHOR_LEN,)

HTML_THREADS_BOTTOM = '</table>'
HTML_THREAD_TOP = '''
<table width="95%%" class="threads_table">
 <col width="15%%" />
 <col width="85%%" />
 <tr><td colspan="2"><a href="?">&lt;&lt; Main</a></td></tr>
 <tr class="thread_header"><td colspan="2">%s</td></tr>
'''
HTML_THREAD_ROW = '''
 <tr class="%s">
  <td valign="top"><b>%s</b><br/><small>%s</small></td>
  <td>%s</td>
 </tr>
'''
HTML_THREAD_BOTTOM = '''
</table>
'''

# ============================================================================
# Error messages
# ============================================================================

ERR_INVALID_THREAD = '<h3 class="error">Invalid Thread Specified</h3>'
ERR_NO_SUBJECT = '<h3 class="error">No Subject Given</h3>'
ERR_NO_BODY = '<h3 class="error">No body text!</h3>'

# ============================================================================
# Misc. globals
# ============================================================================

# No need to fiddle with these though.
ROW_STYLES = {0: 'thread_row', 1: 'thread_row_alt'}
INDEX_FILE = os.path.join(DATA_PATH, 'index.txt')
THREAD_PATH = DATA_PATH

# ============================================================================
# Function definitions
# ============================================================================

html_escape_table = {
        "&": "&amp;", '"': "&quot;", "'": "&apos;", ">": "&gt;",
        "<": "&lt;", ';': "&#59;", "/": "&#47;", '=': "&#61;",
        ":": "&#58;", '?': "&#63;", '!': "&#33;", '(': "&#40;",
        "{": "&#121;", "[": "&#91", "-": "&#45",
        }
    
def strip_html( text ):
    """Remove HTML chars from the given text and replace them with HTML
       entities. """
    return "".join(html_escape_table.get(c,c) for c in text  )


def process_body(body):
    """Process the message body e.g. for escaping smilies, HTML etc.
    ready for storing. We should then just be able to print the body out"""
    import re
    # Maximum body length.
    new_body = strip_html( body[:MAX_BODY_LEN] )
    new_body = new_body.replace('\n', '<br/>\n')
    # Turn (obvious) URLs into links.
#   new_body = url_re.sub(r'<a href="\1">\1</a>', new_body)
#    url_re = re.compile('(http://[\S\.]+)')
    return new_body.encode('string_escape')


def process_author(author):
    """Clean the author tag"""
    # Remove tabs and ensure a maximum length.
    new_author = strip_html( author[:MAX_AUTHOR_LEN] )
    return new_author.replace('\t', ' ')


def process_subject(subject):
    """Clean the subject line"""
    if ( subject is not None):
	return subject[:MAX_SUBJECT_LEN]
    else:
 	return "No Subject"	
	

def get_quer