1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-03 11:46:22 -07:00

fixed database handling, fixed http handles, fixed configuration handling, added preliminary templating, etc

This commit is contained in:
Joshua Merrell 2013-04-20 01:21:40 -07:00
parent e6f6124a1f
commit 68b8c03980
45 changed files with 2742 additions and 632 deletions

View file

@ -1,2 +1,2 @@
#!/bin/bash
go build -o gochan ./src
go build -v -o gochan ./src

View file

@ -1,95 +1,85 @@
[server]
domain = gochan.net
port = 80
document_root = /path/to/document_root/
port = 8080
first_page = board.html,index.html
error_404_path = /error/404.html
error_500_path = /error/500.html
username = gochan
[directories]
document_root = html
template_dir = templates
log_dir = log
[database]
type = mysql
host =
host = unix(/var/run/mysqld/mysqld.sock)
name = gochan
username = myuser
password = mypass
prefix = gochan_
username = root
password = passwd
prefix = gochan
keepalive = false
[gochan]
lockdown = false
lockdown_message = We apologize for the downtime, but we are switching servers. Gochan wil be back up shortly
lockdown_message = This is the message displayed to a user if they try to post when lockdown is set to true.
sillytags = Admin,Mod,Janitor,Faget,Kick me,Derpy
use_sillytags = false
mod_board = secretmodfun
[site]
name = Gochan
slogan = "Do you like mmmmbananas?"
headerurl =
irc =
banreason =
allowdupes = true
webfolder = /beta
webpath = http://gochan.net/
webfolder = /
domain = gochan.net
root_dir =
template_dir = templates
[styles]
styles = burichan,futaba
default = burichan
switcher = true
dropdown_switcher = false
styles = pipes,burichan,futaba,braeburn
default_style = pipes
styles_txt = buritxt,futaba
default_txt-style = buritxt
txt_style_switcher = true
menu_type = normal
menu_styles = burichan:futaba
default_menu_style = burichan
menu_style_switcher = true
default_txt_style = buritxt
[posting]
allow_duplicate_images = true
new_thread_delay = 30
reply_delay = 7
line_length = 150
max_line_length = 150
reserved_trips = "#from:To,#changeme2:changeme2"
[thumbnails]
thumb_width = 200
thumb_height = 200
reply_thumb_width = 125
reply_thumb_height = 125
catalog_thumb_width = 50
catalog_thumb_height = 50
animated_thumbs = false
new_window = true
make_links = true
no_message_thread =
no_message_reply =
[threads]
img_threads_per_page = 10
txt_threads_per_page = 15
replies_on_boardpage = 3
sticky_replies_on_boardpage = 1
thumb_msg = false
ban_colors = Luna:#0000A0,PinkiePie#FF1493,Fleur de Lis:pink,Rainbow Dash:red
ban_msg = <br /><span style=\"color: \""+GB_BANCOLOR+"\"><b>(USER WAS BANISHED AND THEN THROWN IN A DUNGEON IN THE PLACE THAT THEY WERE BANISHED TO FOR THIS POST)</b></span>
traditional_read = false
gen_last50_page = true
gen_first100_page = false
ban_colors = admin:#0000A0,mod#FF1493
ban_msg = <br /><span style=\"color: \""+BANCOLOR+"\"><b>(USER WAS BANNED FOR THIS POST)</b></span>
youtube_width = 200
youtube_height = 164
first_page = board.html"
use_dir_title = false
make_rss = true
expand = true
expand_button = true
images_open_new_tab = true
make_urls_hyperlinked = true
new_tab_on_outlinks = true
quick_reply = true
watched_threads = true
gen_firstlast_pages -= true
use_blotter = true
make_sitemap = false
[misc]
default_ban_reason =
enable_geoip = true
max_recent_posts = 10
make_rss = true
make_sitemap = true
enable_appeals = true
max_modlog_days = 14
random_seed = String to use as seed for randomly generated fun
use_static_menu = false
generate_boardlist = true
date_format = D, F d, Y g:i A
random_seed = DONT Taz3 m3 br0hgyuftyufrtderydrtiygyuigtd5456w53eyrtdughu
enable_geoip = true
geoip_location = /usr/share/GeoIP/GeoIP.dat

BIN
html/135683008384s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
html/135693079632s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -124,7 +124,8 @@ body {
font-family:sans-serif;
font-size:75%;
margin:8px;
width:90%;
width:100%;
height: 100%;
}
body,html {

View file

@ -22,6 +22,10 @@
font-size:50px;
}
#site-slogan {
font-size:25px;
}
#top-pane {
height:75px;
left:0;

View file

@ -4,7 +4,7 @@ body {
background: #EEF2FF;
color: #000;
margin: 0;
width: 90%;
/*width: 90%;*/
}
#topmenu li {
display: block;
@ -38,6 +38,8 @@ div#loginbox {
top:50%;
margin-left: -100px;
margin-top: -35px;
padding-top:5px;
padding-bottom:5px;
text-align: center;
border: solid 1px;
}

127
html/css/global/front.css Normal file
View file

@ -0,0 +1,127 @@
#footer {
clear:both;
position:absolute;
width: 100%;
left: 0px;
bottom: 0%;
text-align: center;
}
#main {
position:absolute;
top:124px;
left:16%;
height: 100%;
right:21%;
width:auto;
}
#menu {
position:absolute;
}
#side-pane {
position:absolute;
top: 106px;
width:15%;
margin-right: 16px;
}
#site-title {
font-size:50px;
text-align: center;
}
#tab-bar {
margin-left: 16px;
clear:both;
}
#topmenu {
position:absolute;
top: 0px;
left: 0px;
width: 100%;
display: block;
z-index:10;}
.topmenu-item {
display: block;
float: left;
text-align: center;
}
.plus {
width: 12px;
text-align: center;
float:right;
padding: 0px 4px 0px 4px;
}
.tab {
padding-right: 5px;
padding-left: 5px;
display: block;
float: left;
}
.section-body {
overflow: hidden;
margin-right: 0px;
margin-bottom:8px;
right:0px;
min-height: 48px;
}
.section-block {
padding-bottom: 8px;
}
#top-pane {
left:0;
position:absolute;
text-align:center;
top:32px;
width:100%;
z-index: 1;
}
#content {
clear:left;
margin-left:0!important;
padding-left:0!important;
text-align:justify;
}
#recent-posts {
float:right;
margin-top: 98px;
overflow: hidden;
right: 8px;
width:20%;
}
#recent-posts img {
float: left;
padding-right: 8px;
}
#recent-posts .section-body {
padding-top: 8px;
}
.section-title-block {
display:block;
}
a.permalink {
float: right;
}
ul.boardmenu {
list-style:none;
margin:0;
padding-left:0;
}

0
html/css/global/img.css Normal file
View file

View file

@ -0,0 +1,27 @@
.loginbox {
width:300px;
height: 120px;
position: absolute;
left:50%;
top:50%;
margin-left: -150px;
margin-top: -60px;
padding-top:5px;
padding-bottom:5px;
text-align: center;
border: solid 1px;
}
.loginbox input {
width:175px;
height: 30%;
}
#footer {
clear:both;
position:absolute;
width: 100%;
left: 0px;
bottom: 0%;
text-align: center;
}

0
html/css/global/txt.css Normal file
View file

BIN
html/css/int-sprites.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -1,76 +1,52 @@
#main {
border-bottom:10px!important;
height:85%;
left:16%;
position:absolute;
top:96px;
width:auto;
}
#menu {
border:0;
height:100%;
left:0;
margin-left:5px;
padding:0;
position:absolute;
top:75px;
width:15%;
#footer {
font-size:12px;
}
#site-title {
background: inherit;
clear: both;
color: #e1b400;
font-family: sans-serif;
font-size:50px;
text-align: center;
width: 100%;
}
#top-pane {
height:75px;
left:0;
position:absolute;
#current-tab {
background-color: #404040;
}
.tab {
background-color: #202020;
border: 1px solid #424242;
text-align:center;
top:0;
width:100%;
}
.section-body {
background-color:#606060;
padding-left:8px;
padding-right: 8px;
padding-bottom: 8px;
}
.section-title-block {
height:19px;
background-color:#202020;
border-radius: 4px 4px 0px 0px;
padding-left:8px;
padding-right: 8px;
}
#topmenu {
left:20%;
padding-bottom:5;
position:absolute;
top:78px;
z-index: 2;
box-shadow: 0 2px 2px 3px #101010;
background-color: #202020;
height: 26px;
}
#topmenu li {
background-color:#404040;
border:1px solid #9295a4;
border-left:none;
border-bottom: none;
display:block;
float:left;
margin-top:-7px;
padding:3px 10px 2px;
.topmenu-item {
padding: 4px;
}
#topmenu li.current {
background-color:#202020;
border-bottom:none;
margin-top:-8px;
padding-top:2px;
.topmenu-item:hover {
background-color: #404040;
}
#topmenu li.first {
border-left:1px solid #9295a4;
}
#topmenu a {
.content {
margin-left:0!important;
padding-left:0!important;
text-align:justify;
}
.menu {
@ -78,44 +54,26 @@
text-align:center;
}
.newssub {
position:absolute;
background-color: #202020;
}
.permalink {
display:block;
text-align:right;
}
.permalink a {
a.permalink {
background: inherit;
color: #f90;
font-family: sans-serif;
text-decoration: none;
}
.permalink a:hover {
a.permalink:hover {
background: inherit;
color: #ffd43f;
font-family: sans-serif;
}
.plus {
background:#404040;
border-radius: 15px;
color:#000;
cursor:pointer;
float:right;
font-size:12px;
height:inherit;
font-weight:400;
margin:0;
padding:1px 4px 2px;
}
.plus:hover {
background:#c5c9e0;
border:1px solid #c97;
cursor:pointer;
}
a {
@ -132,79 +90,24 @@ a:hover {
}
body {
background:#EEF2FF;
color:#000;
font-family:sans-serif;
font-size:75%;
margin:8px;
width:90%;
}
body,html {
margin:0;
padding:0;
background: #32353d;
background: #EEF2FF;
background-attachment: fixed;
background-image: url(images/pipes_bg.png);
color: #d8d0b9;
font-family:sans-serif;
font-size: 12pt;
}
h1 {
color:#000;
font-size:150%;
margin:0;
text-align:center;
}
h1,h2 {
h2 {
background-color: #202020;
-moz-box-shadow: 2px 2px 3px 4px #101010;
-webkit-box-shadow: 2px 2px 3px 4px #101010;
box-shadow: 0 2px 2px 3px #101010;
border-radius: 8px;
text-align:left;
padding-left: 4px;
padding-right: 4px;
}
h1,h3,.menu {
font-family:Verdana,Tahoma,sans-serif;
}
h2 {
border-radius: 8px;
font-size:100%;
margin:1em 0 0;
}
h2 a {
color:#550;
text-decoration:none;
}
h3 {
color:#800;
font-size:medium;
font-weight:400;
margin:0;
text-align:center;
}
li {
margin:0;
}
li a {
display:block;
width:100%;
}
ul.boardmenu li:hover,ul.modmenulink li:hover {
ul.boardmenu li:hover {
background:#404040;
}
ul.boardmenu,ul.modmenulink,div#topmenu ul {
list-style:none;
margin:0;
padding-left:0;
}

106
html/css/pipes/manage.css Normal file
View file

@ -0,0 +1,106 @@
#footer {
font-size:12px;
}
#current-tab {
background-color: #404040;
}
.tab {
background-color: #202020;
border: 1px solid #424242;
text-align:center;
}
.section-body {
background-color:#606060;
padding-left:8px;
padding-right: 8px;
padding-bottom: 8px;
}
.section-title-block {
height:19px;
background-color:#202020;
border-radius: 4px 4px 0px 0px;
padding-left:8px;
padding-right: 8px;
}
#topmenu {
box-shadow: 0 2px 2px 3px #101010;
background-color: #202020;
height: 26px;
}
.topmenu-item {
padding: 4px;
}
.topmenu-item:hover {
background-color: #404040;
}
#topmenu a {
}
.loginbox {
}
.loginbox input {
height:20%;
}
.menu {
margin-top:1em;
text-align:center;
}
.plus {
background:#404040;
color:#000;
height:inherit;
font-weight:400;
}
.plus:hover {
background:#c5c9e0;
cursor:pointer;
}
a {
background: inherit;
color: #f90;
font-family: sans-serif;
text-decoration: none;
}
a:hover {
background: inherit;
color: #ffd43f;
font-family: sans-serif;
}
body {
background: #EEF2FF;
background-attachment: fixed;
background-image: url(images/pipes_bg.png);
color: #d8d0b9;
font-family:sans-serif;
font-size: 12pt;
}
h2 {
background-color: #202020;
-moz-box-shadow: 2px 2px 3px 4px #101010;
-webkit-box-shadow: 2px 2px 3px 4px #101010;
box-shadow: 0 2px 2px 3px #101010;
border-radius: 8px;
padding-left: 4px;
padding-right: 4px;
}
ul.boardmenu li:hover {
background:#404040;
}

