#!/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

import os
import sys
import time
import locale
import re

# Backward compatibilty to python2
try:
    import configparser
except:
    import ConfigParser

from cgi import FieldStorage
from smtplib import SMTP
from hashlib import md5
from glob import glob
from random import randint

# 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("^\+.*$")

# Indention
ind = " "

def generate_uuid(string):
    string_md5sum = md5(string.encode("utf-8")).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):
    document_header("html")
    print("<head>")
    print(ind + "<title>Error!</title>")
    print(ind + "<link rel=\"stylesheet\" type=\"text/css\" href=\"error.css\" />")
    print("</head>")
    print("<body>")
    print(ind + "<div class=\"container\">")
    print(ind*2 + "<div class=\"title\"><h1>Error!</h1></div>")
    print(ind*2 + "<div class=\"text\"><h2>" + string + "</h2></div>")
    print(ind + "</div>")
    print("</body>")
    print("</html>")
    sys.exit()

def document_header(string):
    if string == "html":
        print("Content-type: text/html\n")
        print("<!DOCTYPE html>")
        try:
            print("<html lang=\"" + language + "\">")
        except NameError:
            print("<html 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\">")

# Parse configuration (with backward compatibilty)
try:
    configuration = configparser.SafeConfigParser()
except:
    configuration = ConfigParser.SafeConfigParser()

for config in ["../blogthonrc", "../.blogthonrc", "configuration"]:
    if os.path.exists(config):
        configuration.read(config)
        config = True
        break
    else:
        config = False

if not config:
    errorpage("No suitable configuration found!")

try:
    blog_title = configuration.get("personal", "blog_title")
    blog_subtitle = configuration.get("personal", "blog_subtitle")
    blog_url = configuration.get("personal", "blog_url")
    keywords = configuration.get("personal", "keywords")
    entries_dir = configuration.get("personal", "entries_dir")
    entries_suffix = configuration.get("personal", "entries_suffix")
    staticpages_dir = configuration.get("personal", "staticpages_dir")
    plugins_dir = configuration.get("personal", "plugins_dir")
    style = configuration.get("look", "style")
    language = configuration.get("look", "language")
    entries_per_page = configuration.getint("look", "entries_per_page")
    monthlist = configuration.get("look", "monthlist")
    staticpages = configuration.get("look", "staticpages")
    linklist = configuration.get("look", "linklist")
    permalinks = configuration.get("look", "permalinks")
    comments = configuration.get("look", "comments")
    newest_first = configuration.get("look", "newest_first")
    new_comment_mail = configuration.get("smtp", "new_comment_mail")
    mail_to = configuration.get("smtp", "mail_to")
    smtp_host = configuration.get("smtp", "smtp_host")
    feed_preview = configuration.get("feed", "feed_preview")
except configparser.Error as error:
    errorpage(str(error))
# And for backward compatibility
except ConfigParser.Error as error:
    errorpage(str(error))

if not re.match("^http:\/\/.*$", blog_url):
    blog_url = "http://" + blog_url

if not re.match("^.*\/$", blog_url):
    blog_url = blog_url + "/"

if not os.path.exists(entries_dir):
    errorpage("Directory \"%s\" does not exist!" % entries_dir)

if not os.access(entries_dir, os.W_OK):
    errorpage("Directory \"%s\" is not writable!" % entries_dir)

if not os.path.exists(staticpages_dir):
    errorpage("Directory \"%s\" does not exist!" % staticpages_dir)

if not os.path.exists(plugins_dir):
    errorpage("Directory \"%s\" does not exist!" % plugins_dir)

if not os.path.exists("linklist"):
    errorpage("File \"linklist\" does not exist!")

if language == "de":
    blog_locale = ("Seiten", "Monate", "Links", "Keine Kommentare", "Kommentare", "Alle Einträge anzeigen...", "Name", "Text", "Absenden")
    locales_de = ("de_DE.UTF-8", "de_DE.@euro", "de_DE")
    for i in locales_de:
        try: 
            locale.setlocale(locale.LC_TIME, i)
            break
        except:
            continue
    else:
        locale.setlocale(locale.LC_TIME, None)

else:
    blog_locale = ("pages", "months", "links", "no comments", "comments", "View all entries...", "name", "text", "commit")
    locales_en = ("en_US.UTF-8", "en_US.ISO-8859-15", "en_US")
    for i in locales_en:
        try:
            locale.setlocale(locale.LC_TIME, i)
            break
        except:
            continue
    else:
        locale.setlocale(locale.LC_TIME, None)

