<?php
/*
(c) 2005 Vincent Caron <v.caron@zerodeux.net>
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
$blof_version = '0.1.0';
/*
TODO
- blof_set(opt, val):
name: blog name (for RSS)
var_prefix: prefix CGI vars
- enhanced text (links, images, lists)
- enhanced text: online quickref for author
- thread mod+versionning
- comments delete(by author)
- locking: have a big 'write lock' for mod/del (steal lock_ex code from misc/lock.php)
- RSS feed
- translations (minimal)
*/
/*
* Auth/prefs
*/
function blof_login() {
$_SESSION['blof_auth'] = 'is_author';
}
function blof_logout() {
$_SESSION['blof_auth'] = '';
}
function blof_is_author() {
return ($_SESSION['blof_auth'] == 'is_author');
}
function blof_print_login_form($info = '') {
global $blof_opt;
if (!session_id()) {
blof_print_error(
"session not started",
"You must call ".blof_phpman("session_start()")." in order authentification works.");
} else
if (!isset($blof_opt['passwd'])) {
blof_print_error(
"no password has been set",
"You must call ".blof_phpman("blof_set('passwd', 'your_password')").".</p>");
} else {
$cookie = session_get_cookie_params();
if ($cookie['lifetime']) {
$date = strftime('%c', time() + $cookie['lifetime']);
$hint = "You will be automatically logged in until $date.";
} else
$hint = "You will be automatically logged out when you close your browser.";
$hint .= " Use ".blof_phpman("session_set_cookie_params()")." to change this.";
$body = <<<EOF
<form method="post" action="" id="logform">
<p>
<b>Password:</b> <input type="password" name="passwd" />
<input type="hidden" name="a" value="login" />
<input type="submit" value=" Login " />
</p>
</form>
<script type="text/javascript"><!--
document.forms["logform"].passwd.focus();
// -->
</script>
<p>$info</p>
<p>$hint</p>
EOF;
blof_print_block('thread', 'Author login', '', $body);
}
blof_print_footer(blof_home());
}
function blof_handle_login_form() {
global $blof_opt;
if (!isset($blof_opt['passwd']))
return 0;
if ($_REQUEST['passwd'] != $blof_opt['passwd']) {
blof_print_login_form(" <p><b>Error</b>: wrong password.</p>\n");
return 0;
}
blof_login();
return 1;
}
/*
* Email notification
*/
function blof_notify($id, $meta, $text) {
global $blof_opt;
$email = $blof_opt['email'];
if ($email == '')
return;
$title = $meta['Title'];
$from = $meta['From'];
if ($title == '') {
$id = dirname($id);
if (!blof_read_block($id, $parent_meta, $parent_text))
return;
$title = "Re: ".$parent_meta['Title'];
}
$url = "http://".$_SERVER['HTTP_HOST'].preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI'])."?tid=$id";
$message = <<<EOF
$from said :
$text
--
$url
EOF;
mail($email, $title, $message);
}
/*
* Index (ie. list of thread_id's or comment_id's)
* The global blof_index holds the complete sorted list of threads.
*/
function blof_scandir($path) {
global $blof_opt;
$scan = array();
$path = $blof_opt['store'].($path != '' ? "/".$path : "");
if (!($dh = opendir($path)))
return $scan;
while (($fname = readdir($dh)) !== false)
$scan[] = $fname;
closedir($dh);
return $scan;
}
function blof_is_thread_id($id) {
return preg_match('/^\d{4}-\d{2}-\d{2}-\d+$/', $id);
}
function blof_sort_index() {
global $blof_index;
if (!isset($blof_index))
return;
natsort($blof_index); // To properly sort threads posted the same day
$blof_index = array_reverse($blof_index);
}
function blof_get_index() {
global $blof_index;
if (!isset($blof_index)) {
$blof_index = array_filter(blof_scandir(''), "blof_is_thread_id");
blof_sort_index();
}
return $blof_index;
}
function blof_add_index($thread_id) {
$blof_index = blof_get_index();
$blof_index[] = $thread_id;
blof_sort_index();
}
function blof_is_comment_id($id) {
return preg_match('/^comment-\d+$/', $id);
}
function blof_get_comments($thread_id) {
$comments = array_filter(blof_scandir($thread_id), "blof_is_comment_id");
natsort($comments);
return $comments;
}
function blof_rss() {
header('Content-Type: text/xml');
echo "RSS test ...";
exit;
}
/*
* Common store operations
*/
function blof_store_is_writable() {
global $blof_opt;
$store = $blof_opt['store'];
if (!is_writable($store)) {
blof_print_store_error(
"The folder <code>'$store'</code> cannot be written to by your web server.");
return FALSE;
}
return TRUE;
}
/* Only mkdir() is known to be atomic and race-free over networked fs.
*/
function blof_store_alloc_id($base) {
global $blof_opt;
$store = $blof_opt['store'];
for ($i = 1; $i <= 999; $i++) { // This is a safety max, can be pushed up
$id = "$base-$i";
$path = "$store/$id";
if (@mkdir($path))
return $id;
}
return '';
}
function blof_write_block($id, $mode, $meta, $text) {
global $blof_opt;
$file = fopen($blof_opt['store']."/$id/content", $mode);
if (!$file)
return 0;
$head = '';
foreach($meta as $key => $val) {
/* It is important to strip off EOL values to make header parsing
* work in blof_read_block(see fgets() doc, depends on platform and
* auto_detect_line_endings setting). */
$head .= "$key: ".str_replace(array("\r", "\n"), " ", $val)."\n";
}
$head .= "\n";
$ok = fwrite($file, $head) && fwrite($file, $text);
fclose($file);
return $ok;
}
function blof_read_block($id, &$meta, &$text) {
global $blof_opt;
$meta = array();
$text = '';
$file = fopen($blof_opt['store']."/$id/content", 'r');
if (!$file)
return 0;
$state = 0;
while (!feof($file)) {
$line = fgets($file);
if ($state == 0) {
$line = rtrim($line);
if ($line == '') {
$state = 1;
continue;
}
// We're doing it the old way to spare a preg_split()
$colon = strpos($line, ':');
$key = substr($line, 0, $colon);
$val = substr($line, $colon+2);
$meta[$key] = $val;
}
else if ($state == 1) {
$text .= $line;
}
}
fclose($file);
return 1;
}
/*
* Display
*/
function blof_phpman($func) {
$ref = preg_replace('/\(.*$/', '', $func);
$ref = str_replace('_', '-', $ref);
if ($ref == '' || preg_match('/^blof-/', $ref))
return "<code>$func</code>";
return "<a href=\"http://php.net/manual/function.$ref\"><code>$func</code></a>";
}
function blof_print_error($error, $explain) {
echo <<<EOF
<div class="blof_error">
<p>Blof error</b>: $error.</p>
<p>$explain</p>
</div>
EOF;
}
function blof_print_store_error($text) {
$func = blof_phpman("blof_set('store', 'your/folder/here')");
$explain = <<<EOF
$text
Please either fix the appropriate file permissions, or use $func to set your desired location.
In any case, the web server must be able to write into this folder.
EOF;
blof_print_error('could not create data store', $explain);
}
function blof_print_block($class, $title, $header, $body, $actions = NULL) {
echo "<div class=\"blof_$class\">\n";
if ($title) echo " <div class=\"blof_title\">$title</div>\n";
if ($header) echo " <div class=\"blof_header\">$header</div>\n";
if ($body) echo " <div class=\"blof_body\">\n$body\n </div>\n";
if ($actions) {
$actionbar = $actions ? implode(" |\n ", $actions) : "";
echo " <div class=\"blof_action\">\n$actionbar\n </div>\n";
}
echo "</div>\n\n";
}
function blof_print_footer($more) {
global $blof_opt;
$footer = $blof_opt['footer'];
$brag = $blof_opt['brag'];
$validate = $blof_opt['validate'];
$actions = array();
if ($footer != '') $actions[] = $footer;
if ($brag) $actions[] = "<a href=\"?a=about\">Powered by Blof</a>";
if ($validate) {
$actions[] = "<a href=\"http://validator.w3.org/check?uri=referer\">Valid XHTML</a>";
$actions[] = "<a href=\"http://jigsaw.w3.org/css-validator/check/referer\">Valid CSS</a>";
}
if (blof_is_author()) {
$actions[] = "<a href=\"?a=tidform\">Post a new thread</a>";
$actions[] = "<a href=\"?a=logout\">Logout</a>";
} else
$actions[] = "<a href=\"?a=logform\">Login</a>";
$actions[] = "<a href=\"?a=rss\">RSS</a>";
if (is_array($more))
$actions = array_merge ($actions, $more);
else if ($more != '')
$actions[] = $more;
echo "<div class=\"blof_footer\">\n ";
echo implode(" |\n ", $actions);
echo "\n</div>\n";
}
function blof_home() {
return '<a href="?a=none">Home</a>';
}
/*
* Thread display UI
*/
/* Interpret text enhancements in a Wiki-style syntax.
* The order of the different transformations is important!
*/
function blof_enhance_text($text) {
$fixed = htmlspecialchars($text);
$text = '';
foreach(explode("\n", $fixed) as $line) {
$line = rtrim($line);
if ($line == '')
continue;
// Font styles
$markup = array('*' => 'b', '_' => 'u', '/' => 'i');
$line = preg_replace('/(\*|_|\/)([^\*_\/]+)\1/e', '"<span class=\"blof_".$markup["\1"]."\">\2</span>"', $line);
// Make paragraphs
$text .= ($text == "" ? "" : "\n")." <p>$line</p>";
}
return $text;
}
/* Caps $text to $charcount chars, rounding to the nearest word boundary.
* If capping really occurs, append the '[...]' string.
* Must be applied on raw text, otherwise can break HTML tags.
*/
function blof_cap_text($text, $charcount) {
$len = strlen($text);
if ($len <= $charcount)
return $text;
$text = substr($text, 0, $charcount);
$text = preg_replace('/\s+[^\s]*$/', '', $text); // kill any leftover piece of word
if (strlen($text) < $len)
$text .= ' [...]';
return $text;
}
function blof_format_header($from, $date, $extra) {
return "Posted by <span class=\"blof_from\">$from</span> on <span class=\"blof_date\">$date</span>$extra";
}
/* Print a formatted thread (title, header, body, actions - no comments).
*/
function blof_print_thread($thread_id, $folded, $actions) {
global $blof_opt;
if (!blof_read_block($thread_id, $meta, $text))
return;
// Fix meta data for HTML output
$title = htmlspecialchars($meta['Title']);
$from = htmlspecialchars($meta['From']);
$date = htmlspecialchars($meta['Date']);
// Compose header
$delay = isset($meta['Delay']) ? " (typed in ".$meta['Delay']." seconds)" : "";
$header = blof_format_header($from, $date, $delay);
// Compose body and acton (ie. navigation)
if ($folded)
$text = blof_cap_text($text, $blof_opt['cap_fold']);
$body = blof_enhance_text($text);
// Compose actions
if ($folded)
array_unshift($actions, "<a href=\"?tid=$thread_id\">Read more ...</a>");
else
array_unshift($actions, blof_home());
blof_print_block('thread', $title, $header, $body, $actions);
}
/* Print a formatted comment
*/
function blof_print_comments($tid) {
$count = 0;
foreach(blof_get_comments($tid) as $cid) {
if (!blof_read_block("$tid/$cid", $meta, $data))
continue;
// Fix meta data for HTML output
$from = htmlspecialchars($meta['From']);
$date = htmlspecialchars($meta['Date']);
$header = blof_format_header($from, $date, '');
$header = "<a name=\"$cid\">$header</a>";
$body = blof_enhance_text($data);
blof_print_block('comment'.($count & 1 ? 'alt' : ''), '', $header, $body);
$count++;
}
}
/* This is the default view.
* We must handle navigation (newer, older, offset). Threads are displayed folded.
* Algorithm designed to do a single pass over the $index array.
*/
function blof_print_threads() {
global $blof_opt;
$tid = $_REQUEST['tid'];
if (blof_is_thread_id($tid)) {
// Single thread (full) view
$fold = 0;
$pager = 1;
$offset = $tid;
} else {
// Multiple thread (folded) view
$fold = 1;
$pager = $blof_opt['per_page'];
$offset = $_REQUEST['to'];
$offset = blof_is_thread_id($offset) ? $offset : '';
}
$index = blof_get_index();
$todo = $pager;
$order = 0;
$more = 0;
foreach($index as $id) {
if ($todo <= 0) {
$more = 1; // All threads displayed, and there are more
break;
}
if ($offset != '' && $offset != $id) {
$order++;
continue; // First displayed thread not found yet
}
$offset = ''; // Found, turn off thread search
// Retrieve comments index (cheap lookup) for display count
$thread_nb = count(blof_get_comments($id));
$thread_nb = $thread_nb ? $thread_nb : "none";
$actions = array("<a href=\"?tid=$id&a=cidform\">Post a comment</a> ($thread_nb)");
blof_print_thread($id, $fold, $actions);
$todo--;
}
if (!$folded)
blof_print_comments($tid);
$actions = array();
if ($order) {
$order = max(0, $order - $pager);
$actions[] = "<a href=\"?to=".$index[0]."\"><<</a>";
$actions[] = "<a href=\"?to=".$index[$order]."\">< See newer threads</a>";
}
if ($more) {
$max = count($index);
$end = $index[max(0, $max - $pager)];
$actions[] = "<a href=\"?to=$id\">See older threads ></a>";
$actions[] = "<a href=\"?to=$end\">>></a>";
}
blof_print_footer($actions);
}
/*
* Thread posting UI and handler
*/
function blof_print_thread_form() {
global $blof_opt;
blof_store_is_writable(); // Better warn the user early
$now = time();
$date = strftime('%c', $now);
$from = $blof_opt['author'];
if (!isset($from))
$hint = " (<u>Hint:</u> call blof_set('author', 'your_name') to prefill this field)";
$body = <<<EOF
<form method="post" action="" id="tform">
<table>
<tr>
<td><b>Title:</b> </td>
<td><input name="title" size="80" /></td>
</tr>
<tr>
<td><b>From:</b> </td>
<td><input type="text" name="from" value="$from" />$hint</td>
</tr>
<tr>
<td><b>Date:</b> </td>
<td>around $date</td>
</tr>
<tr>
<td></td>
<td><textarea name="text" cols="80" rows="25"></textarea></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="hidden" name="a" value="tidadd" />
<input type="hidden" name="ctime" value="$now" />
<input type="submit" value=" Post new thread " />
</td>
</tr>
</table>
</form>
<script type="text/javascript"><!--
document.forms["tform"].title.focus();
// -->
</script>
EOF;
blof_print_block("thread", "Post a new thread", "", $body);
blof_print_footer(blof_home());
}
function blof_handle_thread_form() {
if (!blof_store_is_writable())
return;
// Retrieve input, check if we have enough data
$title = $_REQUEST['title'];
$from = $_REQUEST['from'];
$text = $_REQUEST['text'];
$ctime = $_REQUEST['ctime'];
if (!isset($title) || !isset($text))
return 0;
// Allocate new thread_id
$now = time();
$base = strftime('%Y-%m-%d', $now);
$thread_id = blof_store_alloc_id($base);
if ($thread_id == '')
return 0;
// Parse/fix input
if (get_magic_quotes_gpc()) { // What a stupid hack, fix the APIs first!
$title = stripslashes($title);
$from = stripslashes($from);
$text = stripslashes($text);
}
$ctime = is_numeric($ctime) ? $ctime : 0;
// Fill in thread meta values
$meta = array();
$meta['Title'] = $title;
$meta['From'] = $from;
$meta['Date'] = strftime('%Y-%m-%d %H:%M %Z', $now);
$meta['Delay'] = $ctime ? ($now - $ctime) : 0;
$meta['IP-Address'] = $_SERVER['REMOTE_ADDR'];
if (!blof_write_block($thread_id, "w", $meta, $text))
return 0;
blof_add_index($thread_id);
blof_notify($thread_id, $meta, $text);
return 1;
}
/*
* Comment posting UI and handler
*/
function blof_print_comment_form() {
global $blof_opt;
$tid = $_REQUEST['tid'];
if (!blof_is_thread_id($tid))
return 0;
blof_store_is_writable(); // Better warn the user early
blof_print_thread($tid, TRUE, array());
$from = htmlspecialchars($_SESSION['blof_from']);
$focus = ($from == '') ? 'from' : 'text';
$body = <<<EOF
<form method="post" action="" id="cform">
<table>
<tr>
<td><b>From:</b> </td>
<td><input type="text" name="from" value="$from" /></td>
</tr>
<tr>
<td></td>
<td><textarea name="text" cols="80" rows="15"></textarea></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="hidden" name="a" value="cidadd" />
<input type="hidden" name="tid" value="$tid" />
<input type="submit" value=" Post comment " />
</td>
</tr>
</table>
</form>
<script type="text/javascript"><!--
document.forms["cform"].$focus.focus();
// -->
</script>
EOF;
blof_print_block('thread', 'Post a comment', '', $body);
blof_print_footer(blof_home());
return 1;
}
function blof_handle_comment_form() {
if (!blof_store_is_writable())
return 0;
// Retrieve input, check if we have enough data
$tid = $_REQUEST['tid'];
$from = $_REQUEST['from'];
$text = $_REQUEST['text'];
if (!isset($tid) || !isset($text) || !blof_is_thread_id($tid))
return 0;
// Allocate new thread_id
$now = time();
$base = "$tid/comment";
$comment_id = blof_store_alloc_id($base);
if ($comment_id == '')
return 0;
// Parse/fix input
if (get_magic_quotes_gpc()) {
$from = stripslashes($from);
$text = stripslashes($text);
}
if ($from == '')
$from = 'Anonymous';
else
$_SESSION['blof_from'] = $from;
// Fill in comment meta values
$meta = array();
$meta['From'] = $from;
$meta['Date'] = strftime('%Y-%m-%d %H:%M %Z', $now);
$meta['IP-Address'] = $_SERVER['REMOTE_ADDR'];
$meta['Referer'] = $tid;
if (!blof_write_block($comment_id, "w", $meta, $text))
return 0;
blof_notify($comment_id, $meta, $text);
return 1;
}
/*
* Embedded info and doc
*/
function blof_handle_info($action, $embedded) {
global $blof_version;
$about = <<<EOF
<p>Blof is a <em>very</em> simple blog implementation, for the real thing see
<a href="http://www.dotclear.net">DotClear</a> or
<a href="http://www.geeklog.net">Geeklog</a>.</p>
<p>Features:</p>
<ul>
<li>Single PHP file - trivial install and upgrade</li>
<li>No database - stores all data in flat files</li>
<li>Simple setup - follow the embedded tutorial</li>
<li>Conforms to XHTML 1.0 strict and CSS 2.1</li>
<li>GPL license</li>
</ul>
<p>Limitations (by design):</p>
<ul>
<li>Single author - no login, only a password</li>
<li>Flat threading - no nested comments</li>
</ul>
<p>You can <a href="?a=doc">browse the documentation</a>,
<a href="?a=src">browse the source code</a> or
<a href="?a=dl">download it</a>.</p>
EOF;
$xhtml = <<<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!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">
EOF;
$example = <<<EOF
<?php
require 'blof.php';
blof_set('passwd', 'mypass');
blof_init();
?>
$xhtml
<head>
<title>My blog</title>
<link rel="stylesheet" href="blof.css" />
</head>
<body>
<?php blof_main(); ?>
</body>
</html>
EOF;
$example = highlight_string($example, TRUE);
$css = <<<EOF
div.blof_thread { margin-top: 1em; margin-bottom: 2em; }
div.blof_thread
div.blof_title { font-size: 1.25em; font-weight: bold; border-bottom: solid black 1px; }
div.blof_thread
div.blof_header { }
div.blof_comment { margin-bottom: 0.5em; padding: 0.25em; background-color: #e0e0e0; border: solid black 1px; }
div.blof_commentalt { margin-bottom: 0.5em; padding: 0.25em; background-color: #d0d0d0; border: solid black 1px; }
div.blof_comment
div.blof_header,
div.blof_commentalt
div.blof_header { font-weight: bold; }
div.blof_body { text-align: justify; margin-top: 1em; }
div.blof_body p { margin: 0.5em; }
div.blof_action { text-align: center; }
div.blof_footer { text-align: center; margin-top: 2em; border-top: dotted gray 1px; }
div.blof_error { background-color: #f0b0b0; }
span.blof_from { font-style: italic; }
span.blof_date { }
span.blof_u { text-decoration: underline; }
span.blof_i { font-style: italic; }
span.blof_b { font-weight: bold; }
pre.blof_code { margin: 1em; background-color: #d0d0d0; }
EOF;
$doc = <<<EOF
<p>Here is a simple and complete example, let's call it <code>demo.php</code>:</p>
<pre class="blof_code">
$example
</pre>
<p>Explanations for every item:</p>
<ul>
<li>First, you need to link to the Blof code whith <code>require</code>. In
this example, <code>blof.php</code> is in the same folder as <code>demo.php</code>.</li>
<li>Then you must set a password, otherwise you won't be able to login and post new threads.</li>
<li>Then you call <code>blof_init()</code> before sending any header.</li>
<li>Finally, call <code>blof_main()</code> to embed the blog anywhere in the HTML body.</li>
</ul>
<p>Here is a default stylesheet (referred as <code>blof.css</code> in the previous example):</p>
<pre class="blof_code">
$css
</pre>
<p>Blof can be customised with <code>blof_set('param', 'value')</code> where <code>param</code> is:</p>
<ul>
<li><code>store</code>: folder where thread data is stored. Defaults to the name of the embedder
with its file extension removed (ie. embedding in <code>demo.php</code> will store data
in the <code>demo/</code> folder).</li>
<li><code>author</code>: sets the default author's name, can be changed while posting.</li>
<li><code>passwd</code>: sets the author's password, mandatory.</li>
<li><code>email</code>: set the author's email for posting notifications.</li>
<li><code>brag</code>: add a 'powered by Blof' link in the footer (set to 0 or 1).</li>
<li><code>validate</code>: add 'Valid XHTML' and 'Valid CSS' links to W3C validator in the footer (set to 0 or 1).</li>
<li><code>footer</code>: custom footer note, appears on the left.</li>
<li><code>per_page</code>: number of threads per page.</li>
<li><code>cap_fold</code>: number of characters displayed by a folded thread.</li>
</ul>
EOF;
$title = "Blof $blof_version";
if ($action != 'about' && $action != 'doc' && $action != 'src')
return 0;
if (!$embedded)
echo <<<EOF
$xhtml
<head>
<title>$title</title>
<style type="text/css">
$css
</style>
</head>
<body>
EOF;
if ($action == 'about') blof_print_block("thread", $title, "", $about);
if ($action == 'doc') blof_print_block("thread", $title, "", $doc);
if ($action == 'src') highlight_file(__FILE__);
if ($embedded)
blof_print_footer(blof_home());
else
echo <<<EOF
</body>
</html>
EOF;
return 1;
}
/* Use to send the script itself for self-download
*/
function blof_sendfile($fname) {
$file = fopen($fname, 'r');
if (!$file)
return;
header('Content-Type: text/plain');
fpassthru($file);
fclose($file);
exit;
}
/*
* Main routine
*/
function blof_set($opt, $val) {
global $blof_opt;
$blof_opt[$opt] = $val;
}
function blof_init() {
global $blof_opt;
if (!session_id())
session_start();
$action = $_REQUEST['a'];
$brag = $blof_opt['brag'];
if (!isset($brag) || $brag) {
if ($action == 'dl') { blof_sendfile(__FILE__); }
}
if ($action == 'rss') { blof_rss(); }
}
function blof_main() {
global $blof_opt;
// Set some sane defaults were value are not defined
$defaults = array(
'store' => preg_replace('/\.[^\.]+$/', '', $_SERVER['SCRIPT_FILENAME']),
'cap_fold' => 500,
'per_page' => 5,
'footer' => '© 2005 - All rights reserved',
'brag' => 1,
'validate' => 0
);
foreach($defaults as $key => $val)
if (!isset($blof_opt[$key]))
$blof_opt[$key] = $val;
// Check data store, build if necessary
$store = $blof_opt['store'];
if (!file_exists($store)) {
if (!mkdir($store)) {
blof_print_store_error(
"The folder <code>'$store'</code> does not exist on your web server ".
"and could not be automatically created for you.");
return;
}
}
// Handle various actions
$action = $_REQUEST['a'];
if ($action == 'logout') { blof_logout(); }
if (blof_is_author()) {
if ($action == 'tidform') { blof_print_thread_form(); return; }
if ($action == 'tidadd') { blof_handle_thread_form(); }
//if ($action == 'tidmod') {}
//if ($action == 'tiddel') {}
} else {
if ($action == 'logform') { blof_print_login_form(); return; }
if ($action == 'login') { if (!blof_handle_login_form()) return; }
}
if ($action == 'cidform') { if (blof_print_comment_form()) return; }
if ($action == 'cidadd') { blof_handle_comment_form(); }
if ($blof_opt['brag'] && blof_handle_info($action, TRUE)) return;
blof_print_threads();
}
if ($_SERVER['SCRIPT_FILENAME'] == __FILE__) {
$action = $_REQUEST['a'];
$action = isset($action) ? $action : 'about';
if (blof_handle_info($action, FALSE))
return;
if ($action == 'dl') { blof_sendfile(__FILE__); }
}
?>