13
html/error/404.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Error 404: File not Found</title>
</head>
<body>
<h1>404: File not found</h1>
<!--<img src="/error.png" border="0" alt=""> -->
<p>The requested file could not be found on this server. Are you just typing random stuff in the address bar? If you followed a link from this site here, then post <a href="/site">here</a></p>
<audio src="http://dl.dropbox.com/u/10266670/forever.wav" autoplay="autoplay"></audio>
<hr><address>http://lunachan.net powered by Gochan v0.1</address>
</body>
</html>

12
html/error/500.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Error 500: Internal Server error</title>
</head>
<body>
<h1>500: Internal Server error</h1>
<marquee><img src="/error/derpy server.gif" border="0" alt=""></marquee>
<p>Someone let Derpy into the server room again...sorry about that. Please try reloading the page. If that doesn't fix the problem, you can email me <a href="mailto:admin@lunachan.net">here</a>, noting the time and the page you tried to access.
<hr><address>http://lunachan.net powered by Gochan v0.2</address>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Error 500: Internal Server error</title>
</head>
<body>
<h1>500: Internal Server error</h1>
<p>The server encountered an error while trying to serve the page. We apologize for the inconvenience.</p>
<hr><address>http://lunachan.net powered by Gochan v0.2</address>
</body>
</html>

BIN
html/error/derpy server.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View file

@ -1,34 +1,166 @@
<!DOCTYPE html>
<html>
<head>
<title>It worked!</title>
<script type="text/javascript" src="javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
$jq = jQuery.noConflict();
$jq(document).ready(function() {
var killserver_btn = $jq("button#killserver");
$jq("button#killserver").click(function() {
$jq.ajax({
method:'GET',
url:"/manage",
data: {
action: 'killserver'
},
success: function() {
},
error:function() {
}
});
});
});
</script>
</head>
<body>
It works!<br />
<button id="killserver">Kill server</button>
</body>
</html
<!DOCTYPE html>
<html>
<head>
<title>Lunachan</title>
<script type="text/javascript" src="/javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/javascript/gochan.js"></script>
<link rel="stylesheet" href="/css/global/front.css" />
<link rel="stylesheet" href="/css/pipes/front.css" />
<link rel="alternate stylesheet" href="/css/burichan/front.css" />
<link rel="alternate stylesheet" href="/css/futaba/front.css" />
<link rel="alternate stylesheet" href="/css/braeburn/front.css" />
<link rel="alternate stylesheet" href="/css/leetchan/front.css" />
</head>
<body>
<div id="topmenu">
<div class="topmenu-section">
<div class="topmenu-item">
<a href="/test1/">/test1/</a>
</div>
<div class="topmenu-item">
<a href="/test2/">/test2/</a>
</div>
</div>
</div>
<div id="top-pane">
<span id="site-title">Lunachan</span><br />
<span id="site-slogan">Do you like mmmmmmbananas?</span>
</div>
<div id="side-pane">
<h2>Boards</h2>
<div class="section-block">
<div class="section-title-block">
<b>Section 1</b><span class="plus">-</span>
</div>
<div class="section-body">
<ul class="boardmenu">
<a href="/test/"><li id="test" class="boardmenu-item">Test</li></a>
<a href="/test2/"><li id="test" class="boardmenu-item">Test2</li></a>
</ul>
</div>
</div>
<div class="section-block">
<div class="section-title-block">
<b>Section 2</b><span class="plus">-</span>
</div>
<div class="section-body">
<ul class="boardmenu">
<a href="/test/"><li id="test" class="boardmenu-item">Test</li></a>
<a href="/test2/"><li id="test" class="boardmenu-item">Test2</li></a>
</ul>
</div>
</div>
</div>
<div id="main">
<div id="tab-bar">
<div id="current-tab" class="tab">
<a href="#">Home</a>
</div>
<div class="tab">
<a href="#rules">Rules</a>
</div>
<div class="tab">
<a href="#faq">FAQ</a>
</div>
</div>
<div id="content">
<div id="first-page" class="page">
<div class="section-block">
<div class="section-title-block" id="first-page0">
<b>A news message thingy</b> by <a href="mailto:admin@lunachan.net" >Luna</a><a href="/#first-page0" class="permalink">#</a>
</div>
<div class="section-body">
Some funky news stuff would go here
</div>
</div>
<div class="section-block">
<div class="section-title-block">
<b>A news message thingy</b> by <a href="mailto:admin@lunachan.net" class="section-poster">Luna</a>
</div>
<div class="section-body">
Some funky news stuff would go here
</div>
</div>
</div>
<div id="rules-page" class="page">
<div class="section-block">
<div class="section-title-block">
<b>Rules</b> by <a href="mailto:admin@lunachan.net" class="section-poster">Luna</a>
</div>
<div class="section-body">
Don't talk about Gochan.<br />
Do NOT talk about Gochan.<br />
Purple pone is best pone, no exceptions.<br />
No Comic Sans, breakers of this rule will be permabanned without warning<br />
</div>
</div>
</div>
<div id="faq-page" class="page">
<div class="section-block">
<div class="section-title-block">
<b>A FAQ message thingy</b> by <a href="mailto:admin@lunachan.net" class="section-poster">Luna</a>
</div>
<div class="section-body">
Some funky FAQ stuff would go here
</div>
</div>
<div class="section-block">
<div class="section-title-block">
<b>A FAQ message thingy</b> by <a href="mailto:admin@lunachan.net" class="section-poster">Luna</a>
</div>
<div class="section-body">
Some funky FAQ stuff would go here
</div>
</div>
</div>
</div>
<div id="footer">
<a href="/">Home</a> | <a href="http://127.0.0.1:8080/#boards">Boards</a> | <a href="http://127.0.0.1:8080/#rules">Rules</a> | <a href="http://127.0.0.1:8080/#faq">FAQ</a><br />
Powered by Gochan v0.2<br />
Generated in over 9000 hours
</div>
</div>
<div id="recent-posts">
<h2>Recent Posts</h2>
<div class="section-block">
<div class="section-title-block">
<span class="section-title"><a href="/test/">/test/</a></span> - <b>Poster Mc</b>!Tripcode
</div>
<div class="section-body">
<img src="135693079632s.png" />
TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT TOOT...<br /><i>(Comment truncated, click <a href="/test/">here</a> to continue reading)</i>
</div>
</div>
<div class="section-block">
<div class="section-title-block">
<span class="section-title"><a href="/test/">/test/</a></span> - <b>Anonymous</b>
</div>
<div class="section-body">
OP is a pony.
</div>
</div>
<div class="section-block">
<div class="section-title-block">
<span class="section-title"><a href="/test/">/test/</a></span> - <b>Poster Mc</b>!Tripcode
</div>
<div class="section-body">
<img src="135683008384s.png" />
<a href="#">&gt;&gt;22222</a><br />
HERP DERP FROSTED BUTTS
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,118 @@
var $jq = jQuery.noConflict();
function changeFrontPage(page_name) {
var tabs = $jq(".tab");
var pages = $jq(".page");
var current_page = getHashVal();
pages.hide();
if(current_page=="") {
$jq(pages[0]).show();
} else {
for(var p = 0; p < pages.length; p++) {
var page = $jq(pages[p]);
if(page.attr("id").replace("-page","").replace("page","") == current_page) {
page.show()
}
}
}
for(var i = 0; i < tabs.length; i++) {
var child = $jq(tabs[i]).children(0)
var tabname = child.text();
if(tabname.toLowerCase() == current_page) {
$jq("#current-tab").attr({"id":""});
child.parent().attr({"id":"current-tab"});
}
}
tabs.find("a").click(function(event) {
current_page = getHashVal($jq(this).attr("href"));
if(current_page == "") {
$jq("#current-tab").attr({"id":""});
$jq(tabs[0]).attr({"id":"current-tab"});
} else {
for(var i = 0; i < tabs.length; i++) {
var child = $jq(tabs[i]).children(0)
var tabname = child.text();
if(tabname.toLowerCase() == current_page) {
$jq("#current-tab").attr({"id":""});
$jq(tabs[i]).attr({"id":"current-tab"});
}
}
}
pages.hide()
if(current_page=="") {
$jq(pages[0]).show();
} else {
for(var p = 0; p < pages.length; p++) {
var page = $jq(pages[p]);
if(page.attr("id").replace("-page","").replace("page","") == current_page) {
page.show()
}
}
}
});
}
function getArg(name) {
var href = window.location.href;
var args = href.substr(href.indexOf("?")+1, href.length);
args = args.split("&");
for(var i = 0; i < args.length; i++) {
temp_args = args[i];
temp_args = temp_args.split("=");
temp_name = temp_args[0];
temp_value = temp_args[1];
args[temp_name] = temp_value;
args[i] = temp_args;
}
return args[name];
}
function getHashVal() {
var href = window.location.href;
if(arguments.length == 1) {
href = arguments[0];
}
if(href.indexOf("#") == -1) {
return "";
} else {
var hash = href.substring(href.indexOf("#"),href.length);
if(hash == "#") return ""
else return hash.substring(1,hash.length);
}
}
function isFrontPage() {
var page = window.location.pathname;
return page == "/" || page == "/index.html";
}
function isBoardPage() {
}
function isThreadPage() {
}
$jq(document).ready(function() {
if(isFrontPage()) {
changeFrontPage(getHashVal());
}
$jq(".plus").click(function() {
var block = $jq(this).parent().next();
if(block.css("display") == "none") {
block.show();
$jq(this).html("-");
} else {
block.hide();
$jq(this).html("+");
}
});
});

View file

@ -0,0 +1,37 @@
// Drop down link menu
var down_arrow_symbol = "&#9660;";
var up_arrow_symbol = "&#9650;";
var $jq = jQuery.noConflict();
var LinkMenu = function(title) {
this.linkURLs = [];
this.linkTitles = [];
this.buttonTitle = title;
$jq("div#verytopbar").append("<a href=\"#\" style=\"float:right;\" class=\"dropdown-button\" id=\""+title.toLowerCase()+"\">"+title+"</a>");
this.button_jq = $jq("a#"+title.);
this.button_jq.click(function(){
$jq(document.body).append("<div id="+title.toLowerCase())
})
}
LinkMenu.prototype.open = function() {
}
LinkMenu.prototype.close = function() {
}
LinkMenu.prototype.isOpen = function() {
}
LinkMenu.prototype.addLink = function() {
}
LinkMenu.prototype.buildHTML = function() {
}

25
html/linkmenu.html Normal file
View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Link menu test</title>
<script type="text/javascript" src="javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="javascript/linkmenu.js"></script>
<link rel="stylesheet" type="text/css" href="css/burichan/img.css"></link>
<script type="text/javascript">
var $jq = jQuery.noConflict();
var mod_menu;
$jq(document).ready(function(){
mod_menu = new LinkMenu("Manage")
})
</script>
</head>
<body>
<div id="verytopbar"><a href="#">/test/</a> | <a href="#">/test/</a></div>
<br />
<div id="main">
bleh
</div>
</body>
</html>

View file

@ -0,0 +1 @@
Board page

View file

@ -6,8 +6,8 @@
<body>
<h1>Upload file</h1>
<form action="/upload" method="POST" id="uploadform" enctype="multipart/form-data">
<input type="file" id="fileinput" multiple="true" name="file">
<form action="/post" method="POST" id="uploadform" enctype="multipart/form-data">
<input type="file" id="file" multiple="false" name="file">
<input type="submit" id="filesubmit" value="Upload">
</form>

View file

@ -1,2 +1,266 @@
-- Initial setup file for Gochan
-- Deleted after setup is finished
-- Deleted after setup is finished
CREATE DATABASE `DBNAME`;
USE `DBNAME`;
CREATE TABLE `DBPREFIXannouncements` (
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`subject` VARCHAR(45) NOT NULL,
`message` TEXT NOT NULL,
`poster` VARCHAR(45) NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXbanlist` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`expired` TINYINT(1) DEFAULT '0',
`allow_read` TINYINT(1) DEFAULT '1',
`ip` CHAR(15) NOT NULL,
`silent_ban` TINYINT(1) DEFAULT '0',
`boards` VARCHAR(255) NOT NULL,
`banned_by` VARCHAR(50) NOT NULL,
`timestamp` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`expires` TIMESTAMP NOT NULL,
`reason` VARCHAR(255) NOT NULL,
`staff_note` VARCHAR(255) NOT NULL,
`appeal` VARCHAR(255) NOT NULL,
`appeal_at` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXbannedhashes` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`checksum` VARCHAR(45) NOT NULL,
`description` VARCHAR(45) NOT NULL,
PRIMARY KEY(`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXbannedtripcodes` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`tripcode` CHAR(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXboards` (
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`order` TINYINT UNSIGNED NOT NULL DEFAULT 0,
`dir` VARCHAR(45) NOT NULL,
`type` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`first_post` INT UNSIGNED NOT NULL DEFAULT 1,
`upload_type` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`title` VARCHAR(45) NOT NULL,
`subtitle` VARCHAR(64) NOT NULL,
`section` VARCHAR(45) NOT NULL,
`max_image_size` INT UNSIGNED NOT NULL DEFAULT 4718592,
`max_pages` TINYINT UNSIGNED NOT NULL DEFAULT 11,
`locale` VARCHAR(10) NOT NULL DEFAULT 'en-us',
`default_style` VARCHAR(45) NOT NULL,
`locked` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`created_on` TIMESTAMP NOT NULL,
`anonymous` VARCHAR(15) NOT NULL DEFAULT 'Anonymous',
`forced_anon` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`max_age` INT(20) UNSIGNED NOT NULL DEFAULT 0,
`mark_page` TINYINT UNSIGNED NOT NULL DEFAULT 9,
`autosage_after` INT(5) UNSIGNED NOT NULL DEFAULT 200,
`no_images_after` INT(5) UNSIGNED NOT NULL,
`max_message_length` INT(10) UNSIGNED NOT NULL DEFAULT 8192,
`embeds_allowed` VARCHAR(45) NOT NULL,
`redirect_to_thread` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`show_id` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`compact_list` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`enable_nofile` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`enable_catalog` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXsections` (
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`order` TINYINT UNSIGNED NOT NULL DEFAULT 0,
`hidden` TINYINT(1) UNSIGNED NOT NULL,
`name` VARCHAR(45) NOT NULL,
`abbreviation` VARCHAR(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXembeds` (
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`filetype` CHAR(3) NOT NULL,
`name` VARCHAR(45) NOT NULL,
`video_url` VARCHAR(255) NOT NULL,
`width` SMALLINT UNSIGNED NOT NULL,
`height` SMALLINT UNSIGNED NOT NULL,
`embed_code` TEXT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXfiletypes` (
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`filetype` VARCHAR(10) NOT NULL,
`mime` VARCHAR(45) NOT NULL,
`thumb_image` VARCHAR(255) NOT NULL,
`image_w` INT UNSIGNED NOT NULL DEFAULT 0,
`image_h` INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXfrontpage` (
`id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`page` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
`order` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
`subject` VARCHAR(140) NOT NULL,
`message` TEXT NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`poster` VARCHAR(45) NOT NULL,
`email` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXlinks` (
`id` TINYINT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(45) NOT NULL,
`url` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXloginattempts` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`ip` CHAR(10) NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXmodlog` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`entry` TEXT NOT NULL,
`user` VARCHAR(45) NOT NULL,
`category` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXpollresults` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`ip` CHAR(10) NOT NULL,
`selection` VARCHAR(62) NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXposts` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`boardid` TINYINT(3) UNSIGNED NOT NULL,
`parentid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`name` VARCHAR(45) NOT NULL,
`tripcode` CHAR(10) NOT NULL,
`email` VARCHAR(45) NOT NULL,
`subject` VARCHAR(64) NOT NULL,
`message` TEXT NOT NULL,
`password` VARCHAR(45) NOT NULL,
`filename` VARCHAR(45) NOT NULL,
`filename_original` VARCHAR(45) NOT NULL,
`file_checksum` VARCHAR(45) NOT NULL,
`filesize` INT(20) UNSIGNED NOT NULL DEFAULT '0',
`image_w` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`image_h` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`thumb_w` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`thumb_h` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`ip` CHAR(10) NOT NULL,
`tag` VARCHAR(5) NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`autosage` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`poster_authority` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`deleted_timestamp` TIMESTAMP NULL DEFAULT NULL,
`bumped` TIMESTAMP NULL DEFAULT NULL,
`stickied` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`locked` TINYINT(1) NOT NULL DEFAULT '0',
`reviewed` TINYINT(1) NOT NULL DEFAULT '0',
`sillytag` VARCHAR(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `parentid` (`parentid`),
KEY `bumped` (`bumped`),
KEY `file_checksum` (`file_checksum`),
KEY `stickied` (`stickied`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `DBPREFIXtempposts` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`boardid` SMALLINT(5) UNSIGNED NOT NULL,
`parentid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`name` VARCHAR(45) NOT NULL,
`tripcode` CHAR(10) NOT NULL,
`email` VARCHAR(45) NOT NULL,
`subject` VARCHAR(64) NOT NULL,
`message` TEXT NOT NULL,
`password` VARCHAR(45) NOT NULL,
`filename` VARCHAR(45) NOT NULL,
`filename_original` VARCHAR(45) NOT NULL,
`file_checksum` VARCHAR(45) NOT NULL,
`filesize` INT(20) UNSIGNED NOT NULL DEFAULT '0',
`image_w` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`image_h` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`thumb_w` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`thumb_h` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
`ip` CHAR(10) NOT NULL,
`tag` VARCHAR(5) NOT NULL,
`TIMESTAMP` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`autosage` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`poster_authority` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`deleted_timestamp` TIMESTAMP NULL DEFAULT NULL,
`bumped` TIMESTAMP NULL DEFAULT NULL,
`stickied` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`locked` TINYINT(1) NOT NULL DEFAULT '0',
`reviewed` TINYINT(1) NOT NULL DEFAULT '0',
`sillytag` VARCHAR(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `parentid` (`parentid`),
KEY `bumped` (`bumped`),
KEY `file_checksum` (`file_checksum`),
KEY `stickied` (`stickied`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE `DBPREFIXreports` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`board` VARCHAR(45) NOT NULL,
`postid` INT(10) UNSIGNED NOT NULL,
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ip` CHAR(15) NOT NULL,
`reason` VARCHAR(255) NOT NULL,
`cleared` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
`istemp` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXsessions` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`key` CHAR(10) NOT NULL,
`data` VARCHAR(45) NOT NULL,
`expires` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXstaff` (
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`password_checksum` VARCHAR(120) NOT NULL,
`salt` CHAR(3) NOT NULL,
`rank` TINYINT(1) UNSIGNED NOT NULL,
`boards` VARCHAR(128) NOT NULL DEFAULT 'all',
`added_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_active` TIMESTAMP NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `DBPREFIXwordfilters` (
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`from` VARCHAR(75) NOT NULL,
`to` VARCHAR(75) NOT NULL,
`boards` TEXT NOT NULL,
`regex` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- Create admin account so we can log in
-- INSERT INTO `DBPREFIXstaff` (`username`,`password_checksum`,`salt`,`rank`) VALUES ('admin','24326124313024434b7a4f6267504a6631716b464e2f41353457785465766c484f4f766a387843415778696c507a6358504d574a3357794574395975','abc',3);

View file

@ -1,11 +1,13 @@
X Make sure error codes and caching work correctly
- Clean up library dependencies
- Set up daemonization
+ Clean up config file template
+ Clean up server.go
- Set up load balancing
- Clean up config file template
- Set up HTTPS for management
- gzip-encoding for html, css, and javascript
- Set up backend mode, compatibility with nginx and Apache
- Set up templating
- Set up timezone adjusting
- Set up new mod menu style, integrated with board pages
- Set up fallback mod menu (like Kusaba)
- Give administrator server control options (restart/shutdown daemon, etc)
- Give administrator server control options (restart/shutdown daemon, etc)
- Set up basic posting/board interaction
- Set up thumbnailing/image uploading
- find out what changes were made in Kusaba 0.9.3 to fix XSS vulnerability

5
src/boards.go Normal file
View file

@ -0,0 +1,5 @@
package main
func makeImageBoard(board BoardsTable) error {
return nil
}

16
src/geoip.go Normal file
View file

@ -0,0 +1,16 @@
package main
import (
"github.com/nranchev/go-libGeoIP"
)
func getCountryCode(ip string) (string,error) {
if config.EnableGeoIP && config.GeoIPDBlocation != "" {
gi, err := libgeo.Load(config.GeoIPDBlocation)
if err != nil {
return "",err
}
return gi.GetLocationByIP(ip).CountryCode,nil
}
return "",nil
}

View file

@ -7,23 +7,27 @@ import (
)
var (
pid, piderr uintptr
version = 0.1
err error
version float32 = 0.2
)
func main() {
//modlogentries := []ModLogEntry
//posts := []Post
_,err = os.Stat("initialsetupdb.sql")
initConfig()
fmt.Println("Config file loaded. Connecting to database...")
_,err := os.Stat("initialsetupdb.sql")
//check if initialsetup file exists
if err != nil {
needs_initial_setup = false
connectToSQLServer(true)
} else {
needs_initial_setup = true
runInitialSetup()
}
fmt.Println("Connecting to database...(no, not really)")
//connectToDB()
//dbTests()
fmt.Println("Loading and parsing templates...")
initTemplates()
fmt.Println("Initializing server...")
go initServer()
select {}

45
src/initialsetup.go Normal file
View file

@ -0,0 +1,45 @@
// functions for setting up SQL tables and the base administrator account
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func runInitialSetup() {
if !db_connected {
connectToSQLServer(false)
}
loadInitialSetupFile()
}
func loadInitialSetupFile() {
initial_sql_bytes,err := ioutil.ReadFile("initialsetupdb.sql")
initial_sql_str := string(initial_sql_bytes)
fmt.Println("Starting initial setup...")
if err == nil {
initial_sql_str = strings.Replace(initial_sql_str,"DBNAME",config.DBname, -1)
initial_sql_str = strings.Replace(initial_sql_str,"DBPREFIX",config.DBprefix, -1)
initial_sql_str += "\nINSERT INTO `"+config.DBname+"`.`"+config.DBprefix+"staff` (`username`, `password_checksum`, `salt`, `rank`) VALUES ('admin', '"+bcrypt_sum("password")+"', 'abc', 3);"
_,err := db.Start(initial_sql_str)
if err != nil {
fmt.Println("Initial setup failed.")
error_log.Write(err.Error())
} else {
/*_,err := db.Start("INSERT INTO `"+config.DBname+"`.`"+config.DBprefix+"_staff` (`username`, `password_checksum`, `salt`, `rank`) VALUES ('admin', '"+bcrypt_sum("password")+"', 'abc', 3);")
if err != nil {
fmt.Println("Failed creating administrator account.")
error_log.Write(err.Error())
} else {*/
fmt.Println("Initial setup complete.")
}
} else {
error_log.Write("Couldn't load initial sql file")
os.Exit(2)
}
}

247
src/lib/resize/resize.go Normal file
View file

@ -0,0 +1,247 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package resize
import (
"image"
"image/color"
)
// Resize returns a scaled copy of the image slice r of m.
// The returned image has width w and height h.
func Resize(m image.Image, r image.Rectangle, w, h int) image.Image {
if w < 0 || h < 0 {
return nil
}
if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
return image.NewRGBA64(image.Rect(0, 0, w, h))
}
switch m := m.(type) {
case *image.RGBA:
return resizeRGBA(m, r, w, h)
case *image.YCbCr:
if m, ok := resizeYCbCr(m, r, w, h); ok {
return m
}
}
ww, hh := uint64(w), uint64(h)
dx, dy := uint64(r.Dx()), uint64(r.Dy())
// The scaling algorithm is to nearest-neighbor magnify the dx * dy source
// to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate
// image back down to a ww * hh destination with a simple box filter.
// The intermediate image is implied, we do not physically allocate a slice
// of length ww*dx*hh*dy.
// For example, consider a 4*3 source image. Label its pixels from a-l:
// abcd
// efgh
// ijkl
// To resize this to a 3*2 destination image, the intermediate is 12*6.
// Whitespace has been added to delineate the destination pixels:
// aaab bbcc cddd
// aaab bbcc cddd
// eeef ffgg ghhh
//
// eeef ffgg ghhh
// iiij jjkk klll
// iiij jjkk klll
// Thus, the 'b' source pixel contributes one third of its value to the
// (0, 0) destination pixel and two thirds to (1, 0).
// The implementation is a two-step process. First, the source pixels are
// iterated over and each source pixel's contribution to 1 or more
// destination pixels are summed. Second, the sums are divided by a scaling
// factor to yield the destination pixels.
// TODO: By interleaving the two steps, instead of doing all of
// step 1 first and all of step 2 second, we could allocate a smaller sum
// slice of length 4*w*2 instead of 4*w*h, although the resultant code
// would become more complicated.
n, sum := dx*dy, make([]uint64, 4*w*h)
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; x < r.Max.X; x++ {
// Get the source pixel.
r32, g32, b32, a32 := m.At(x, y).RGBA()
r64 := uint64(r32)
g64 := uint64(g32)
b64 := uint64(b32)
a64 := uint64(a32)
// Spread the source pixel over 1 or more destination rows.
py := uint64(y) * hh
for remy := hh; remy > 0; {
qy := dy - (py % dy)
if qy > remy {
qy = remy
}
// Spread the source pixel over 1 or more destination columns.
px := uint64(x) * ww
index := 4 * ((py/dy)*ww + (px / dx))
for remx := ww; remx > 0; {
qx := dx - (px % dx)
if qx > remx {
qx = remx
}
sum[index+0] += r64 * qx * qy
sum[index+1] += g64 * qx * qy
sum[index+2] += b64 * qx * qy
sum[index+3] += a64 * qx * qy
index += 4
px += qx
remx -= qx
}
py += qy
remy -= qy
}
}
}
return average(sum, w, h, n*0x0101)
}
// average convert the sums to averages and returns the result.
func average(sum []uint64, w, h int, n uint64) image.Image {
ret := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
index := 4 * (y*w + x)
ret.SetRGBA(x, y, color.RGBA{
uint8(sum[index+0] / n),
uint8(sum[index+1] / n),
uint8(sum[index+2] / n),
uint8(sum[index+3] / n),
})
}
}
return ret
}
// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m.
// The returned image has width w and height h.
func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) {
var verticalRes int
switch m.SubsampleRatio {
case image.YCbCrSubsampleRatio420:
verticalRes = 2
case image.YCbCrSubsampleRatio422:
verticalRes = 1
default:
return nil, false
}
ww, hh := uint64(w), uint64(h)
dx, dy := uint64(r.Dx()), uint64(r.Dy())
// See comment in Resize.
n, sum := dx*dy, make([]uint64, 4*w*h)
for y := r.Min.Y; y < r.Max.Y; y++ {
Y := m.Y[y*m.YStride:]
Cb := m.Cb[y/verticalRes*m.CStride:]
Cr := m.Cr[y/verticalRes*m.CStride:]
for x := r.Min.X; x < r.Max.X; x++ {
// Get the source pixel.
r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2])
r64 := uint64(r8)
g64 := uint64(g8)
b64 := uint64(b8)
// Spread the source pixel over 1 or more destination rows.
py := uint64(y) * hh
for remy := hh; remy > 0; {
qy := dy - (py % dy)
if qy > remy {
qy = remy
}
// Spread the source pixel over 1 or more destination columns.
px := uint64(x) * ww
index := 4 * ((py/dy)*ww + (px / dx))
for remx := ww; remx > 0; {
qx := dx - (px % dx)
if qx > remx {
qx = remx
}
qxy := qx * qy
sum[index+0] += r64 * qxy
sum[index+1] += g64 * qxy
sum[index+2] += b64 * qxy
sum[index+3] += 0xFFFF * qxy
index += 4
px += qx
remx -= qx
}
py += qy
remy -= qy
}
}
}
return average(sum, w, h, n), true
}
// resizeRGBA returns a scaled copy of the RGBA image slice r of m.
// The returned image has width w and height h.
func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image {
ww, hh := uint64(w), uint64(h)
dx, dy := uint64(r.Dx()), uint64(r.Dy())
// See comment in Resize.
n, sum := dx*dy, make([]uint64, 4*w*h)
for y := r.Min.Y; y < r.Max.Y; y++ {
pixOffset := m.PixOffset(r.Min.X, y)
for x := r.Min.X; x < r.Max.X; x++ {
// Get the source pixel.
r64 := uint64(m.Pix[pixOffset+0])
g64 := uint64(m.Pix[pixOffset+1])
b64 := uint64(m.Pix[pixOffset+2])
a64 := uint64(m.Pix[pixOffset+3])
pixOffset += 4
// Spread the source pixel over 1 or more destination rows.
py := uint64(y) * hh
for remy := hh; remy > 0; {
qy := dy - (py % dy)
if qy > remy {
qy = remy
}
// Spread the source pixel over 1 or more destination columns.
px := uint64(x) * ww
index := 4 * ((py/dy)*ww + (px / dx))
for remx := ww; remx > 0; {
qx := dx - (px % dx)
if qx > remx {
qx = remx
}
qxy := qx * qy
sum[index+0] += r64 * qxy
sum[index+1] += g64 * qxy
sum[index+2] += b64 * qxy
sum[index+3] += a64 * qxy
index += 4
px += qx
remx -= qx
}
py += qy
remy -= qy
}
}
}
return average(sum, w, h, n)
}
// Resample returns a resampled copy of the image slice r of m.
// The returned image has width w and height h.
func Resample(m image.Image, r image.Rectangle, w, h int) image.Image {
if w < 0 || h < 0 {
return nil
}
if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
return image.NewRGBA64(image.Rect(0, 0, w, h))
}
curw, curh := r.Dx(), r.Dy()
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
// Get a source pixel.
subx := x * curw / w
suby := y * curh / h
r32, g32, b32, a32 := m.At(subx, suby).RGBA()
r := uint8(r32 >> 8)
g := uint8(g32 >> 8)
b := uint8(b32 >> 8)
a := uint8(a32 >> 8)
img.SetRGBA(x, y, color.RGBA{r, g, b, a})
}
}
return img
}

View file

@ -3,82 +3,223 @@ package main
import (
"net/http"
"fmt"
"io/ioutil"
"os"
"strings"
"strconv"
_ "code.google.com/p/go.crypto/bcrypt"
)
type ManageFunction struct {
Permissions int // 0 -> non-staff, 1 => janitor, 2 => moderator, 3 => administrator
Callback func() string //return string of html output?
Callback func() string //return string of html output
}
func callManageFunction(w http.ResponseWriter, request *http.Request) int {
func callManageFunction(w http.ResponseWriter, r *http.Request) {
// check if we have sufficient permissions to run this function
//return values: 0 if successful, 1 if insufficient privelages
form := request.Form
request = *r
writer = w
cookies = r.Cookies()
request.ParseForm()
form := r.Form
action := form.Get("action")
if action == "" {
staff_rank := getStaffRank()
//writer.Header().Set("CContent-Type", "text/html")
manage_page_html := ""
//manage_page_html = strings.Replace(manage_page_html,"{link css}",getStyleLinks(w,"manage"),-1)
//getStyleLinks(w,"manage")
if action == "" {
action = "announcements"
}
if staff_rank == 0 {
action = "login"
}
global_header,err := getTemplateAsString(*global_header_tmpl)
if err != nil {
fmt.Fprintf(writer,err.Error())
} else {
fmt.Fprintf(writer,global_header)
}
manage_header,err := getTemplateAsString(*manage_header_tmpl)
if err != nil {
fmt.Fprintf(writer,err.Error())
} else {
fmt.Fprintf(writer,manage_header)
}
if _,ok := manage_functions[action]; ok {
var manage_page_html = ""
if getStaffRank() >= manage_functions[action].Permissions {
global_header,_ := readFileToString("templates/global_header.html")
manage_header,_ := readFileToString("templates/manage_header.html")
global_footer,_ := readFileToString("templates/global_footer.html")
manage_page_html = global_header +"\n"+ manage_header
manage_page_html = strings.Replace(manage_page_html,"{link css}",getStyleLinks("manage"),-1)
manage_page_html = manage_page_html + manage_functions[action].Callback()+global_footer
fmt.Fprintf(w,manage_page_html)
return 0
if staff_rank >= manage_functions[action].Permissions {
manage_page_html += manage_functions[action].Callback()
fmt.Fprintf(writer,manage_page_html)
} else {
manage_page_html = manage_page_html + action + " is undefined."
fmt.Fprintf(writer,manage_page_html)
}
} else {
var manage_page_html = ""
global_header,_ := readFileToString("templates/global_header.html")
manage_header,_ := readFileToString("templates/manage_header.html")
global_footer,_ := readFileToString("templates/global_footer.html")
manage_page_html = global_header +"\n"+ manage_header
manage_page_html = strings.Replace(manage_page_html,"{link css}",getStyleLinks("manage"),-1)
manage_page_html = manage_page_html + action + " is undefined." + global_footer
fmt.Fprintf(w,manage_page_html)
return 0
manage_page_html = manage_page_html + action + " is undefined."
fmt.Fprintf(writer,manage_page_html)
}
global_footer,err := getTemplateAsString(*global_footer_tmpl)
if err != nil {
fmt.Fprintf(writer,err.Error())
} else {
fmt.Fprintf(writer,global_footer)
}
return 1
}
func getStaffRank() int {
return 3
var key string
var staffname string
db.Start("USE `"+config.DBname+"`")
session_cookie := getCookie("sessiondata")
if session_cookie == nil {
return 0
} else {
key = session_cookie.Value
}
results,err := db.Start("SELECT * FROM `"+config.DBprefix+"sessions` WHERE `key` = '"+key+"';")
if err != nil {
error_log.Write(err.Error())
return 0
}
for {
row, err := results.GetRow()
if err != nil {
error_log.Write(err.Error())
}
if row == nil {
break
}
for col_num, col := range row {
if col_num == 2 {
staffname = string(col.([]byte))
}
}
}
results,err = db.Start("SELECT * FROM `"+config.DBprefix+"staff` WHERE `username` = '"+staffname+"';")
if err != nil {
error_log.Write(err.Error())
return 0
}
for {
row, err := results.GetRow()
if err != nil {
error_log.Write(err.Error())
return 0
}
if row == nil {
break
}
for col_num, col := range row {
if col_num == 4 {
rank,rerr := strconv.Atoi(string(col.([]byte)))
if rerr == nil {
return rank
} else {
return 0
}
}
}
}
return 0
}
func createSession(key string,username string, password string) bool {
//sum := bcrypt_sum(password)
rows,_,err := db.Query("SELECT `password_checksum` FROM `"+config.DBprefix+"staff`")
if err != nil {
error_log.Write(err.Error())
fmt.Println("nope 1")
return false
} else {
if len(rows) > 0 {
_,err := db.Start(" INSERT INTO `"+config.DBprefix+"sessions` (`key`, `data`, `expires`) VALUES('"+key+"','"+username+"', '2023-17-04 16:21:01');")
if err != nil {
fmt.Println("Initial setup failed.")
error_log.Write(err.Error())
}
} else {
fmt.Println("nope 2")
return false
}
}
fmt.Println("wtf")
return false
}
var manage_functions = map[string]ManageFunction{
"initialsetup": {
Permissions: 0,
Callback: func() string {
html,_ := ioutil.ReadFile(config.DocumentRoot+"/index.html")
return string(html)
}},
"error": {
Permissions: 0,
Callback: func() (html string) {
html,err = readFileToString(document_root+"/index.html")
exitWithErrorPage("lel, internet")
return
}},
"login":{
Permissions: 0,
Callback: func() (html string) {
html = "<div id=\"loginbox\">" +
"\t<form method=\"GET\" action=\"/manage\">\n" +
"\t\t<input type=\"hidden\" name=\"action\" value=\"login\" />\n" +
"\t\t<input type=\"text\" name=\"username\" /><br />\n" +
"\t\t<input type=\"password\" name=\"password\" /> <br />\n" +
"\t\t<input type=\"submit\" value=\"Login\" />\n" +
"\t</form>" +
"</div>"
username := request.FormValue("username")
password := request.FormValue("password")
if username == "" || password == "" {
//assume that they haven't logged in
html = "\t<form method=\"POST\" action=\"/manage?action=login\" class=\"loginbox\">\n" +
//"\t\t<input type=\"hidden\" name=\"action\" value=\"login\" />\n" +
"\t\t<input type=\"text\" name=\"username\" class=\"logindata\" /><br />\n" +
"\t\t<input type=\"password\" name=\"password\" class=\"logindata\" /> <br />\n" +
"\t\t<input type=\"submit\" value=\"Login\" />\n" +
"\t</form>"
} else {
key := md5_sum(request.RemoteAddr+username+password+config.RandomSeed+generateSalt())
createSession(key,username,password)
//check db for valid login
/*
password_bcrypt = bcrypt_encode(password)
results,err := db.Query("SELECT `username`,`password`, FROM `"+config.DBprefix+"staff")
if err != nil {
error_log.Write(err.Error())
}
var entry StaffTable
for results.Next() {
err = results.Scan(&entry.username,&entry.password)
if entry.username == username && entry.password == password_bcrypt {
//authenticated
}
if err != nil { error_log.write(err.Error()) }
}
*/
}
return
}},
"announcements": {
Permissions: 1,
Callback: func() (html string) {
html = "Announcements will eventually go here."
html = "<h2>Announcements</h2><br />" +
"Announcements will eventually go here."
/*results,err := db.Query("SELECT * FROM `"+db_prefix+"announcements")
if err != nil {
@ -92,23 +233,30 @@ var manage_functions = map[string]ManageFunction{
return
}},
"manageserver": {
Permissions: 0,
Permissions: 3,
Callback: func() (html string) {
html = "<script type=\"text/javascript\">\n$jq = jQuery.noConflict();\n$jq(document).ready(function() {\n\tvar killserver_btn = $jq(\"button#killserver\");\n\n\t$jq(\"button#killserver\").click(function() {\n\t\t$jq.ajax({\n\t\t\tmethod:'GET',\n\t\t\turl:\"/manage\",\n\t\t\tdata: {\n\t\t\t\taction: 'killserver'\n\t\t\t},\n\n\t\t\tsuccess: function() {\n\t\t\t\t\n\t\t\t},\n\t\t\terror:function() {\n\t\t\t\t\n\t\t\t}\n\t\t});\n\t});\n});\n</script>" +
"<button id=\"killserver\">Kill server</button><br />\n"
return
}},
"rebuildall": {
"cleanup": {
Permissions:3,
Callback: func() (html string) {
return
}},
"rebuildall": {
Permissions:3,
Callback: func() (html string) {
initTemplates()
return
}},
"recentposts": {
Permissions:1,
Callback: func() (html string) {
html = "<h1>Recent posts</h1>\n<table>\n<tr></tr> "
html = "<h1>Recent posts</h1>\n<table style=\"border:2px solid;\">\n<tr><td>bleh</td><td>bleh bleh</td></tr>" +
"</table>"
return
}},
"killserver": {
@ -116,4 +264,5 @@ var manage_functions = map[string]ManageFunction{
Callback: func() (html string) {
os.Exit(0)
return
}}}
}},
}

136
src/posting.go Normal file
View file

@ -0,0 +1,136 @@
package main
import (
"net/http"
"io/ioutil"
"fmt"
"image"
"image/jpeg"
"image/gif"
"image/png"
"os"
"./lib/resize"
"syscall"
)
func generateTripCode(input string) string {
input += " " //padding
return crypt(input,input[1:3])[3:]
}
func createThumbnail(input string, output string) bool {
var image_obj image.Image
failed := false
handle,err := os.Open(input)
if err != nil {
return false
}
defer func() {
if _, ok := recover().(error); ok {
handle.Close()
failed = true
}
}()
if failed {
error_log.Write("Failed to create thumbnail")
return false
}
filetype := input[len(input)-3:len(input)]
if filetype == "gif" {
image_obj,_ = gif.Decode(handle)
} else if filetype == "jpeg" || filetype == "jpg" {
image_obj,_ = jpeg.Decode(handle)
} else if filetype == "png" {
image_obj,_ = png.Decode(handle)
} else {
exitWithErrorPage("Upload file type not supported")
}
old_rect := image_obj.Bounds()
defer func() {
if _, ok := recover().(error); ok {
//serverError()
exitWithErrorPage("lel, internet")
}
}()
if config.ThumbWidth >= old_rect.Max.X && config.ThumbHeight >= old_rect.Max.Y {
err := syscall.Symlink(input,output)
if err != nil {
error_log.Write(fmt.Sprintf("Error, couldn't create symlink to %s, %s", input, err.Error()))
return false
} else {
return true
}
}
thumb_w,thumb_h := getThumbnailSize(old_rect.Max.X,old_rect.Max.Y)
image_obj = resize.Resize(image_obj, image.Rect(0,0,old_rect.Max.X,old_rect.Max.Y), thumb_w,thumb_h)
outwriter,_ := os.OpenFile(output, os.O_RDWR|os.O_CREATE,0777)
if filetype == "gif" {
//because Go doesn't come with a GIF writer :c
jpeg.Encode(outwriter, image_obj, &jpeg.Options{Quality: 80})
} else if filetype == "jpg" || filetype == "jpeg" {
jpeg.Encode(outwriter,image_obj, &jpeg.Options{Quality: 80})
} else if filetype == "png" {
png.Encode(outwriter,image_obj)
}
return false
}
//find out what out thumbnail's width and height should be, partially ripped from Kusaba X
func getThumbnailSize(w int, h int) (new_w int, new_h int) {
if w == h {
new_w = config.ThumbWidth
new_h = config.ThumbWidth
} else {
var percent float32
if (w > h) {
percent = float32(config.ThumbWidth) / float32(w)
} else {
percent = float32(config.ThumbWidth) / float32(h)
}
new_w = int(float32(w) * percent)
new_h = int(float32(h) * percent)
//fmt.Printf("Old width: %d\nOld height: %d\nPercent: %f\nWidth: %d\nHeight: %d\n",w,h,percent*100,new_w,new_h)
}
return
}
func shortenPostForBoardPage(post *string) {
}
func makePost(w http.ResponseWriter, r *http.Request) {
request = *r
writer = w
file,handler,err := request.FormFile("file")
//post has no referrer, or has a referrer from a different domain, probably a spambot
if request.Referer() == "" || request.Referer()[7:len(config.Domain)+7] != config.Domain {
access_log.Write("Rejected post from possible spambot @ : "+request.RemoteAddr)
//TODO: insert post into temporary post table and add to report list
}
//no file was uploaded
if err != nil {
access_log.Write("Receiving post from "+request.RemoteAddr+", referred from: "+request.Referer())
} else {
data,err := ioutil.ReadAll(file)
if err != nil {
fmt.Println("Couldn't read file")
} else {
access_log.Write("Receiving post with image: "+handler.Filename+" from "+request.RemoteAddr+", referrer: "+request.Referer())
err = ioutil.WriteFile(handler.Filename, data, 0777)
createThumbnail(handler.Filename,"output")
if err != nil {
fmt.Println("Couldn't write file")
}
}
}
}

17
src/process.go Normal file
View file

@ -0,0 +1,17 @@
package main
// #define _GNU_SOURCE
// #include <stdio.h>
// #include <unistd.h>
// #include <sys/types.h>
import "C"
var pid uintptr
/*
func fork() int {
return C.GoInt(C.fork())
}
func setsid() {
C.setsid()
}*/

View file

@ -1,86 +1,128 @@
package main
import (
"os"
"fmt"
"strconv"
"path"
"io/ioutil"
"net"
"net/url"
"net/http"
//"html/template"
"net/url"
"os"
"path"
"strconv"
"strings"
)
var (
form url.Values
header http.Header
cookies []*http.Cookie
writer http.ResponseWriter
request http.Request
exit_error bool
)
func initServer() {
if port == 0 {
port = 80
if config.Port == 0 {
config.Port = 80
}
getStyleLinks("manage")
listener,err := net.Listen("tcp", domain+":"+strconv.Itoa(port))
listener,err := net.Listen("tcp", config.Domain+":"+strconv.Itoa(config.Port))
if(err != nil) {
error_log.Write(err.Error())
fmt.Println("Failed listening on "+domain+":"+strconv.Itoa(port)+", see log for details")
fmt.Printf("Failed listening on "+config.Domain+":%d, see log for details",config.Port)
os.Exit(2)
}
http.Handle("/", makeHandler(serveFile))
http.Handle("/", makeHandler(fileHandle))
http.Handle("/manage",makeHandler(callManageFunction))
http.Handle("/post",makeHandler(makePost))
//http.Handle("/util",makeHandler(utilHandler))
http.Serve(listener, nil)
}
func getFileHTTPCode(filename string) int {
filename = document_root+"/"+filename
stat, err := os.Stat(filename);
if err == nil {
return 200
} else {
if stat.IsDir() {
num_indexes := len(first_page)
for i := 0; i < num_indexes; i++ {
_,newerr := os.Stat(filename+"/"+first_page[i])
if newerr == nil {
return 200
} else {
return 404
}
}
} else {
return 200
}
}
return 500
}
func serveFile(w http.ResponseWriter, request *http.Request, request_url string) {
func fileHandle(w http.ResponseWriter, r *http.Request) {
request = *r
writer = w
cookies = request.Cookies()
request.ParseForm()
form = request.Form
request_url := request.URL.Path
filepath := path.Join(config.DocumentRoot, request_url)
results,err := os.Stat(filepath)
if !strings.Contains(request.Header.Get("Accept-Encoding"), "gzip") {
if request.URL.Path == "/manage" {
callManageFunction(w,request)
} else {
http.ServeFile(w, request, path.Join(document_root, request_url))
}
access_log.Write("Success: 200 from " + request.RemoteAddr + " @ " + request.RequestURI)
if err == nil {
//the file exists, or there is a folder here
if results.IsDir() {
found_index := false
newpath := ""
//check to see if one of the specified index pages exists
for i := 0; i < len(config.FirstPage); i++ {
newpath = path.Join(filepath,config.FirstPage[i])
_,err := os.Stat(newpath)
if err == nil {
serveFile(w, newpath)
found_index = true
break
}
}
if !found_index {
error404()
}
} else {
//the file exists, and is not a folder
//writer.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", 500))
serveFile(w, filepath)
}
} else {
//there is nothing at the requested address
error404()
}
}
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, request *http.Request) {
defer func() {
if _, ok := recover().(error); ok {
//don't panic if the file doesn't exist
//w.WriteHeader(404)
http.ServeFile(w, request, path.Join(document_root, "404.html"))
error_log.Write("Error: 404 Not Found from " + request.RemoteAddr + " @ " + request.RequestURI)
return
}
}()
title := request.URL.Path
fn(w, request, title)
func makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
//defer serverError()
if !exit_error {
fn(w, r)
exit_error = false
} else {
exit_error = false
}
}
}
func exitWithErrorPage(err string) {
error_page_bytes,_ := ioutil.ReadFile("templates/error.html")
error_page := string(error_page_bytes)
error_page = strings.Replace(error_page,"{ERRORTEXT}", err,-1)
fmt.Fprintf(writer,error_page)
exit_error = true
}
func redirect(location string) {
//http.Redirect(writer,&request,location,http.StatusMovedTemporarily)
}
func error404() {
http.ServeFile(writer, &request, path.Join(config.DocumentRoot, "/error/404.html"))
error_log.Write("Error: 404 Not Found from " + request.RemoteAddr + " @ " + request.RequestURI)
}
func serverError() {
if _, ok := recover().(error); ok {
//something went wrong, now we need to throw a 500
http.ServeFile(writer,&request, path.Join(config.DocumentRoot, "/error/500.html"))
error_log.Write("Error: 500 Internal Server error from " + request.RemoteAddr + " @ " + request.RequestURI)
return
}
}
func serveFile(w http.ResponseWriter, filepath string) {
http.ServeFile(w, &request, filepath)
access_log.Write("Success: 200 from " + request.RemoteAddr + " @ " + request.RequestURI)
}

107
src/server.go.bak Normal file
View file

@ -0,0 +1,107 @@
package main
import (
"os"
"fmt"
"strconv"
"path"
"net"
"net/url"
"net/http"
//"html/template"
)
var (
form url.Values
header http.Header
cookies []*http.Cookie
)
func initServer() {
if port == 0 {
port = 80
}
getStyleLinks("manage")
listener,err := net.Listen("tcp", domain+":"+strconv.Itoa(port))
if(err != nil) {
error_log.Write(err.Error())
fmt.Printf("Failed listening on "+domain+":%d, see log for details",port)
os.Exit(2)
}
http.Handle("/", makeHandler(fileHandle))
http.Handle("/manage",makeHandler(callManageFunction))
http.Handle("/post",makeHandler(makePost))
http.Serve(listener, nil)
}
func fileHandle(writer http.ResponseWriter, request *http.Request) {
cookies = request.Cookies()
request.ParseForm()
form = request.Form
request_url := request.URL.Path
filepath := path.Join(document_root, request_url)
results,err := os.Stat(filepath)
if err == nil {
//the file exists, or there is a folder here
if results.IsDir() {
found_index := false
newpath := ""
//check to see if one of the specified index pages exists
for i := 0; i < len(first_page); i++ {
newpath = path.Join(filepath,first_page[i])
_,err := os.Stat(newpath)
if err == nil {
serveFile(writer,request,newpath)
found_index = true
break
}
}
if !found_index {
error404(&writer,request)
}
} else {
//the file exists, and is not a folder
writer.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", 500))
serveFile(writer,request,filepath)
}
} else {
//there is nothing at the requested address
error404(&writer, request)
}
}
func makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
defer serverError(writer,request)
fn(writer, request)
}
}
func exitWithErrorPage(error string) {
}
func error404(writer *http.ResponseWriter, request *http.Request) {
//w := *writer
//w.WriteHeader(404)
http.ServeFile(*writer, request, path.Join(document_root, "404.html"))
error_log.Write("Error: 404 Not Found from " + request.RemoteAddr + " @ " + request.RequestURI)
}
func serverError(writer http.ResponseWriter, request *http.Request) {
if _, ok := recover().(error); ok {
//something went wrong, now we need to throw a 500
http.ServeFile(writer,request, path.Join(document_root, "/error/500.html"))
error_log.Write("Error: 500 Internal Server error from " + request.RemoteAddr + " @ " + request.RequestURI)
return
}
}
func serveFile(writer http.ResponseWriter, request *http.Request, filepath string) {
http.ServeFile(writer, request, filepath)
access_log.Write("Success: 200 from " + request.RemoteAddr + " @ " + request.RequestURI)
}

View file

@ -3,33 +3,75 @@ package main
import (
"os"
"fmt"
"database/sql"
_ "go-mysql-driver/mysql"
//"database/sql"
//_ "github.com/go-sql-driver/mysql"
"github.com/ziutek/mymysql/mysql"
_ "github.com/ziutek/mymysql/native" // Native engine
//_ "github.com/ziutek/mymysql/thrsafe" // Thread safe engine
)
var (
db *sql.DB
//db *sql.DB
db mysql.Conn
db_connected = false
)
func connectToDB() {
db, err = sql.Open("mysql",db_username+":"+db_password+"@"+db_host+"/"+db_name+"?charset=utf8&keepalive="+db_persistent_str)
func connectToSQLServer(usedb bool) {
//db, err = sql.Open("mysql",config.DBusername+":"+config.DBpassword+"@"+db_host+"/?charset=utf8")
db = mysql.New("tcp", "", "127.0.0.1:3306", config.DBusername, config.DBpassword)
err := db.Connect()
if err != nil {
error_log.Write(err.Error())
fmt.Println("Failed to connect to the database, see log for details.")
os.Exit(2)
}
if usedb {
_,err = db.Start("USE `"+config.DBname+"`;")
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
}
}
db_connected = true
}
func dbTests() {
results,err := db.Query("SELECT * FROM `"+db_prefix+"modlog")
results,err := db.Start("SELECT * FROM `"+config.DBprefix+"staff")
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
}
/*var entry StaffTable
if err != nil {
error_log.Write(err.Error())
fmt.Println("Failed to connect to the database, see log for details.")
os.Exit(2)
}
var entry ModLogTable
for results.Next() {
err = results.Scan(&entry.entry,&entry.user,&entry.category,&entry.timestamp)
err = results.Scan(&entry.username,&entry.password_checksum,&entry.rank)
//if err != nil { panic(err) }
}*/
for {
row, err := results.GetRow()
if err != nil {
error_log.Write(err.Error())
}
if row == nil {
// No more rows
break
}
// Print all cols
for _, col := range row {
if col == nil {
fmt.Print("<NULL>")
} else {
os.Stdout.Write(col.([]byte))
}
fmt.Print(" ")
}
fmt.Println()
}
}

View file

@ -1,13 +1,134 @@
package main
func getStyleLinks(stylesheet string) (links_str string) {
num_styles := len(styles_arr)
for l := 0; l < num_styles; l++ {
links_str += "<link rel=\""
if l > 0 {
links_str += "alternate "
}
links_str += "stylesheet\" href=\"/css/"+styles_arr[l]+"/"+stylesheet+".css\" />\n"
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"text/template"
)
type FooterData struct {
Version float32
GeneratedTime float32
}
var funcMap = template.FuncMap{
"isStyleDefault_img": func(style string) bool {
return style == config.DefaultStyle_img
},
"isStyleNotDefault_img": func(style string) bool {
return style != config.DefaultStyle_img
},
}
var (
footer_data = FooterData{version, float32(0)}
global_footer_tmpl_str string
global_footer_tmpl *template.Template
global_header_tmpl_str string
global_header_tmpl *template.Template
img_header_tmpl_str string
img_header_tmpl *template.Template
manage_header_tmpl_str string
manage_header_tmpl *template.Template
template_buffer bytes.Buffer
)
func initTemplates() {
global_footer_tmpl_bytes,tmpl_err := ioutil.ReadFile(config.TemplateDir+"/global_footer.html")
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/global_footer.html\"")
os.Exit(2)
}
return
global_footer_tmpl_str = string(global_footer_tmpl_bytes)
global_footer_tmpl,tmpl_err = template.New("global_footer_tmpl").Funcs(funcMap).Parse(string(global_footer_tmpl_str))
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/global_footer.html\"")
os.Exit(2)
}
global_header_tmpl_bytes,tmpl_err := ioutil.ReadFile(config.TemplateDir+"/global_header.html")
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/global_header.html\"")
os.Exit(2)
}
global_header_tmpl_str = string(global_header_tmpl_bytes)
global_header_tmpl,tmpl_err = template.New("global_header_tmpl").Funcs(funcMap).Parse(string(global_header_tmpl_str))
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/global_header.html\"")
os.Exit(2)
}
img_header_tmpl_bytes,_ := ioutil.ReadFile(config.TemplateDir+"/img_header.html")
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/img_header.html\"")
os.Exit(2)
}
img_header_tmpl_str = string(img_header_tmpl_bytes)
img_header_tmpl,_ = template.New("img_header_tmpl").Funcs(funcMap).Parse(string(img_header_tmpl_str))
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/img_header.html\"")
os.Exit(2)
}
manage_header_tmpl_bytes,_ := ioutil.ReadFile(config.TemplateDir+"/manage_header.html")
manage_header_tmpl_str = string(manage_header_tmpl_bytes)
manage_header_tmpl,_ = template.New("manage_header_tmpl").Funcs(funcMap).Parse(manage_header_tmpl_str)
if tmpl_err != nil {
fmt.Println("Failed loading template \""+config.TemplateDir+"/manage_header.html\"")
os.Exit(2)
}
}
func getTemplateAsString(templ template.Template) (string,error) {
var buf bytes.Buffer
err := templ.Execute(&buf,config)
if err == nil {
return buf.String(),nil
}
return "",err
}
func getStyleLinks(w http.ResponseWriter, stylesheet string) {
styles_map := make(map[int]string)
for i := 0; i < len(config.Styles_img); i++ {
styles_map[i] = config.Styles_img[i]
}
err := manage_header_tmpl.Execute(w,config)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
}
}
func buildAll() error {
buildFrontPage()
/*
results,err := db.Query("SELECT `dir` FROM `"+config.DBprefix+"boards")
var entry BoardTable
for results.Next() {
err = results.Scan(&entry.dir)
buildBoard(entry.dir)
}
*/
return nil
}
func buildFrontPage() error {
return nil
}
func buildBoard(dir string) error {
//build board pages
//build board thread pages
return nil
}