# Read POST Variables
action = 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 = os.path.join(entries_dir, ctitle + ".comments")
        if not os.path.exists(comments_file):
            content = open(comments_file, "w")
            content.close()

        content = open(comments_file, "a")
        content.write("-." + cname + "\n")
        content.write("+." + time.strftime("%c", time.localtime()) + "\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: %s\nSubject: New comment on %s\n\nSomeone wrote a comment to this entry: %s?p=%s" % (mail_to, blog_title, blog_url, ctitle.replace(" ", "-"))
            smtp = SMTP(smtp_host)
            smtp.starttls()
            smtp.sendmail(blog_title, mail_to, msg)
            smtp.quit()

# Read entries and store their title and timestamp
entries = []
entries_list = glob(os.path.join(entries_dir, "*." + entries_suffix))

for entry in entries_list:
    title = entry.replace(entries_dir, "", 1)
    title = title.replace("." + entries_suffix, "")
    
    stampfile = os.path.join(entries_dir, title + ".stamp")
    if os.path.exists(stampfile):
        timestamp = os.stat(stampfile)
    else:
        timestamp = os.stat(entry)
        stampfile = os.path.join(entries_dir, title + ".stamp")
        stamp = open(stampfile, "w")
        stamp.close()
        utime = os.utime(stampfile, (os.stat(entry)[8], os.stat(entry)[8]))

    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=\"%s?feed=atom\" rel=\"self\" type=\"application/atom+xml\"/>" % blog_url)
    print(ind + "<author>")
    print(ind*2 + "<name>%s</name>" % blog_title)
    print(ind + "</author>")
    print(ind + "<title>%s</title>" % blog_title)
    print(ind + "<id>urn:uuid:%s</id>" % blog_title_md5sum)
    print(ind + "<updated>%s-%s-%sT%s:%s:%sZ</updated>" % (str(date[0]), month, day, hour, min, sec))
    print("")
    j = len(entries)
    if j > 10: j = 10
    for i in range(0, j):
        title = str(entries[i][1]).replace(entries_dir, "", 1).replace("." + entries_suffix, "")
        date = entries[i][0]
        title_md5sum = generate_uuid(title)
        print(ind*2 + "<entry>")
        print(ind*3 + "<title>%s</title>" % title)
        print(ind*3 + "<link href=\"%s?p=%s\"/>" % (blog_url, title))
        print(ind*3 + "<id>urn:uuid:%s</id>" % title_md5sum)
        print(ind*3 + "<updated>%s-%s-%sT%s:%s:%sZ</updated>" % (str(date[0]), month, day, hour, min, sec))
        print(ind*3 + "<summary>")
        content = open(str(entries[i][1]), "r")
        for h in range(0, int(feed_preview)):
            rss_line = content.readline().strip()
            if rss_line != "":
                print(ind*4 + rss_line)
        content.close()
        print(ind*3 + "</summary>")
        print(ind*2 + "</entry>")
    print("</feed>")

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

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

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

    # Site header
    print(ind*2 + "<div class=\"header\">")
    print(ind*3 + "<div class=\"header_title\">")
    print(ind*4 + "<a href=\"?\" class=\"header_link\">%s</a>" % blog_title)
    print(ind*3 + "</div>")
    print(ind*3 + "<div class=\"header_subtitle\">")
    print(ind*4 + "<span class=\"header_subtitle\">%s</span>" % blog_subtitle)
    print(ind*3 + "</div>")
    print(ind*2 + "</div>")
    print("")

    # RSS feed
    print(ind*2 + "<div class=\"rss\">")
    print(ind*3 + "<a href=\"?feed=rss\" class=\"rss_link\">rss</a>")
    print(ind*2 + "</div>")
    print("")

    # Atom feed
    print(ind*2 + "<div class=\"atom\">")
    print(ind*3 + "<a href=\"?feed=atom\" class=\"atom_link\">atom</a>")
    print(ind*2 + "</div>")
    print("")

    # Staticpages
    if staticpages == "True":
        staticpages = []
        staticpages_list  = glob(os.path.join(staticpages_dir, "*"))
        staticpages_list.sort()
        print(ind*2 + "<div class=\"pages\">")
        print(ind*3 + "<div class=\"pages_title\">%s</div>" % blog_locale[0])
        print(ind*3 + "<div class=\"pages_list\">")
        print(ind*4 + "<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=%s" % link
            file.close()
            title = re.sub("\w+?\/\d+?-", "", staticpage)
            print(ind*5 + "<li class=\"pages_list_entry\"><a href=\"%s\" class=\"pages_list_entry\">%s</a></li>" % (link, title))
        print(ind*4 + "</ul>")
        print(ind*3 + "</div>")
        print(ind*3 + "<div class=\"pages_footer\"></div>")
        print(ind*2 + "</div>")
        print("")

    # Monthlist
    if monthlist == "True":
        olddate = ""
        print(ind*2 + "<div class=\"months\">")
        print(ind*3 + "<div class=\"months_title\">%s</div>" % blog_locale[1])
        print(ind*3 + "<div class=\"months_list\">")
        print(ind*4 + "<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(ind*5 + "<li class=\"months_list_entry\"><a href=\"?m=%s\" class=\"months_list_entry\">%s</a></li>" % (date, date_display))
            olddate = date
        print(ind*4 + "</ul>")
        print(ind*3 + "</div>")
        print(ind*3 + "<div class=\"months_footer\"></div>")
        print(ind*2 + "</div>")
        print("")

    # Linklist
    if linklist == "True":
        print(ind*2 + "<div class=\"linklist\">")
        print(ind*3 + "<div class=\"linklist_title\">%s</div>" % blog_locale[2])
        print(ind*3 + "<div class=\"linklist_list\">")
        print(ind*4 + "<ul class=\"linklist_list\">")
        try:
            content = open("linklist", "r")
            for line in content:
                if line.strip() is "":
                    print("<br />")
                else:
                    print(ind*5 + "<li class=\"linklist_list_entry\"><a href=\"%s\" class=\"months_list_entry\">%s</a></li>" % (line.split(" ")[0], line.split(" ", 1)[1].strip()))
            content.close()
        except:
            print("")
        print(ind*4 + "</ul>")
        print(ind*3 + "</div>")
        print(ind*3 + "<div class=\"linklist_footer\"></div>")
        print(ind*2 + "</div>")
        print("")

    print(ind*2 + "<div class=\"entries\">")
    print("")

    # Staticpage
    if static_display != "":
        content = open(os.path.join(staticpages_dir, static_display), "r")
        print(ind*3 + "<div class=\"entry\">")
        print(ind*4 + "<div class=\"entry_title\">%s</div>" % re.sub("^\.", "", re.sub("\d+?-", "", static_display)))
        print(ind*4 + "<div class=\"entry_content\">")
        print(ind*5 + "<p>")
        for line in content:
            if no_break.match(line):
                print(ind*5 + line.strip())
            else:
                print(ind*5 + line.strip() + "<br />")
        print(ind*5 + "</p>")
        print(ind*4 + "</div>")
        print(ind*4 + "<div class=\"entry_footer\"></div>")
        print(ind*4 + "<div class=\"entry_border_left\"></div>")
        print(ind*4 + "<div class=\"entry_border_right\"></div>")
        print(ind*4 + "<div class=\"entry_border_top\"></div>")
        print(ind*4 + "<div class=\"entry_border_bottom\"></div>")
        print(ind*3 + "</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_dir, "", 1)
            title = title.replace("." + entries_suffix, "")

            stampfile = os.path.join(entries_dir, title + ".stamp")
            if os.path.exists(stampfile):
                date = time.localtime(os.stat(stampfile)[8])
                date = time.strftime("%c", date)

            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(ind*3 + "<div class=\"entry\">")
                        if permalinks:
                            print(ind*4 + "<div class=\"entry_title\"><a href=\"?p=%s\" class=\"entry_title\">%s</a></div>" % (title.replace(" ", "-"), title))
                        else:
                            print(ind*4 + "<div class=\"entry_title\">%s</div>" % title)
                        print(ind*4 + "<div class=\"entry_date\">%s</div>" % date)
                        print(ind*4 + "<div class=\"entry_content\">")
                        for line in content:
                            if no_break.match(line):
                                print(ind*5 + line.strip())
                            else:
                                print(ind*5 + line.strip() + "<br />")
                        print(ind*4 + "</div>")
                        print(ind*4 + "<div class=\"entry_footer\"></div>")
                        print(ind*4 + "<div class=\"entry_border_left\"></div>")
                        print(ind*4 + "<div class=\"entry_border_right\"></div>")
                        print(ind*4 + "<div class=\"entry_border_top\"></div>")
                        print(ind*4 + "<div class=\"entry_border_bottom\"></div>")

                        # Comments...
                        # ... are shown when post_display and comments_file isn't false
                        comments_file = glob(os.path.join(entries_dir, title + ".comments"))
                        if post_display:
                            if comments_file:
                                comments_content = open(comments_file[0], "r")
                                print(ind*3 + "</div>")
                                print(ind*2 + "</div>")
                                print("")
                                print(ind*2 + "<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(ind*4 + "</div>")
                                            print(ind*3 + "</div>")
                                            notfirstline = 0
                                        print(ind*3 + "<div class=\"comment\">")

                                        # Label for each comment
                                        label_count += 1
                                        print(ind*4 + "<a name=\"%s\"></a>" % str(label_count))

                                        print(ind*4 + "<div class=\"comment_author\">%s</div>" % line.split(".", 1)[1].strip())
                                    elif line_start_plus.match(line):
                                        print(ind*4 + "<div class=\"comment_date\">%s</div>" % line.split(".", 1)[1].strip())
                                        print(ind*4 + "<div class=\"comment_content\">")
                                    else:
                                        notfirstline = 1
                                        line = line.split(".", 1)[1]
                                        print(ind*5 + line.strip() + "<br />")
                                        print("")
                                print(ind*4 + "</div>")
                                print(ind*3 + "</div>")
                                comments_content.close()
                            else:
                                print(ind*3 + "</div>")
                                print(ind*2 + "</div>")
                                print(ind*2 + "<div class=\"comments\">")

                            # Form for adding comments
                            if comments == "True":
                                random_int_a = randint(1,9)
                                random_int_b = randint(1,9)
                                cquizv = random_int_a + random_int_b
                                print(ind*3 + "<div class=\"submit_comment\">")
                                print(ind*4 + "<form action=\"\" method=\"post\">")
                                print(ind*5 + "<input type=\"hidden\" name=\"ctitle\" value=\"%s\" />" % title)
                                print(ind*5 + "<input type=\"hidden\" name=\"cquizv\" value=\"%s\" />" % str(cquizv))
                                print(ind*5 + "<label class=\"submit_comment_name\">%s:</label><input class=\"submit_comment_name_input\" type=\"text\" id=\"cname\" name=\"cname\" />" % blog_locale[6])
                                print(ind*5 + "<br /><label class=\"submit_comment_text\">%s:</label><textarea class=\"submit_comment_textarea\" id=\"ctext\" name=\"ctext\"></textarea>" % blog_locale[7])
                                print(ind*5 + "<br /><label class=\"submit_comment_quiz\">%s+%s=</label><input class=\"submit_comment_quiz_input\" type=\"text\" id=\"cquiz\" name=\"cquiz\" />" % (str(random_int_a), str(random_int_b)))
                                print(ind*5 + "<br /><input class=\"submit_comment_button\" type=\"submit\" id=\"submit\" value=\"%s\" />" % blog_locale[8])
                                print(ind*4 + "</form>")
                                print(ind*3 + "</div>")
                            else:
                                print(ind*3 + "<div class=\"submit_border_bottom\"></div>")
                                print("")

                        if comments == "True":
                            comments_file = glob(os.path.join(entries_dir, title + ".comments"))
                            if not comments_file and not post_display:
                                print(ind*4 + "<div class=\"entry_comment\">")
                                print(ind*5 + "<a href=\"?p=%s\" class=\"entry_comment\">%s</a>" % (title.replace(" ", "-"), blog_locale[3]))
                                print(ind*4 + "</div>")
                                print(ind*3 + "</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(ind*4 + "<div class=\"entry_comment\">")
                                print(ind*5 + "<a href=\"?p=%s\" class=\"entry_comment\">%s (%s)</a>" % (title.replace(" ", "-"), blog_locale[4], str(comments_counter)))
                                print(ind*4 + "</div>")
                                print(ind*3 + "</div>")
                                print("")
                                comments_content.close()
                        else:
                            print(ind*3 + "</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(ind*3 + "<div class=\"show_all\"><a href=\"?a=1\">%s</a></div>" % blog_locale[5])

    print(ind*2 + "</div>")
    print("")
    print(ind + "</body>")
    print("</html>")

# vim: set sw=4 tw=0 ts=4 expandtab: