#!/usr/bin/python
# -*- coding: utf-8 -*-

# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.

# Authors: Stefan Ritter <xeno@thehappy.de>
#          Adrian Vondendriesch <disco-stu@disco-stu.de>
#          Pascal Turbing <pascal@turbing.de>
# Description: A simple blogging software

# TODO:
# * Complete Atom Feed (like RSS)
# * Fix broken charset in outgoing mails (needs some testing)

import cgi, os, time, glob, re, md5, sys, random, smtplib
import ConfigParser

# A wonderful place for doing some regexp ;)
no_break          = re.compile('^\s*(<ul|</ul>|<li|</li>|<ol|</ol>|<table|</table>|<tr|</tr>|<td|</td>|<th|</th>|<p|</p>).*$')
line_start_hyphen = re.compile('^-.*$')
line_start_plus   = re.compile('^\+.*$')

def generate_uuid(string):
	string_md5sum = md5.new(string).hexdigest()
	string = str.join('-', (string_md5sum[0:8], string_md5sum[8:12], string_md5sum[12:16], string_md5sum[16:20], string_md5sum[20:32]))
	return string

def errorpage(string):
	print 'Content-type: text/html\n'
	print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'
	print ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
	print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">'
	print '<head>'
	print '    <title>Error!</title>'
	print '</head>'
	print '<body>'
	print '    ' + string
	print '</body>'
	print '</html>'
	sys.exit()

def document_header(string):
	if string == "xhtml-transitional":
		print 'Content-type: text/html\n'
		print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
		print ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
		print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">'
	if string == "xhtml-strict":
		print 'Content-type: text/html\n'
		print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'
		print ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
		print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">'
	if string == "atom":
		print 'Content-type: application/atom+xml\n'
		print '<?xml version="1.0" encoding="utf-8"?>'
		print '<feed xmlns="http://www.w3.org/2005/Atom">'
	if string == "rss":
		print 'Content-type: application/rss+xml\n'
		print '<?xml version="1.0" encoding="utf-8"?>'
		print '<rss version="2.0">'

configuration = ConfigParser.ConfigParser()

# Look for a configuration:
if os.path.exists('../blogthonrc'):
	configuration.read('../blogthonrc')
elif os.path.exists('../.blogthonrc'):
	configuration.read('../.blogthonrc')
elif os.path.exists('configuration'):
	configuration.read('configuration')
else:
	errorpage('No suitable configuration found!')
	sys.exit()

try: blog_title = configuration.get('personal', 'blog_title')
except: errorpage('"blog_title" is missing in configuration!')

try: blog_subtitle = configuration.get('personal', 'blog_subtitle')
except: errorpage('"blog_subtitle" is missing in configuration!')

try: blog_url = configuration.get('personal', 'blog_url')
except: errorpage('"blog_url" is missing in configuration!')

try: keywords = configuration.get('personal', 'keywords')
except: errorpage('"keywords" is missing in configuration!')

try: entries_dir = configuration.get('personal', 'entries_dir')
except: errorpage('"entries_dir" is missing in configuration!')
if not os.path.exists(entries_dir):
	errorpage('"entries_dir" does not exist!')

try: entries_suffix = configuration.get('personal', 'entries_suffix')
except: errorpage('"entries_suffix" is missing in configuration!')

try: staticpages_dir = configuration.get('personal', 'staticpages_dir')
except: errorpage('"staticpages_dir" is missing in configuration!')
if not os.path.exists(staticpages_dir):
	errorpage('"staticpages_dir" does not exist!')

try: plugins_dir = configuration.get('personal', 'plugins_dir')
except: errorpage('"plugins_dir" is missing in configuration!')
if not os.path.exists(plugins_dir):
	errorpage('"plugins_dir" does not exist!')

try: style = configuration.get('look', 'style')
except: errorpage('"style" is missing in configuration!')

try: entries_per_page = configuration.getint('look', 'entries_per_page')
except: errorpage('"entries_per_page" is missing in configuration!')

try: monthlist = configuration.get('look', 'monthlist')
except: errorpage('"monthlist" is missing in configuration!')

try: staticpages = configuration.get('look', 'staticpages')
except: errorpage('"staticpages" is missing in configuration!')

try: linklist = configuration.get('look', 'linklist')
except: errorpage('"linklist" is missing in configuration!')
if not os.path.exists("linklist"):
	errorpage('"linklist" does not exist!')

try: permalinks = configuration.get('look', 'permalinks')
except: errorpage('"permalinks" is missing in configuration!')

try: comments = configuration.get('look', 'comments')
except: errorpage('"comments" is missing in configuration!')

try: newest_first = configuration.get('look', 'newest_first')
except: errorpage('"newest_first" is missing in configuration!')

try: new_comment_mail = configuration.get('smtp', 'new_comment_mail')
except: errorpage('"new_comment_mail" is missing in configuration!')

try: mail_to = configuration.get('smtp', 'mail_to')
except: errorpage('"mail_to" is missing in configuration!')

try: smtp_host = configuration.get('smtp', 'smtp_host')
except: errorpage('"smtp_host" is missing in configuration!')

try: feed_preview = configuration.get('feed', 'feed_preview')
except: errorpage('"feed_preview" is missing or empty in configuration!')

# Read POST Variables
action = cgi.FieldStorage()
month_display = action.getvalue('m')

static_display = action.getvalue('s')
if static_display: static_display = static_display.replace('/', '')

post_display = action.getvalue('p')
if post_display: post_display = post_display.replace(' ', '-').replace('/', '')

allentries_display = action.getvalue('a')
feed_display = action.getvalue('feed')
if not month_display: month_display = ""
if not post_display: post_display = ""
if not static_display: static_display = ""
if not allentries_display: allentries_display = ""
if not feed_display: feed_display = ""

# Commentstuff
ctitle = action.getvalue('ctitle')
cname = action.getvalue('cname')
ctext = action.getvalue('ctext')
cquiz = action.getvalue('cquiz')
cquizv = action.getvalue('cquizv')
if not ctitle: ctitle = ""
if not cname: cname = ""
if not ctext: ctext = ""
if not cquiz: cquiz = ""
if not cquizv: cquizv = ""

# Comment to commit?
if cname and ctext and ctitle:
	# Prevent XSS hacks
	cname = cname.replace('<', '&lt;').replace('>', '&gt;').replace('\'', '&quot;')
	ctext = ctext.replace('<', '&lt;').replace('>', '&gt;').replace('\'', '&quot;')

	# Add comment
	if not cquiz == cquizv:
		errorpage("Brainmode")
	else:
		comments_file = glob.glob(entries_dir + ctitle + '.comments')
		if not comments_file:
			try:
				content = open(entries_dir + ctitle + '.comments', "w")
				content.close()
			except:
				errorpage('"' + entries_dir + '" isn\'t writable!')
		try:
			content = open(entries_dir + ctitle + '.comments', "a")
			content.write("-." + cname + "\n")
			content.write("+." + time.asctime() + "\n")
			ctext = ctext.split("\n")
			for line in ctext:
				content.write("." + line + "\n")
			content.close()
			# Send mail?
			if not new_comment_mail == 'False':
				msg = 'From: Blogthon\nTo: ' + mail_to + '\nSubject: New comment on ' + blog_title + '\n\nSomeone wrote a comment to this entry: ' + blog_url + '?p=' + ctitle.replace(' ', '-')
				smtp = smtplib.SMTP(smtp_host)
				smtp.sendmail(blog_title, mail_to, msg)
				smtp.quit()
		except:
			errorpage('Comment cannot be written!')

# Read entries and store their title and timestamp
entries      = []
entries_list  = glob.glob(entries_dir + '*.' + entries_suffix)

for entry in entries_list:
	timestamp = os.stat(entry)
	timestamp = time.localtime(timestamp[8])
	entry = timestamp, entry
	entries.append(entry)

if newest_first:
	entries.sort(reverse=True)
else:
	entries.sort()