View file

@ -1,329 +1,665 @@
package main
import (
"strconv"
"fmt"
"os"
"strings"
"go-logfile/logfile"
"goconf/conf"
)
var (
c *conf.ConfigFile
needs_initial_setup = true
config GochanConfig
access_log logfile.Log
error_log logfile.Log
)
// SQL Table structs
type AnnouncementsTable struct {
id uint
subject string
message string
poster string
timestamp string
}
type BanlistTable struct {
id int
Type bool
id uint
expired bool
allowread bool
allow_read bool
ip string
ipmd5 string
globalban bool
silentban bool
silent_ban uint8
boards string
by string
at int
until int
banned_by string
timestamp string
expires string
reason string
staffnote string
staff_note string
appeal string
appealat int
appeal_at string
}
type BannedHashesTable struct {
id int
md5 string
bantime int
id uint
checksum string
description string
}
type BannedTripcodesTable struct {
id int
id uint
name string
tripcode string
}
type BlotterTable struct {
id int
important bool
at int
message string
}
type BoardFiletypesTable struct {
boardid int
typeid int
}
type BoardsTable struct {
id int
order int
name string
Type bool
start int
uploadtype bool
desc string
image string
section int
maximagesize int
maxpages int
maxage int
markpage int
maxreplies int
messagelength int
createdon int
locked bool
includeheader string
redirecttothread bool
anonymous string
forcedanon bool
embeds_allowed string
trial bool
popular bool
defaultstyle string
id uint8
order uint8
dir string
Type uint8
first_post uint
upload_type uint8
title string
subtitle string
section string
max_image_size int
max_pages uint8
locale string
showid bool
compactlist bool
enablereporting bool
enablecaptcha bool
enablenofile bool
enablearchiving bool
enablecatalog bool
loadbalanceurl string
loadbalancepassword string
default_style string
locked bool
created_on string
anonymous string
forced_anon string
max_age uint
mark_page uint8
autosage_after uint
no_images_after uint
max_message_length uint
embeds_allowed string
redirect_to_thread bool
show_id bool
compact_list bool
enable_nofile bool
enable_catalog bool
}
type BoardSectionsTable struct {
id int
order int
id uint8
order uint8
hidden bool
name string
abbreviation string
}
type EmbedsTable struct {
id int
id uint8
filetype string
name string
videourl string
width int
height int
code string
video_url string
width uint16
height uint16
embed_code string
}
type FiletypesTable struct {
id int
id uint8
filetype string
mime string
image string
image_w int
image_h int
force_thumb bool
thumb_image string
image_w uint
image_h uint
}
type FrontTable struct {
id int
page int
order int
id uint16
page uint8
order uint8
subject string
message string
timestamp int
timestamp string
poster string
email string
}
type FrontLinksTable struct {
id uint8
title string
url string
}
type LoginAttemptsTable struct {
username string
id uint
ip string
timestamp int
timestamp string
}
type ModLogTable struct {
id uint
entry string
user string
category int
timestamp int
category uint8
timestamp string
}
type ModpageAnnouncementsTable struct {
id int
parentid int
subject string
postedat int
postedby string
message string
}
type PollTable struct {
type PollResultsTable struct {
id uint
ip string
selection string
time int
timestamp string
}
type PostTable struct {
id int
boardid int
parentid int
id uint
boarid uint8
parentid uint
name string
tripcode string
email string
subject string
message string
password string
file string
file_md5 string
file_type string
file_original string
file_size int
file_size_formatted string
image_w int
image_h int
thumb_w int
thumb_h int
filename string
filename_original string
file_checksum string
filesize string
image_w uint16
image_h uint16
thumb_w uint16
thumb_h uint16
ip string
ipmd5 string
tag string
timestamp int
timestamp string
autosage uint8
poster_authority uint8
deleted_timestamp string
bumped string
stickied bool
locked bool
autosage int
posterauthority int
reviewed bool
deleted_timestamp int
IS_DELETED bool
bumped int
sillytag string
sillytag bool
}
type TempPostTable struct {
id uint
boarid uint8
parentid uint
name string
tripcode string
email string
subject string
message string
password string
filename string
filename_original string
file_checksum string
filesize string
image_w uint16
image_h uint16
thumb_w uint16
thumb_h uint16
ip string
tag string
timestamp string
autosage uint8
poster_authority uint8
deleted_timestamp string
bumped string
stickied bool
locked bool
reviewed bool
sillytag bool
}
type ReportsTable struct {
id int
cleared bool
id uint
board string
postid int
when int
postid uint
timestamp string
ip string
reason string
cleared bool
istemp bool
}
type SessionsTable struct {
id uint
data string
expires string
}
type StaffTable struct {
id int
id uint16
username string
password string
password_checksum string
salt string
Type int
rank uint8
boards string
addedon int
lastactive int
em_contact string
}
type TrackerTable struct {
index int
search_query string
found_names string
found_ips string
}
type WatchedThreadsTable struct {
id int
threadid int
board string
ip string
lastsawreplyid int
addedon string
last_active string
}
type WordFiltersTable struct {
id int
word string
replacedby string
id uint16
from string
to string
boards string
time int
regex bool
}
var (
needs_initial_setup = true
config,_ = conf.ReadConfigFile("config.cfg")
log_dir,_ = config.GetString("server","log_dir")
access_log,_ = logfile.OpenLogFile(log_dir+"/access.log",false)
error_log,_ = logfile.OpenLogFile(log_dir+"/error.log",false)
document_root,_ = config.GetString("server","document_root")
first_page_str,_ = config.GetString("server","first_page")
first_page = strings.Split(first_page_str,",")
domain,_ = config.GetString("server","domain")
port,_ = config.GetInt("server","port")
db_type,_ = config.GetString("database","type")
db_name,_ = config.GetString("database", "name")
db_host,_ = config.GetString("database","host")
db_username,_ = config.GetString("database","username")
db_password,_ = config.GetString("database","password")
db_prefix,_ = config.GetString("database","prefix")
db_persistent,_ = config.GetBool("database","keepalive")
db_persistent_str = strconv.Itoa(Btoi(db_persistent))
lockdown bool
lockdown_message string
sillytags string
use_sillytags bool
site_name string
site_slogan string
site_headerurl bool
site_irc string
site_banreason string
site_allowdupes bool
webfolder string
webpath string
root_dir string
template_dir string
cached_template_dir string
styles,_ = config.GetString("styles","styles")
styles_arr = strings.Split(styles,",")
default_style string
style_switcher bool
dropdown_style_switcher bool
styles_txt []string
default_txt_style string
txt_style_switcher bool
menu_type string
menu_styles []string
default_menu_style string
menu_style_switcher bool
new_thread_delay int
reply_delay int
line_length int
thumb_width int
thumb_height int
reply_thumb_width int
reply_thumb_height int
catalog_thumb_width int
catalog_thumb_height int
thumb_method string
animated_thumbs bool
new_window bool
make_links bool
no_message_thread bool
no_message_reply bool
img_threads_per_page int
txt_threads_per_page int
replies_on_boardpage int
sticky_replies_on_boardpage int
thumb_msg bool
ban_colors []string
ban_msg string
traditional_read bool
youtube_width int
youtube_height int
use_dir_title bool
make_rss bool
expand bool
quick_reply bool
watched_threads bool
gen_firstlast_pages bool
use_blotter bool
make_sitemap bool
enable_appeals bool
max_modlog_days int
random_seed string
use_static_menu bool
generate_boardlist bool
)
// Global variables, most initialized by config.cfg
type GochanConfig struct {
Domain string
Port int
FirstPage []string
Error404Path string
Error500Path string
Username string
DocumentRoot string
TemplateDir string
LogDir string
DBtype string
DBhost string
DBname string
DBusername string
DBpassword string
DBprefix string
DBkeepalive bool
Lockdown bool
LockdownMessage string
Sillytags string
UseSillytags bool
Modboard string
SiteName string
SiteSlogan string
SiteHeaderURL string
SiteWebfolder string
SiteDomain string
Styles_img []string
DefaultStyle_img string
Styles_txt []string
DefaultStyle_txt string
AllowDuplicateImages bool
NewThreadDelay int
ReplyDelay int
MaxLineLength int
ReservedTrips string //eventually this will be map[string]string
ThumbWidth int
ThumbHeight int
ThumbWidth_reply int
ThumbHeight_reply int
ThumbWidth_catalog int
ThumbHeight_catalog int
ThreadsPerPage_img int
ThreadsPerPage_txt int
RepliesOnBoardpage int
GenLast50 bool
GenFirst100 bool
StickyRepliesOnBoardPage int
BanColors string //eventually this will be map[string] string
BanMsg string
YoutubeWidth int
YoutubeHeight int
ExpandButton bool
ImagesOpenNewTab bool
MakeURLsHyperlinked bool
NewTabOnOutlinks bool
EnableQuickReply bool
DefaultBanReason string
EnableGeoIP bool
GeoIPDBlocation string // set to "cf" or the path to the db
MaxRecentPosts int
MakeRSS bool
MakeSitemap bool
EnableAppeals bool
MaxModlogDays int
RandomSeed string
Version float32
}
func initConfig() {
var err error
c,err = conf.ReadConfigFile("config.cfg")
if err != nil {
fmt.Println(err)
os.Exit(2)
}
config.Domain,err = c.GetString("server", "domain")
config.Port,err = c.GetInt("server", "port")
if err != nil {
config.Port = 80
fmt.Println("server.port not set in config.cfg, defaulting to 80")
}
first_page_str,err_ := c.GetString("server", "first_page")
if err_ != nil {
first_page_str = "board.html,index.html"
fmt.Println("server.first_page not set in config.cfg, defaulting to "+first_page_str)
}
config.FirstPage = strings.Split(first_page_str, ",")
config.Error404Path,err = c.GetString("server", "error_404_path")
if err != nil {
config.Error404Path = "/error/404.html"
fmt.Println("server.error_404_path not set in config.cfg, defaulting to "+config.Error404Path)
}
config.Error500Path,err = c.GetString("server", "error_500_path")
if err != nil {
config.Error500Path = "/error/500.html"
fmt.Println("server.error_500_path not set in config.cfg, defaulting to "+config.Error500Path)
}
config.Username,err = c.GetString("server", "username")
if err != nil {
config.Username = "gochan"
fmt.Println("server.username not set in config.cfg, defaulting to "+config.Username)
}
config.DocumentRoot,err = c.GetString("directories", "document_root")
if err != nil {
fmt.Println("directories.document_root not set in config.cfg, halting.")
os.Exit(2)
}
config.TemplateDir,err = c.GetString("directories", "template_dir")
if err != nil {
config.TemplateDir = "templates"
fmt.Println("directories.template_dir not set in config.cfg, defaulting to "+config.TemplateDir)
}
config.LogDir,err = c.GetString("directories", "log_dir")
if err != nil {
config.LogDir = "log"
fmt.Println("directories.log_dir not set in config.cfg, defaulting to "+config.LogDir)
}
access_log,err = logfile.OpenLogFile(config.LogDir+"/access.log",false)
if err != nil {
fmt.Println("Couldn't open access log. Returned error: "+err.Error())
}
error_log,err = logfile.OpenLogFile(config.LogDir+"/error.log",false)
if err != nil {
fmt.Println("Couldn't open error log. Returned error: "+err.Error())
}
config.DBtype,err = c.GetString("database", "type")
if err != nil {
config.DBtype = "mysql"
fmt.Println("database.db_type not set in config.cfg, defaulting to "+config.DBtype)
}
config.DBhost,err = c.GetString("database", "host")
if err != nil {
config.DBhost = "unix(/var/run/mysqld/mysqld.sock)"
fmt.Println("database.db_host not set in config.cfg, defaulting to "+config.DBhost)
}
config.DBname,err = c.GetString("database", "name")
if err != nil {
fmt.Println("database.db_name not set in config.cfg, halting.")
os.Exit(2)
}
config.DBusername,err = c.GetString("database", "username")
if err != nil {
fmt.Println("database.db_username not set in config.cfg, halting.")
os.Exit(2)
}
config.DBpassword,err = c.GetString("database", "password")
if err != nil {
config.DBpassword = ""
}
config.DBprefix,err = c.GetString("database", "prefix")
if err == nil {
config.DBprefix += "_"
} else {
config.DBprefix = ""
}
config.DBkeepalive,err = c.GetBool("database", "keepalive")
if err != nil {
config.DBkeepalive = false
}
config.Lockdown,err = c.GetBool("gochan", "lockdown")
if err != nil {
config.Lockdown = false
}
config.LockdownMessage,err = c.GetString("gochan", "lockdown_message")
if err != nil {
config.LockdownMessage = ""
}
config.Sillytags,err = c.GetString("gochan", "sillytags")
if err != nil {
config.Sillytags = ""
}
config.UseSillytags,err = c.GetBool("gochan", "use_sillytags")
if err != nil {
config.UseSillytags = false
}
config.Modboard,err = c.GetString("gochan", "mod_board")
if err != nil {
config.Modboard = "staff"
}
config.SiteName,err = c.GetString("site", "name")
if err != nil {
config.SiteName = "An unnamed imageboard"
}
config.SiteSlogan,err = c.GetString("site", "slogan")
if err != nil {
config.SiteSlogan = ""
}
config.SiteWebfolder,err = c.GetString("site", "webfolder")
if err != nil {
fmt.Println("site.webfolder not set in config.cfg, halting.")
os.Exit(2)
}
styles_str,err_ := c.GetString("styles", "styles")
if err == nil {
config.Styles_img = strings.Split(styles_str, ",")
}
config.DefaultStyle_img,err = c.GetString("styles", "default_style")
if err != nil {
config.DefaultStyle_img = "pipes"
}
styles_txt_str,err_ := c.GetString("styles", "styles_txt")
if err == nil {
config.Styles_txt = strings.Split(styles_txt_str, ",")
}
config.DefaultStyle_txt,err = c.GetString("styles", "default_txt_style")
if err != nil {
config.DefaultStyle_txt = "pipes"
}
config.AllowDuplicateImages,err = c.GetBool("posting", "allow_duplicate_images")
if err != nil {
config.AllowDuplicateImages = true
}
config.NewThreadDelay,err = c.GetInt("posting", "new_thread_delay")
if err != nil {
config.NewThreadDelay = 30
}
config.ReplyDelay,err = c.GetInt("posting", "reply_delay")
if err != nil {
config.ReplyDelay = 7
}
config.MaxLineLength,err = c.GetInt("posting", "max_line_length")
if err != nil {
config.MaxLineLength = 150
}
//ReservedTrips string //eventually this will be map[string]string
config.ThumbWidth,err = c.GetInt("thumbnails", "thumb_width")
if err != nil {
config.ThumbWidth = 200
}
config.ThumbWidth,err = c.GetInt("thumbnails", "thumb_height")
if err != nil {
config.ThumbHeight = 200
}
config.ThumbWidth_reply,err = c.GetInt("thumbnails", "reply_thumb_width")
if err != nil {
config.ThumbWidth_reply = 125
}
config.ThumbWidth,err = c.GetInt("thumbnails", "reply_thumb_width")
if err != nil {
config.ThumbHeight_reply = 125
}
config.ThumbWidth,err = c.GetInt("thumbnails", "catalog_thumb_width")
if err != nil {
config.ThumbWidth_catalog = 50
}
config.ThumbWidth,err = c.GetInt("thumbnails", "catalog_thumb_width")
if err != nil {
config.ThumbHeight_catalog = 50
}
config.ThreadsPerPage_img,err = c.GetInt("threads", "img_threads_per_page")
if err != nil {
config.ThreadsPerPage_img = 10
}
config.ThreadsPerPage_txt,err = c.GetInt("threads", "txt_threads_per_page")
if err != nil {
config.ThreadsPerPage_txt = 15
}
config.RepliesOnBoardpage,err = c.GetInt("threads", "replies_on_boardpage")
if err != nil {
config.RepliesOnBoardpage = 3
}
config.StickyRepliesOnBoardPage,err = c.GetInt("threads", "sticky_replies_on_boardpage")
if err != nil {
config.StickyRepliesOnBoardPage = 1
}
config.GenLast50,err = c.GetBool("threads", "gen_last50_page")
if err != nil {
config.GenLast50 = true
}
config.GenFirst100,err = c.GetBool("threads", "gen_first100_page")
if err != nil {
config.GenFirst100 = false
}
config.BanColors,err = c.GetString("threads", "ban_colors") //eventually this will be map[string] string
if err != nil {
config.BanColors = "admin:#CC0000"
}
config.BanMsg,err = c.GetString("threads", "ban_msg")
if err != nil {
config.BanMsg = "(USER WAS BANNED FOR THIS POST)"
}
config.ExpandButton,err = c.GetBool("threads", "expand_button")
if err != nil {
config.ExpandButton = true
}
config.ImagesOpenNewTab,err = c.GetBool("threads", "images_open_new_tab")
if err != nil {
config.ImagesOpenNewTab = true
}
config.MakeURLsHyperlinked,err = c.GetBool("threads", "make_urls_hyperlinked")
if err != nil {
config.MakeURLsHyperlinked = true
}
config.NewTabOnOutlinks,err = c.GetBool("threads", "new_tab_on_outlinks")
if err != nil {
config.NewTabOnOutlinks = true
}
config.EnableQuickReply,err = c.GetBool("threads", "quick_reply")
if err != nil {
config.EnableQuickReply = true
}
config.DefaultBanReason,err = c.GetString("misc","default_ban_reason")
if err != nil {
config.DefaultBanReason = ""
}
config.EnableGeoIP,err = c.GetBool("misc", "enable_geoip")
if err != nil {
config.EnableGeoIP = false
}
config.GeoIPDBlocation,err = c.GetString("misc","geoip_location") // cf for cloudflare or a local path
if err != nil {
if config.EnableGeoIP {
fmt.Println("Error: GeoIP enabled but no database provided. Set misc.geoip_location in config.cfg to \"cf\" to use CloudFlare's GeoIP headers, or to a local filepath")
} else {
config.GeoIPDBlocation = ""
}
}
config.MaxRecentPosts,err = c.GetInt("misc", "max_recent_posts")
if err != nil {
config.MaxRecentPosts = 10
}
config.MakeRSS,err = c.GetBool("misc", "make_rss")
if err != nil {
config.MakeRSS = false
}
config.MakeSitemap,err = c.GetBool("misc", "make_sitemap")
if err != nil {
config.MakeSitemap = false
}
config.EnableAppeals,err = c.GetBool("misc", "enable_appeals")
if err != nil {
config.EnableAppeals = true
}
config.MaxModlogDays,err = c.GetInt("misc", "max_modlog_days")
if err != nil {
config.MaxModlogDays = 15
}
config.RandomSeed,err = c.GetString("misc", "random_seed")
if err != nil {
}
config.Version = version
}

View file

@ -1,13 +1,34 @@
package main
import (
"bufio"
"bytes"
"crypto/md5"
"fmt"
"crypto/sha1"
"code.google.com/p/go.crypto/bcrypt"
"io"
"os"
"math/rand"
"net/http"
"fmt"
"unsafe"
)
// #cgo LDFLAGS: -lcrypt
// #define _GNU_SOURCE
// #include <crypt.h>
// #include <stdlib.h>
import "C"
var crypt_data = C.struct_crypt_data{}
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+{}[]-=:\"\\/?.>,<;:'"
func crypt(key, salt string) string {
ckey := C.CString(key)
csalt := C.CString(salt)
out := C.GoString(C.crypt_r(ckey,csalt,&crypt_data))
C.free(unsafe.Pointer(ckey))
C.free(unsafe.Pointer(csalt))
return out
}
func md5_sum(str string) string {
hash := md5.New()
@ -16,34 +37,51 @@ func md5_sum(str string) string {
return digest
}
func readFileToString(path string) (str string, err error) {
var (
file *os.File
part []byte
prefix bool
)
func sha1_sum(str string) string {
hash := sha1.New()
io.WriteString(hash,str)
digest := fmt.Sprintf("%x",hash.Sum(nil))
return digest
}
if file,err = os.Open(path); err != nil {
return
func bcrypt_sum(str string) string {
hash := ""
digest,err := bcrypt.GenerateFromPassword([]byte(str), 10)
if err == nil {
hash = fmt.Sprintf("%x",digest)
}
defer file.Close()
return hash
}
reader := bufio.NewReader(file)
buffer := bytes.NewBuffer(make([]byte,0))
for {
if part,prefix,err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
str = str + buffer.String() + "\n"
buffer.Reset()
func getCookie(name string) *http.Cookie {
num_cookies := len(cookies)
for c := 0; c < num_cookies; c += 1 {
if cookies[c].Name == name {
return cookies[c]
}
}
if err == io.EOF {
err = nil
return nil
}
func generateSalt() string {
salt := make([]byte, 3)
salt[0] = chars[rand.Intn(86)]
salt[1] = chars[rand.Intn(86)]
salt[2] = chars[rand.Intn(86)]
return string(salt)
}
func getFormattedFilesize(size float32) string {
if(size < 1000) {
return fmt.Sprintf("%fB", size)
} else if(size <= 100000) {
//size = size * 0.2
return fmt.Sprintf("%fKB", size/1024)
} else if(size <= 100000000) {
//size = size * 0.2
return fmt.Sprintf("%fMB", size/1024/1024)
}
return
return fmt.Sprintf("%0.2fGB", size/1024/1024/1024)
}
func searchStrings(item string,arr []string,permissive bool) int {
@ -57,6 +95,7 @@ func searchStrings(item string,arr []string,permissive bool) int {
}
func Btoi(b bool) int {
if b { return 1 }
if b == true { return 1 }
return 0
}
}

12
templates/error.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Error :c</title>
</head>
<body>
<center>
<h1>Error!</h1>
<h2>{ERRORTEXT}</h2>
</center>
</body>
</html>

View file

@ -1 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>{SITE_NAME}</title>
<script type="text/javascript" src="/javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/javascript/gochan.js"></script>
{link css}
</head>
<body>
<center>
<h1>{SITE_NAME}</h1>
<h2>{SITE_SLOGAN}</h2>
</body>
</html>

View file

@ -1,4 +1,8 @@
</div>
<div id="footer">
<a href="{{.SiteWebfolder}}">Home</a> | <a href="{{.SiteWebfolder}}#boards">Boards</a> | <a href="{{.SiteWebfolder}}#rules">Rules</a> | <a href="{{.SiteWebfolder}}#faq">FAQ</a><br />
Powered by Kusaba v{{.Version}}<br />
Generated in over 9000 hours
</div>
</body>
</html>

View file

@ -1,8 +1,8 @@
<script type="text/javascript">
var board_type = "img";
</script>
<link rel="stylesheet" href="/css/global/front.css" />
{{range $i, $style := .Styles_img}}
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/img.css" />{{end}}
<script type="text/javascript">
var board_type = "img";
</script>
</head>
<body>
</body>

View file

@ -1,9 +1,9 @@
<link rel="stylesheet" href="/css/global/manage.css" />
{{range $i, $style := .Styles_img}}
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/manage.css" />{{end}}
<title>Gochan Manage page</title>
<script type="text/javascript" src="/javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/javascript/gochan.js"></script>
{link css}
</head>
<body>
<div id="content">
<body>