@ -25,6 +25,8 @@
* For more information, please refer to < http: / / unlicense . org / >
* For more information, please refer to < http: / / unlicense . org / >
*/
*/
$start_time = microtime(true);
// Return source code
// Return source code
if(isset($_GET['source'])) {
if(isset($_GET['source'])) {
header("Content-Type: text/plain");
header("Content-Type: text/plain");
@ -40,7 +42,6 @@
* define('ADMIN_ID', '< SHA1 uid from your cookies > '); // uid that is able to delete anything
* define('ADMIN_ID', '< SHA1 uid from your cookies > '); // uid that is able to delete anything
* define('UID_SALT', '< some string to salt uids with > ');
* define('UID_SALT', '< some string to salt uids with > ');
* define('MAX_POSTS', 50); // Max posts after which old posts will be auto deleted. Only existing posts count, not manually deleted ones.
* define('MAX_POSTS', 50); // Max posts after which old posts will be auto deleted. Only existing posts count, not manually deleted ones.
* define('DISCORD_WEBHOOK', 'https://discord.com/api/webhooks/< webhook id > /< webhook token > '); // A message will be sent to this webhook on post, for easier moderation
*
*
* // Database constants for PostgreSQL database
* // Database constants for PostgreSQL database
* $DB_HOST = 'localhost';
* $DB_HOST = 'localhost';
@ -55,6 +56,7 @@
* post_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
* post_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
* user_id VARCHAR(40),
* user_id VARCHAR(40),
* name VARCHAR(256),
* name VARCHAR(256),
* trip VARCHAR(10),
* email VARCHAR(256),
* email VARCHAR(256),
* comment VARCHAR(2048),
* comment VARCHAR(2048),
* img VARCHAR(256)
* img VARCHAR(256)
@ -81,21 +83,30 @@
& & ($img['size'] > 0) & & ($img['size'] < = MAX_FILE_SIZE)) {
& & ($img['size'] > 0) & & ($img['size'] < = MAX_FILE_SIZE)) {
if($img['error'] == 0) {
if($img['error'] == 0) {
// Move the file to the target upload folder
// Move the file to the target upload folder
$name = time();
$img_name = time();
$target = UPLOAD_PATH . $name . $extensions[$img['type']];
$ext = $extensions[$img['type']];
if(move_uploaded_file($img['tmp_name'], $target)) {
$temp = UPLOAD_PATH . "__temp$ext";
$target = UPLOAD_PATH . $img_name . $ext;
if(move_uploaded_file($img['tmp_name'], $temp)) {
$output = null;
$output = null;
$ret = null;
$ret = null;
exec("ffmpeg -y -loglevel error -i '$target' -vf 'scale=-1:min(100\,ih)' " . THUMB_PATH . "$name.jpg", $output, $ret);
// Strip EXIF data
exec("ffmpeg -y -loglevel error -i $temp '$target'", $output, $ret);
@unlink($temp);
if($ret != 0)
return 'Unable to upload image, please contact the webmaster.';
// Create thumbnail
exec("ffmpeg -y -loglevel error -i '$target' -filter_complex 'color=#ffccdd[c];[c][0]scale2ref[cs][0s];[cs][0s]overlay=shortest=1[o];[o]scale=min(200\,iw):-1[o];[o]scale=-1:min(100\,ih)' " . THUMB_PATH . "$img_name.jpg", $output, $ret);
if($ret != 0) {
if($ret != 0) {
// The new image file move failed, so delete the temporary file and return an error
@unlink($target);
@unlink($target);
return 'Unable to create thumbnail, please contact the webmaster.';
return 'Unable to create thumbnail, please contact the webmaster.';
}
}
} else {
} else {
// The new image file move failed, so delete the temporary file and return an error
// The new image file move failed, so delete the temporary file and return an error
@unlink($img['tmp_name']);
@unlink($img['tmp_name']);
return 'Unable to upload image, please contact the webmaster.';
return 'Unable to rename image, please contact the webmaster.';
}
}
}
}
} else {
} else {
@ -131,16 +142,18 @@
}
}
}
}
$trip_name = trip_name($name);
// Add post to database
// Add post to database
$query = "INSERT INTO posts (user_id, name, email, comment, img) VALUES ($1, $2, $3, $4, $5)";
$query = "INSERT INTO posts (user_id, name, trip, email, comment, img) VALUES ($1, $2, $3, $4, $5, $6 )";
$params = [
$params = [
empty($uid) ? NULL : $uid,
empty($uid) ? NULL : $uid,
empty($name) ? 'Anonymous' : htmlspecialchars($name),
empty($trip_name['name']) ? 'Anonymous' : htmlspecialchars($trip_name['name']),
empty($trip_name['trip']) ? NULL : htmlspecialchars($trip_name['trip']),
empty($email) ? NULL : $email,
empty($email) ? NULL : $email,
empty($comment) ? NULL : htmlspecialchars($comment),
empty($comment) ? NULL : htmlspecialchars($comment),
empty($target) ? NULL : basename($target)
empty($target) ? NULL : basename($target)
];
];
webhook($params[1], $params[3], 'http://' . $_SERVER['SERVER_NAME'] . dirname($_SERVER['PHP_SELF']) . $target); // Send to discord for moderation
pg_query_params($query, $params) or die('Query failed: ' . pg_last_error());
pg_query_params($query, $params) or die('Query failed: ' . pg_last_error());
return ""; // Success, no error
return ""; // Success, no error
@ -160,7 +173,7 @@
// Prints the post list
// Prints the post list
function show_posts() {
function show_posts() {
$query = 'SELECT post_id, user_id, name, email, comment, img, TO_CHAR(post_time, \'YYYY-MM-DD HH24:MI (TZ)\') AS post_time FROM posts ORDER BY posts.post_time';
$query = 'SELECT post_id, user_id, name, trip, email, comment, img, post_time FROM posts ORDER BY posts.post_time';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
// Clean up old posts
// Clean up old posts
@ -174,28 +187,39 @@
// Print posts
// Print posts
$show_delete = FALSE;
$show_delete = FALSE;
echo '< form action = "' . $_SERVER['PHP_SELF'] . '#bottom" method = "post" > ';
echo '< form action = "' . $_SERVER['PHP_SELF'] . '#bottom" method = "post" > ';
while($row = pg_fetch_array($result)) {
while($row = pg_fetch_array($result)) {
echo "< fieldset id = \"p{$row['post_id']}\" > ";
echo "< div id = \"p{$row['post_id']}\" class = \"post\" > ";
$time_rel = time_ago_en($row['post_time']);
$time_abs = date('Y-m-d H:m (T)', strtotime($row['post_time']));
echo '< legend > ';
echo '< p class = "posthead" > ';
if((!empty($row['user_id']) & & ($row['user_id'] == $_COOKIE['uid'])) || (!empty(ADMIN_ID) & & ($_COOKIE['uid'] == ADMIN_ID))) {
if((!empty($row['user_id']) & & ($row['user_id'] == $_COOKIE['uid'])) || (!empty(ADMIN_ID) & & ($_COOKIE['uid'] == ADMIN_ID))) {
echo '< input type = "checkbox" name = "delete[]" value = "' . $row['post_id'] . '" / > ';
echo '< input type = "checkbox" name = "delete[]" value = "' . $row['post_id'] . '" / > ';
$show_delete = TRUE;
$show_delete = TRUE;
}
}
echo '< strong class = "name" > ';
if(!empty($row['email']))
if(!empty($row['email']))
echo "< strong > < a href = \"mailto:{$row['email']}\" > {$row['name']}< / a > < / strong > {$row['post_time']} ";
echo "< a href = \"mailto:{$row['email']}\" > {$row['name']}< / a > ";
else
else
echo "< strong > {$row['name']}< / strong > {$row['post_time']} ";
echo $row['name'];
echo '< / strong > ';
if(!empty($row['trip']))
echo "< span class = \"trip\" > ◆{$row['trip']}< / span > ";
echo " < span title = \"$time_abs\" > $time_rel< / span > ";
echo "< a href = \"#p{$row['post_id']}\" > #{$row['post_id']}< / a > ";
echo "< a href = \"#p{$row['post_id']}\" > #{$row['post_id']}< / a > ";
// Find references
// Find references
$post_id = pg_escape_string($row['post_id']);
$post_id = pg_escape_string($row['post_id']);
$ref_query = "SELECT post_id FROM posts WHERE comment LIKE '%> > $post_id%' ORDER BY post_time";
$ref_query = "SELECT post_id FROM posts WHERE comment LIKE '%> > $post_id%' ORDER BY post_time";
$ref_result = pg_query_params($ref_query, []) or die('Query failed: ' . pg_last_error());
$ref_result = pg_query_params($ref_query, []) or die('Query failed: ' . pg_last_error());
while($ref = pg_fetch_array($ref_result))
while($ref = pg_fetch_array($ref_result))
echo "< a href = \"#p{$ref['post_id']}\" > > > {$ref['post_id']}< / a > ";
echo "< a href = \"#p{$ref['post_id']}\" > > > {$ref['post_id']}< / a > ";
echo '< / legend > ';
echo '< / p > ';
echo '< div class = "postbody" > ';
if(!empty($row['img'])){
if(!empty($row['img'])){
echo '< a href = "' . UPLOAD_PATH . $row['img'] . '" target = "_blank" > ';
echo '< a href = "' . UPLOAD_PATH . $row['img'] . '" target = "_blank" > ';
echo '< img src = "' . THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img']) . '" alt = "' . $row['img'] . '" / > ';
echo '< img src = "' . THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img']) . '" alt = "' . $row['img'] . '" / > ';
@ -205,14 +229,15 @@
// Process quotes, links, and newlines
// Process quotes, links, and newlines
if(!empty($row['comment'])) {
if(!empty($row['comment'])) {
$comment = $row['comment'];
$comment = $row['comment'];
$comment = preg_replace('/^> (?!> \d).+/m', '< strong > $0< / strong > ', $comment);
$comment = preg_replace('/^> (?!> \d).+/m', '< strong class = "quote" > $0< / strong > ', $comment);
$comment = preg_replace('/(?:https?|mailto|tel|ftp):[^\s]+/m', '< a href = "$0" > $0< / a > ', $comment);
$comment = preg_replace('/(?:https?|mailto|tel|ftp):[^\s]+/m', '< a href = "$0" > $0< / a > ', $comment);
$comment = preg_replace_callback('/> > \s*(\d+)/', quote_link, $comment);
$comment = preg_replace_callback('/> > \s*(\d+)/', quote_link, $comment);
$comment = str_replace("\n", "< br / > ", $comment);
$comment = str_replace("\n", "< br / > ", $comment);
echo "< p> $comment< / p > ";
echo "< div> $comment< / div > ";
}
}
echo '< / div > ';
echo '< / fieldset > ';
echo '< / div > ';
}
}
pg_free_result($result);
pg_free_result($result);
@ -234,37 +259,91 @@
pg_query_params($query, [$id]) or die('Query failed: ' . pg_last_error());
pg_query_params($query, [$id]) or die('Query failed: ' . pg_last_error());
}
}
}
}
// https://stackoverflow.com/a/9619947
function time_ago_en($time) {
if(!is_numeric($time))
$time = strtotime($time);
$periods = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'age'];
$lengths = [60, 60, 24, 7, 4.35, 12, 100];
$now = time();
$difference = $now - $time;
if($difference < = 10 & & $difference >= 0)
return $tense = 'just now';
else if($difference > 0)
$tense = 'ago';
else if($difference < 0 )
$tense = 'later';
for($j = 0; $difference >= $lengths[$j] & & $j < count ( $ lengths ) -1 ; $ j + + ) {
$difference /= $lengths[$j];
}
// Sends a webhook to Discord
$difference = round($difference);
function webhook($name, $message, $img) {
if(empty(DISCORD_WEBHOOK))
$period = $periods[$j] . ($difference >1 ? 's' :'');
return;
return "{$difference} {$period} {$tense} ";
}
$data = [
'username' => $name,
function trip_name($name) {
'embeds' => [
$split = explode('#', $name);
[
$name = array_shift($split);
'title' => "New Post",
if(empty($split[0]))
'url' => 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . '#bottom',
return ['name' => $name];
'description' => $message,
'image' => [
$key = implode('#', $split);
'url' => $img
$salt = substr($key . 'H.', 1, 2);
]
$salt = preg_replace('/[^\.-z]/', '.', $salt);
]
$salt = strtr($salt, ':;< =>?@[\\]^_`', 'ABCDEFGabcdef');
]
$trip = crypt($key, $salt);
$trip = substr($trip, -10);
return [
'name' => $name,
'trip' => $trip
];
];
}
$curl = curl_init(DISCORD_WEBHOOK);
//// code start ////
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Connect to the database
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
$dbc = pg_connect("host=$DB_HOST dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD")
curl_setopt($curl, CURLOPT_POST, true);
or die('Could not connect: ' . pg_last_error());
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_exec($curl);
if($_POST['submit'] == 'Post') {
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
// Grab the data from the POST
curl_close($curl);
$name = trim($_POST['name']);
if($status != 204)
$email = trim($_POST['email']);
die("Error: Sending webhook failed with status $status.");
$comment = trim($_POST['comment']);
$img = $_FILES['img'];
$save_cookie = isset($_POST['save_cookie']);
$err = post($name, $email, $comment, $img, $save_cookie);
if($err == "") {
# Redirect to latest post
$query = 'SELECT post_id FROM posts ORDER BY posts.post_time DESC LIMIT 1';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
$row = pg_fetch_array($result);
if($row) {
pg_close($dbc);
header("Location: {$_SERVER['PHP_SELF']}#p{$row['post_id']}", true, 303);
die();
}
}
} else if($_POST['submit'] == 'Delete' & & !empty($_COOKIE['uid'])) {
foreach($_POST['delete'] as $id) {
cleanup($id);
}
}
$mobile = preg_match('/Mobile|Nintendo DSi/', $_SERVER['HTTP_USER_AGENT']);
if($mobile) {
$header_image = 'header-mobile.gif';
} else {
$header_image = 'header.gif';
}
}
?>
?>
< !DOCTYPE html
< !DOCTYPE html
@ -276,87 +355,244 @@
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
< title > BBS | ピケ.コム< / title >
< title > BBS | ピケ.コム< / title >
< link rel = "alternate" type = "application/rss+xml" title = "RSS feed" href = "/rss.php" / >
< style type = "text/css" >
body {
max-width: 960px;
background-color: #ebc;
background: url(bg2.gif);
background-attachment: fixed;
color: #201;
}
hr {
border: 0;
border-top: 1px solid #534;
}
textarea {
max-width: 100%;
}
input[type=submit],
button {
background-color: #ebc;
border: 2px outset #dab;
}
input[type=submit]:active,
button:active {
background-color: #dab;
border: 2px inset #dab;
color: #201;
}
strong.error, del {
color: red;
}
strong.quote,
strong.name, strong.name > a,
span.trip {
color: green;
}
strong.quote {
font-weight: normal;
}
#newpost {
max-width: 500px;
padding-bottom: 10px;
}
.center {
margin-left: auto;
margin-right: auto;
display: block;
}
.post,
#newpost {
background-color: #fcd;
border: 5px outset #dab;
margin-bottom: 10px;
}
#newpost > h2 {
margin: 0;
}
#newpost input:not([type=submit]),
#newpost textarea {
background-color: #ebc;
border: 2px inset #dab;
}
#footer {
background-color: #ebc;
border: 5px inset #dab;
padding: 10px;
}
#footer p:first-of-type {
margin-top: 0;
}
#footer p:last-of-type {
margin-bottom: 0;
}
.post {
display: table;
padding: 0 10px 10px 0;
}
.posthead {
margin: 0 0 5px 0;
}
.postbody {
padding-left: 10px;
}
.postbody img {
float: left;
margin-right: 10px;
}
< / style >
< / head >
< / head >
< body >
< body class = "center" >
< img class = "center" src = " <?php echo $header_image ; ?> " alt = "BBS.ピケ.コム" />
< p >
< p >
[< a href = "#bottom" > bottom< / a > ]
[< a href = "#bottom" > bottom< / a > ]
[< a href = " <?php echo $_SERVER [ 'PHP_SELF' ]; ?> " > reload</ a > ]
[< a href = "?source" > source< / a > ]
[< a href = "rss.php" > feed< / a > ]
[< a href = "rss.php?source" > feed source< / a > ]
< / p >
< / p >
<?php
< hr / >
// Connect to the database
$dbc = pg_connect("host=$DB_HOST dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD")
< form enctype = "multipart/form-data" method = "post" action = " <?php echo $_SERVER [ 'PHP_SELF' ]; ?> #newpost" >
or die('Could not connect: ' . pg_last_error());
< input type = "hidden" name = "MAX_FILE_SIZE" value = " <?php echo MAX_FILE_SIZE ; ?> " />
< div id = "newpost" class = "center" >
if($_POST['submit'] == 'Post') {
< h2 > New Post< / h2 >
// Grab the data from the POST
$name = trim($_POST['name']);
< div class = "postbody" >
$email = trim($_POST['email']);
<?php if ( $mobile ) { ?>
$comment = trim($_POST['comment']);
< label for = "name" > Name:< / label > (Optional)
$img = $_FILES['img'];
< br / >
$save_cookie = isset($_POST['save_cookie']);
< input id = "name" name = "name" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $name ); ?> " />
< br / >
$err = post($name, $email, $comment, $img, $save_cookie);
< br / >
} else if($_POST['submit'] == 'Delete' & & !empty($_COOKIE['uid'])) {
foreach($_POST['delete'] as $id) {
< label for = "email" > Email:< / label > (Optional)
cleanup($id);
< br / >
}
< input id = "email" name = "email" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $email ); ?> " />
}
< br / >
< br / >
< label for = "comment" > Comment:< / label >
< br / >
< textarea id = "comment" name = "comment" rows = "10" cols = "27" > <?php if ( ! empty ( $err )) echo htmlspecialchars ( $comment ); ?> </ textarea >
< br / >
< br / >
< label for = "img" > Image:</ label > (Limit: <?php echo MAX_FILE_SIZE >> 10 ; ?> KiB)
< br / >
< input type = "file" id = "img" name = "img" / >
< br / >
< br / >
< label for = "save-cookie" > Save cookie:< / label >
< input type = "checkbox" id = "save-cookie" name = "save_cookie" <?php if ( $_COOKIE [ 'uid' ]) echo 'checked' ; ?> />
< br / >
(Allows deleting your own posts)
< br / >
< br / >
< input type = "submit" value = "Post" name = "submit" / >
<?php } else { ?>
< table >
< tr >
< td > < label for = "name" > Name:< / label > < / td >
< td >< input id = "name" name = "name" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $name ); ?> " /> (Optional)</ td >
< / tr >
< tr >
< td > < label for = "email" > Email:< / label > < / td >
< td >< input id = "email" name = "email" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $email ); ?> " /> (Optional)</ td >
< / tr >
< tr >
< td > < label for = "comment" > Comment:< / label > < / td >
< td >< textarea id = "comment" name = "comment" rows = "10" cols = "40" > <?php if ( ! empty ( $err )) echo htmlspecialchars ( $comment ); ?> </ textarea ></ td >
< / tr >
< tr >
< td > < label for = "img" > Image:< / label > < / td >
< td >< input type = "file" id = "img" name = "img" /> (Limit: <?php echo MAX_FILE_SIZE >> 10 ; ?> KiB)</ td >
< / tr >
< tr >
< td > < label for = "save-cookie" > Save cookie:< / label > < / td >
< td >< input type = "checkbox" id = "save-cookie" name = "save_cookie" <?php if ( $_COOKIE [ 'uid' ]) echo 'checked' ; ?> /> (Allows deleting your own posts)</ td >
< / tr >
< tr >
< td > < / td >
< td > < input type = "submit" value = "Post" name = "submit" / > < / td >
< / tr >
< / table >
<?php } ?>
<?php if ( ! empty ( $err )) echo "<p><strong class= \" error \" > $err </strong></p>" ; ?>
< / div >
< / div >
< / form >
< hr / >
<?php
show_posts();
show_posts();
pg_close($dbc);
pg_close($dbc);
?>
?>
< form enctype = "multipart/form-data" method = "post" action = " <?php echo $_SERVER [ 'PHP_SELF' ]; ?> #bottom" >
< hr / >
< input type = "hidden" name = "MAX_FILE_SIZE" value = " <?php echo MAX_FILE_SIZE ; ?> " />
< fieldset id = "bottom" >
< div id = "footer" >
< legend > New Post< / legend >
< p >
Welcome to my silly little bulletin board. It's primarily intended
< table >
for personal use transferring images from devices where that is
< tr >
otherwise difficult, however anyone is free to use it.
< td > < label for = "name" > Name:< / label > < / td >
Old posts are automatically deleted once there are more than
< td >< input id = "name" name = "name" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $name ); ?> " /> (Optional)</ td >
<?php echo MAX_POSTS ; ?> , anything inappropriate will be deleted.
< / tr >
< / p >
< tr >
< p >
< td > < label for = "email" > Email:< / label > < / td >
Formatting is very simple, just < strong class = "quote" > > quote< / strong >
< td >< input id = "email" name = "email" value = " <?php if ( ! empty ( $err )) echo htmlspecialchars ( $email ); ?> " /> (Optional)</ td >
by starting a line with > and link to other posts with two & gt
< / tr >
and the post number, < del > > > 1< / del > . No other formatting is
< tr >
supported besides the automatic conversion of hyperlinks.
< td > < label for = "comment" > Comment:< / label > < / td >
You may provide a name and/or email address when posting if you wish
< td >< textarea id = "comment" name = "comment" rows = "10" cols = "40" > <?php if ( ! empty ( $err )) echo htmlspecialchars ( $comment ); ?> </ textarea ></ td >
to identify yourself, however it is not required. A tripcode can be
< / tr >
used by writing "Name#Password" in the name field.
< tr >
< / p >
< td > < label for = "img" > Image:< / label > < / td >
< p >
< td >< input type = "file" id = "img" name = "img" /> (Limit: <?php echo MAX_FILE_SIZE >> 10 ; ?> KiB)</ td >
The year is <?php echo date ( 'Y' ); ?> , but no © . This page was generated in <?php printf ( "%.2f" , microtime ( true ) - $start_time ); ?> seconds.
< / tr >
< / p >
< tr >
< / div >
< td > < label for = "save-cookie" > Save cookie:< / label > < / td >
< td >< input type = "checkbox" id = "save-cookie" name = "save_cookie" <?php if ( $_COOKIE [ 'uid' ]) echo 'checked' ; ?> /> (Allows deleting your own posts)</ td >
< p id = "bottom" >
< / tr >
< tr >
< td > < / td >
< td > < input type = "submit" value = "Post" name = "submit" / > < / td >
< / tr >
< / table >
<?php if ( ! empty ( $err )) echo "<br /><strong> $err </strong>" ; ?>
< / fieldset >
< / form >
< p >
Old posts are automatically deleted once there are more than <?php echo MAX_POSTS ; ?> , anything inappropriate will be deleted.
< / p >
< p >
[< a href = "#top" > top< / a > ]
[< a href = "#top" > top< / a > ]
[< a href = " <?php echo $_SERVER [ 'PHP_SELF' ]; ?> " > reload</ a > ]
[< a href = " <?php echo $_SERVER [ 'PHP_SELF' ]; ?> " > reload</ a > ]
[< a href = "?source" > source< / a > ]
[< a href = "?source" > source< / a > ]
[< a href = "rss.php" > feed< / a > ]
[< a href = "rss.php?source" > feed source< / a > ]
< / p >
< / p >
< p >
< p >
< a href = "//validator.w3.org/check?uri= <?php echo urlencode ( 'http://' . $_SERVER [ 'SERVER_NAME' ] . $_SERVER [ 'PHP_SELF' ]); ?> " target = "_blank" >
< a href = "//validator.w3.org/check?uri= <?php echo urlencode ( 'http://' . $_SERVER [ 'SERVER_NAME' ] . $_SERVER [ 'PHP_SELF' ]); ?> " target = "_blank" >< img src = "//www.w3.org/Icons/valid-xhtml10" alt = "Valid XHTML 1.0 Transitional" height = "31" width = "88" /></ a >
< img src = "//www.w3.org/Icons/valid-xhtml10" alt = "Valid XHTML 1.0 Transitional" height = "31" width = "88" / >
< a href = "https://jigsaw.w3.org/css-validator/check/referer" > < img style = "border:0;width:88px;height:31px" src = "https://jigsaw.w3.org/css-validator/images/vcss" alt = "Valid CSS!" / > < / a >
< / a >
< / p >
< / p >
< / body >
< / body >
< / html >
< / html >