# Generate atom feed
if feed_display == "atom":
	date = entries[0][0]
	blog_title_md5sum = generate_uuid(blog_title)

	# Append 0 to the beginning if len of integer is 1 (value<10)
	month = '%(#)02d' % {'#': int(date[1])}
	day = '%(#)02d' % {'#': int(date[2])}
	hour = '%(#)02d' % {'#':int(date[3])}
	min = '%(#)02d' % {'#': int(date[4])}
	sec = '%(#)02d' % {'#': int(date[5])}

	document_header("atom")
	print '<link href="' + blog_url + '/?feed=atom" rel="self" type="application/atom+xml"/>'
	print '    <author>'
	print '        <name>' + blog_title + '</name>'
	print '    </author>'
	print '    <title>' + blog_title + '</title>'
	print '    <id>urn:uuid:' + blog_title_md5sum + '</id>'
	print '    <updated>' + str(date[0]) + '-' + month + '-' + day + 'T' + hour + ':' + min + ':' + sec + 'Z</updated>'
	print ''
	j = len(entries)
	if j > 10: j = 10
	for i in xrange(0, j):
		title = str(entries[i][1]).replace('entries/', '', 1).replace('.' + entries_suffix, '')
		date = entries[i][0]
		title_md5sum = generate_uuid(title)
		print '        <entry>'
		print '            <title>' + title + '</title>'
		print '            <link href="' + blog_url + '?p=' + title + '"/>'
		print '            <id>urn:uuid:' + title_md5sum + '</id>'
		print '            <updated>' + str(date[0]) + '-' + month + '-' + day + 'T' + hour + ':' + min + ':' + sec + 'Z</updated>'
		print '            <summary>'
		content = open(str(entries[i][1]), 'r')
		for h in xrange(0, int(feed_preview)):
			rss_line = content.readline().strip()
			if rss_line != '':
				print '             ' + rss_line
		content.close()
		print '            </summary>'
		print '        </entry>'
	print '</feed>'

# Generate rss 2.0 feed
elif feed_display == "rss":
	document_header("rss")
	print '    <channel>'
	print '        <title>' + blog_title + '</title>'
	print '        <link>' + blog_url + '</link>'
	print '        <description>' + blog_subtitle + '</description>'
	date = time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(time.mktime(entries[0][0])))
	print '        <pubDate>' + date + '</pubDate>'
	print ''
	j = len(entries)
	if j > 10: j = 10
	for i in xrange(0, j):
		title = str(entries[i][1]).replace('entries/', '', 1).replace('.' + entries_suffix, '')
		date = time.strftime("%a, %d %b %Y %H:%M:%S %z", time.gmtime(time.mktime(entries[i][0])))
		title_md5sum = generate_uuid(title)
		print '        <item>'
		print '            <title>' + title + '</title>'
		print '            <link>' + blog_url + '?p=' + title + '</link>'
		print '            <guid>' + title_md5sum + '</guid>'
		print '            <pubDate>' + date + '</pubDate>'
		content = open(str(entries[i][1]), 'r')
		rss_description= ''
		for h in xrange(0, int(feed_preview)):
			line = content.readline().strip()
			if line:
				rss_description = rss_description + line + '<br />'
		content.close()
		print '            <description><![CDATA[' + rss_description + ']]></description>'
		print '        </item>'
	print '    </channel>'
	print '</rss>'

# Generate regular page
else:
	document_header("xhtml-strict")
	print '    <head>'
	print '        <title>' + blog_title + '</title>'
	print '        <meta http-equiv="content-type" content="text/html; charset=utf-8" />'
	print '        <meta name="keywords" content="' + keywords + '" />'
	print '        <meta name="description" content="' + blog_title + '" />'
	print '        <link rel="stylesheet" type="text/css" href="styles/' + style + '/' + style + '.css" />'
	print '    </head>'
	print '    <body>'
	print ''

	# Plugins
	sys.path.append(plugins_dir)
	for plugin in glob.glob(plugins_dir + '*.py'):
		__import__ (plugin.split('/')[1].replace('.py', ''))

	# Site header
	print '        <div class="header">'
	print '            <div class="header_title">'
	print '                <a href="?" class="header_link">' + blog_title + '</a>'
	print '            </div>'
	print '            <div class="header_subtitle">'
	print '                <span class="header_subtitle">' + blog_subtitle + '</span>'
	print '            </div>'
	print '        </div>'
	print ''

	# RSS feed
	print '        <div class="rss">'
	print '            <a href="?feed=rss" class="rss_link">rss</a>'
	print '        </div>'
	print ''

	# Atom feed
	print '        <div class="atom">'
	print '            <a href="?feed=atom" class="atom_link">atom</a>'
	print '        </div>'
	print ''

	# Staticpages
	if staticpages == "True":
		staticpages = []
		staticpages_list  = glob.glob(staticpages_dir + '*')
		staticpages_list.sort()
		print '        <div class="pages">'
		print '            <div class="pages_title">pages</div>'
		print '            <div class="pages_list">'
		print '                <ul class="pages_list">'
		for staticpage in staticpages_list:
			file = open(staticpage, 'r')
			header = file.readline()
			if header.split(':', 1)[0] == 'extern_link':
				link = header.split(':', 1)[1].strip()
			else:
				link = re.sub('\w+?\/', '', staticpage)
				link = '?s=' + link
			file.close()
			title = re.sub('\w+?\/\d+?-', '', staticpage)
			print '                    <li class="pages_list_entry"><a href="' + link + '" class="pages_list_entry">' + title + '</a></li>'
		print '                </ul>'
		print '            </div>'
		print '            <div class="pages_footer"></div>'
		print '        </div>'
		print ''

	# Monthlist
	if monthlist == "True":
		olddate = ""
		print '        <div class="months">'
		print '            <div class="months_title">months</div>'
		print '            <div class="months_list">'
		print '                <ul class="months_list">'
		for entry in entries:
			date         = time.strftime("%m%Y", entry[0])
			date_display = time.strftime("%h %Y", entry[0])
			if not olddate == date:
				print '                    <li class="months_list_entry"><a href="?m=' + date + '" class="months_list_entry">' + date_display + '</a></li>'
			olddate = date
		print '                </ul>'
		print '            </div>'
		print '            <div class="months_footer"></div>'
		print '        </div>'
		print ''

	# Linklist
	if linklist == "True":
		print '        <div class="linklist">'
		print '            <div class="linklist_title">links</div>'
		print '            <div class="linklist_list">'
		print '                <ul class="linklist_list">'
		try:
			content = open("linklist", "r")
			for line in content:
				if line.strip() is "":
					print '<br />'
				else:
					print '                    <li class="linklist_list_entry"><a href="' + line.split(" ")[0] + '" target="_blank" class="months_list_entry">' + line.split(" ", 1)[1].strip() + '</a></li>'
			content.close()
		except:
			print ''
		print '                </ul>'
		print '            </div>'
		print '            <div class="linklist_footer"></div>'
		print '        </div>'
		print ''

	print '        <div class="entries">'
	print ''

	# Staticpage
	if static_display != "":
		content = open(staticpages_dir + static_display, "r")
		print '            <div class="entry">'
		print '                <div class="entry_title">' + re.sub('\d+?-', '', static_display) + '</div>'
		print '                <div class="entry_content">'
		print '                    <p>'
		for line in content:
			if no_break.match(line):
				print '                    ' + line.strip()
			else:
				print '                    ' + line.strip() + '<br />'
		print '                    </p>'
		print '                </div>'
		print '                <div class="entry_footer"></div>'
		print '                <div class="entry_border_left"></div>'
		print '                <div class="entry_border_right"></div>'
		print '                <div class="entry_border_top"></div>'
		print '                <div class="entry_border_bottom"></div>'
		print '            </div>'
		print ''
		content.close()

	# Entry
	else:
		entry_counter = 0
		for entry in entries:
			date            = time.strftime("%c", entry[0])
			date_to_compare = time.strftime("%m%Y", entry[0]) # Needed for permalinks
			entry           = entry[1]
			title           = entry.replace('entries/', '', 1)
			title           = title.replace('.' + entries_suffix, '')

			if month_display == date_to_compare or not month_display:
				if post_display == title.replace(' ', '-') or not post_display:
					if allentries_display == "1" or entry_counter < entries_per_page:
						content = open(entry, "r")
						print '            <div class="entry">'
						if permalinks:
							print '                <div class="entry_title"><a href="?p=' + title.replace(' ', '-') + '" class="entry_title">' + title + '</a></div>'
						else:
							print '                <div class="entry_title">' + title + '</div>'
						print '                <div class="entry_date">' + date + '</div>'
						print '                <div class="entry_content">'
						for line in content:
							if no_break.match(line):
								print '                    ' + line.strip()
							else:
								print '                    ' + line.strip() + '<br />'
						print '                </div>'
						print '                <div class="entry_footer"></div>'
						print '                <div class="entry_border_left"></div>'
						print '                <div class="entry_border_right"></div>'
						print '                <div class="entry_border_top"></div>'
						print '                <div class="entry_border_bottom"></div>'

						# Comments...
						# ... are shown when post_display and comments_file isn't false
						comments_file = glob.glob(entries_dir + title + '.comments')
						if post_display:
							if comments_file:
								comments_file = glob.glob(entries_dir + title + '.comments')
								comments_content = open(comments_file[0], "r")
								print '            </div>'
								print '        </div>'
								print ''
								print '        <div class="comments">'

								notfirstline = 0 # Ugly fix for closing comment containers
								label_count = 0

								for line in comments_content:
									if line_start_hyphen.match(line):
										if notfirstline == 1:
											print '                </div>'
											print '            </div>'
											notfirstline = 0;
										print '            <div class="comment">'

										#Label for each comment
										label_count += 1
										print '                <a name="' + str(label_count) + '"></a>'

										print '                <div class="comment_author">' + line.split(".", 1)[1].strip() + '</div>'
									elif line_start_plus.match(line):
										print '                <div class="comment_date">' + line.split(".", 1)[1].strip() + '</div>'
										print '                <div class="comment_content">'
									else:
										notfirstline = 1;
										line = line.split(".", 1)[1]
										print '                    ' + line.strip() + '<br />'
										print ''
								print '                </div>'
								print '            </div>'
								comments_content.close()
							else:
								print '            </div>'
								print '        </div>'
								print '        <div class="comments">'

							# Form for adding comments
							if comments == "True":
								random_int_a = random.randint(1,9)
								random_int_b = random.randint(1,9)
								cquizv = random_int_a + random_int_b
								print '            <div class="submit_comment">'
								print '                <form action="" method="post">'
								print '                    <input type="hidden" name="ctitle" value="' + title + '" />'
								print '                    <input type="hidden" name="cquizv" value="' + str(cquizv) + '" />'
								print '                    <label class="submit_comment_name">name:</label><input class="submit_comment_name_input" type="text" id="cname" name="cname" />'
								print '                    <br /><label class="submit_comment_text">text:</label><textarea class="submit_comment_textarea" id="ctext" name="ctext"></textarea>'
								print '                    <br /><label class="submit_comment_quiz">' + str(random_int_a) + '+' + str(random_int_b) + '=</label><input class="submit_comment_quiz_input" type="text" id="cquiz" name="cquiz" />'
								print '                    <br /><input class="submit_comment_button" type="submit" id="submit" value="post comment" />'
								print '                 </form>'
								print '            </div>'
							else:
								print '            <div class="submit_border_bottom"></div>'
								print ''

						if comments == "True":
							comments_file = glob.glob(entries_dir + title + '.comments')
							if not comments_file and not post_display:
								print '                <div class="entry_comment">'
								print '                    <a href="?p=' + title.replace(' ','-') + '" class="entry_comment">no comments</a>'
								print '                </div>'
								print '            </div>'
								print ''
							elif comments_file and not post_display:
								comments_content = open(comments_file[0], "r")
								comments_counter = 0
								for line in comments_content:
									if line.split(".", 1)[0] == "-": comments_counter += 1
								print '                <div class="entry_comment">'
								print '                    <a href="?p=' + title.replace(' ', '-') + '" class="entry_comment">comments (' + str(comments_counter) + ')</a>'
								print '                </div>'
								print '            </div>'
								print ''
								comments_content.close()
						else:
							print '            </div>'
							print ''

						content.close()
						entry_counter += 1

		if not month_display and not post_display and not allentries_display and entry_counter == entries_per_page: # Display pagelist
			print '            <div class="entry"><a href=?a=1>View all entries...</a></div>'

	print '        </div>'
	print ''
	print '    </body>'
	print '</html>'

# vim: set tw=0 ts=4: