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 #!/bin/bash
go build -o gochan ./src go build -v -o gochan ./src

View file

@ -1,95 +1,85 @@
[server] [server]
domain = gochan.net domain = gochan.net
port = 80 port = 8080
document_root = /path/to/document_root/ 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] [database]
type = mysql type = mysql
host = host = unix(/var/run/mysqld/mysqld.sock)
name = gochan name = gochan
username = myuser username = root
password = mypass password = passwd
prefix = gochan_ prefix = gochan
keepalive = false keepalive = false
[gochan] [gochan]
lockdown = false 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 sillytags = Admin,Mod,Janitor,Faget,Kick me,Derpy
use_sillytags = false use_sillytags = false
mod_board = secretmodfun
[site] [site]
name = Gochan name = Gochan
slogan = "Do you like mmmmbananas?" slogan = "Do you like mmmmbananas?"
headerurl = headerurl =
irc = webfolder = /
banreason =
allowdupes = true
webfolder = /beta
webpath = http://gochan.net/
domain = gochan.net domain = gochan.net
root_dir =
template_dir = templates
[styles] [styles]
styles = burichan,futaba styles = pipes,burichan,futaba,braeburn
default = burichan default_style = pipes
switcher = true
dropdown_switcher = false
styles_txt = buritxt,futaba styles_txt = buritxt,futaba
default_txt-style = buritxt default_txt_style = buritxt
txt_style_switcher = true
menu_type = normal
menu_styles = burichan:futaba
default_menu_style = burichan
menu_style_switcher = true
[posting] [posting]
allow_duplicate_images = true
new_thread_delay = 30 new_thread_delay = 30
reply_delay = 7 reply_delay = 7
line_length = 150 max_line_length = 150
reserved_trips = "#from:To,#changeme2:changeme2"
[thumbnails]
thumb_width = 200 thumb_width = 200
thumb_height = 200 thumb_height = 200
reply_thumb_width = 125 reply_thumb_width = 125
reply_thumb_height = 125 reply_thumb_height = 125
catalog_thumb_width = 50 catalog_thumb_width = 50
catalog_thumb_height = 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 img_threads_per_page = 10
txt_threads_per_page = 15 txt_threads_per_page = 15
replies_on_boardpage = 3 replies_on_boardpage = 3
sticky_replies_on_boardpage = 1 sticky_replies_on_boardpage = 1
thumb_msg = false gen_last50_page = true
ban_colors = Luna:#0000A0,PinkiePie#FF1493,Fleur de Lis:pink,Rainbow Dash:red gen_first100_page = false
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> ban_colors = admin:#0000A0,mod#FF1493
traditional_read = false ban_msg = <br /><span style=\"color: \""+BANCOLOR+"\"><b>(USER WAS BANNED FOR THIS POST)</b></span>
youtube_width = 200 youtube_width = 200
youtube_height = 164 youtube_height = 164
expand_button = true
first_page = board.html" images_open_new_tab = true
use_dir_title = false make_urls_hyperlinked = true
new_tab_on_outlinks = true
make_rss = true
expand = true
quick_reply = true quick_reply = true
watched_threads = true
gen_firstlast_pages -= true [misc]
use_blotter = true default_ban_reason =
make_sitemap = false enable_geoip = true
max_recent_posts = 10
make_rss = true
make_sitemap = true
enable_appeals = true enable_appeals = true
max_modlog_days = 14 max_modlog_days = 14
random_seed = String to use as seed for randomly generated fun random_seed = DONT Taz3 m3 br0hgyuftyufrtderydrtiygyuigtd5456w53eyrtdughu
use_static_menu = false enable_geoip = true
generate_boardlist = true geoip_location = /usr/share/GeoIP/GeoIP.dat
date_format = D, F d, Y g:i A

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-family:sans-serif;
font-size:75%; font-size:75%;
margin:8px; margin:8px;
width:90%; width:100%;
height: 100%;
} }
body,html { body,html {

View file

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

View file

@ -4,7 +4,7 @@ body {
background: #EEF2FF; background: #EEF2FF;
color: #000; color: #000;
margin: 0; margin: 0;
width: 90%; /*width: 90%;*/
} }
#topmenu li { #topmenu li {
display: block; display: block;
@ -38,6 +38,8 @@ div#loginbox {
top:50%; top:50%;
margin-left: -100px; margin-left: -100px;
margin-top: -35px; margin-top: -35px;
padding-top:5px;
padding-bottom:5px;
text-align: center; text-align: center;
border: solid 1px; 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 { #footer {
border-bottom:10px!important; font-size:12px;
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%;
} }
#site-title { #site-title {
background: inherit;
clear: both;
color: #e1b400; color: #e1b400;
font-family: sans-serif;
font-size:50px;
text-align: center;
width: 100%;
} }
#top-pane { #current-tab {
height:75px; background-color: #404040;
left:0; }
position:absolute;
.tab {
background-color: #202020;
border: 1px solid #424242;
text-align:center; 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 { #topmenu {
left:20%; box-shadow: 0 2px 2px 3px #101010;
padding-bottom:5; background-color: #202020;
position:absolute; height: 26px;
top:78px;
z-index: 2;
} }
#topmenu li { .topmenu-item {
background-color:#404040; padding: 4px;
border:1px solid #9295a4;
border-left:none;
border-bottom: none;
display:block;
float:left;
margin-top:-7px;
padding:3px 10px 2px;
} }
#topmenu li.current { .topmenu-item:hover {
background-color:#202020; background-color: #404040;
border-bottom:none;
margin-top:-8px;
padding-top:2px;
} }
#topmenu li.first { #topmenu a {
border-left:1px solid #9295a4;
}
.content {
margin-left:0!important;
padding-left:0!important;
text-align:justify;
} }
.menu { .menu {
@ -78,44 +54,26 @@
text-align:center; text-align:center;
} }
.newssub { a.permalink {
position:absolute;
background-color: #202020;
}
.permalink {
display:block;
text-align:right;
}
.permalink a {
background: inherit; background: inherit;
color: #f90; color: #f90;
font-family: sans-serif;
text-decoration: none;
} }
.permalink a:hover { a.permalink:hover {
background: inherit; background: inherit;
color: #ffd43f; color: #ffd43f;
font-family: sans-serif;
} }
.plus { .plus {
background:#404040; background:#404040;
border-radius: 15px;
color:#000; color:#000;
cursor:pointer; height:inherit;
float:right;
font-size:12px;
font-weight:400; font-weight:400;
margin:0;
padding:1px 4px 2px;
} }
.plus:hover { .plus:hover {
background:#c5c9e0; background:#c5c9e0;
border:1px solid #c97; cursor:pointer;
} }
a { a {
@ -132,79 +90,24 @@ a:hover {
} }
body { body {
background:#EEF2FF; background: #EEF2FF;
color:#000;
font-family:sans-serif;
font-size:75%;
margin:8px;
width:90%;
}
body,html {
margin:0;
padding:0;
background: #32353d;
background-attachment: fixed; background-attachment: fixed;
background-image: url(images/pipes_bg.png); background-image: url(images/pipes_bg.png);
color: #d8d0b9; color: #d8d0b9;
font-family:sans-serif;
font-size: 12pt; font-size: 12pt;
} }
h2 {
h1 {
color:#000;
font-size:150%;
margin:0;
text-align:center;
}
h1,h2 {
background-color: #202020; background-color: #202020;
-moz-box-shadow: 2px 2px 3px 4px #101010; -moz-box-shadow: 2px 2px 3px 4px #101010;
-webkit-box-shadow: 2px 2px 3px 4px #101010; -webkit-box-shadow: 2px 2px 3px 4px #101010;
box-shadow: 0 2px 2px 3px #101010; box-shadow: 0 2px 2px 3px #101010;
border-radius: 8px; border-radius: 8px;
text-align:left; padding-left: 4px;
padding-right: 4px;
} }
h1,h3,.menu { ul.boardmenu li:hover {
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 {
background:#404040; 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> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>It worked!</title> <title>Lunachan</title>
<script type="text/javascript" src="javascript/jquery/jquery-1.7.2.min.js"></script> <script type="text/javascript" src="/javascript/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript"> <script type="text/javascript" src="/javascript/gochan.js"></script>
$jq = jQuery.noConflict();
$jq(document).ready(function() { <link rel="stylesheet" href="/css/global/front.css" />
var killserver_btn = $jq("button#killserver"); <link rel="stylesheet" href="/css/pipes/front.css" />
<link rel="alternate stylesheet" href="/css/burichan/front.css" />
$jq("button#killserver").click(function() { <link rel="alternate stylesheet" href="/css/futaba/front.css" />
$jq.ajax({ <link rel="alternate stylesheet" href="/css/braeburn/front.css" />
method:'GET', <link rel="alternate stylesheet" href="/css/leetchan/front.css" />
url:"/manage", </head>
data: { <body>
action: 'killserver'
},
<div id="topmenu">
success: function() { <div class="topmenu-section">
<div class="topmenu-item">
}, <a href="/test1/">/test1/</a>
error:function() { </div>
<div class="topmenu-item">
} <a href="/test2/">/test2/</a>
}); </div>
}); </div>
}); </div>
</script> <div id="top-pane">
</head> <span id="site-title">Lunachan</span><br />
<body> <span id="site-slogan">Do you like mmmmmmbananas?</span>
It works!<br /> </div>
<button id="killserver">Kill server</button>
</body> <div id="side-pane">
</html <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> <body>
<h1>Upload file</h1> <h1>Upload file</h1>
<form action="/upload" method="POST" id="uploadform" enctype="multipart/form-data"> <form action="/post" method="POST" id="uploadform" enctype="multipart/form-data">
<input type="file" id="fileinput" multiple="true" name="file"> <input type="file" id="file" multiple="false" name="file">
<input type="submit" id="filesubmit" value="Upload"> <input type="submit" id="filesubmit" value="Upload">
</form> </form>

View file

@ -1,2 +1,266 @@
-- Initial setup file for Gochan -- 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 - Set up daemonization
+ Clean up config file template - Set up load balancing
+ Clean up server.go - 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 templating
- Set up timezone adjusting
- Set up new mod menu style, integrated with board pages - 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 basic posting/board interaction
- Set up thumbnailing/image uploading
- find out what changes were made in Kusaba 0.9.3 to fix XSS vulnerability - 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 ( var (
pid, piderr uintptr version float32 = 0.2
version = 0.1
err error
) )
func main() { func main() {
//modlogentries := []ModLogEntry //modlogentries := []ModLogEntry
//posts := []Post //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 //check if initialsetup file exists
if err != nil { if err != nil {
needs_initial_setup = false needs_initial_setup = false
connectToSQLServer(true)
} else {
needs_initial_setup = true
runInitialSetup()
} }
fmt.Println("Connecting to database...(no, not really)")
//connectToDB() fmt.Println("Loading and parsing templates...")
//dbTests() initTemplates()
fmt.Println("Initializing server...") fmt.Println("Initializing server...")
go initServer() go initServer()
select {} 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 ( import (
"net/http" "net/http"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strings" "strconv"
_ "code.google.com/p/go.crypto/bcrypt"
) )
type ManageFunction struct { type ManageFunction struct {
Permissions int // 0 -> non-staff, 1 => janitor, 2 => moderator, 3 => administrator 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 // check if we have sufficient permissions to run this function
//return values: 0 if successful, 1 if insufficient privelages //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") 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" 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 { if _,ok := manage_functions[action]; ok {
var manage_page_html = "" if staff_rank >= manage_functions[action].Permissions {
if getStaffRank() >= manage_functions[action].Permissions { manage_page_html += manage_functions[action].Callback()
global_header,_ := readFileToString("templates/global_header.html") fmt.Fprintf(writer,manage_page_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
} else {
manage_page_html = manage_page_html + action + " is undefined."
fmt.Fprintf(writer,manage_page_html)
} }
} else { } else {
var manage_page_html = "" manage_page_html = manage_page_html + action + " is undefined."
global_header,_ := readFileToString("templates/global_header.html") fmt.Fprintf(writer,manage_page_html)
manage_header,_ := readFileToString("templates/manage_header.html") }
global_footer,_ := readFileToString("templates/global_footer.html")
manage_page_html = global_header +"\n"+ manage_header global_footer,err := getTemplateAsString(*global_footer_tmpl)
manage_page_html = strings.Replace(manage_page_html,"{link css}",getStyleLinks("manage"),-1) if err != nil {
fmt.Fprintf(writer,err.Error())
manage_page_html = manage_page_html + action + " is undefined." + global_footer } else {
fmt.Fprintf(writer,global_footer)
fmt.Fprintf(w,manage_page_html)
return 0
} }
return 1
} }
func getStaffRank() int { 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{ var manage_functions = map[string]ManageFunction{
"initialsetup": { "initialsetup": {
Permissions: 0,
Callback: func() string {
html,_ := ioutil.ReadFile(config.DocumentRoot+"/index.html")
return string(html)
}},
"error": {
Permissions: 0, Permissions: 0,
Callback: func() (html string) { Callback: func() (html string) {
html,err = readFileToString(document_root+"/index.html") exitWithErrorPage("lel, internet")
return return
}}, }},
"login":{ "login":{
Permissions: 0, Permissions: 0,
Callback: func() (html string) { Callback: func() (html string) {
html = "<div id=\"loginbox\">" + username := request.FormValue("username")
"\t<form method=\"GET\" action=\"/manage\">\n" + password := request.FormValue("password")
"\t\t<input type=\"hidden\" name=\"action\" value=\"login\" />\n" +
"\t\t<input type=\"text\" name=\"username\" /><br />\n" + if username == "" || password == "" {
"\t\t<input type=\"password\" name=\"password\" /> <br />\n" + //assume that they haven't logged in
"\t\t<input type=\"submit\" value=\"Login\" />\n" + html = "\t<form method=\"POST\" action=\"/manage?action=login\" class=\"loginbox\">\n" +
"\t</form>" + //"\t\t<input type=\"hidden\" name=\"action\" value=\"login\" />\n" +
"</div>" "\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 return
}}, }},
"announcements": { "announcements": {
Permissions: 1, Permissions: 1,
Callback: func() (html string) { 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") /*results,err := db.Query("SELECT * FROM `"+db_prefix+"announcements")
if err != nil { if err != nil {
@ -92,23 +233,30 @@ var manage_functions = map[string]ManageFunction{
return return
}}, }},
"manageserver": { "manageserver": {
Permissions: 0, Permissions: 3,
Callback: func() (html string) { 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>" + 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" "<button id=\"killserver\">Kill server</button><br />\n"
return return
}}, }},
"rebuildall": { "cleanup": {
Permissions:3, Permissions:3,
Callback: func() (html string) { Callback: func() (html string) {
return return
}}, }},
"rebuildall": {
Permissions:3,
Callback: func() (html string) {
initTemplates()
return
}},
"recentposts": { "recentposts": {
Permissions:1, Permissions:1,
Callback: func() (html string) { 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 return
}}, }},
"killserver": { "killserver": {
@ -116,4 +264,5 @@ var manage_functions = map[string]ManageFunction{
Callback: func() (html string) { Callback: func() (html string) {
os.Exit(0) os.Exit(0)
return 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 package main
import ( import (
"os"
"fmt" "fmt"
"strconv" "io/ioutil"
"path"
"net" "net"
"net/url"
"net/http" "net/http"
//"html/template" "net/url"
"os"
"path"
"strconv"
"strings"
) )
var ( var (
form url.Values form url.Values
header http.Header header http.Header
cookies []*http.Cookie cookies []*http.Cookie
writer http.ResponseWriter
request http.Request
exit_error bool
) )
func initServer() { func initServer() {
if port == 0 { if config.Port == 0 {
port = 80 config.Port = 80
} }
getStyleLinks("manage") listener,err := net.Listen("tcp", config.Domain+":"+strconv.Itoa(config.Port))
listener,err := net.Listen("tcp", domain+":"+strconv.Itoa(port))
if(err != nil) { if(err != nil) {
error_log.Write(err.Error()) 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) 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) http.Serve(listener, nil)
} }
func getFileHTTPCode(filename string) int { func fileHandle(w http.ResponseWriter, r *http.Request) {
filename = document_root+"/"+filename request = *r
writer = w
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) {
cookies = request.Cookies() cookies = request.Cookies()
request.ParseForm() request.ParseForm()
form = request.Form 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 { func makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, request *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
defer func() { //defer serverError()
if _, ok := recover().(error); ok { if !exit_error {
//don't panic if the file doesn't exist fn(w, r)
//w.WriteHeader(404) exit_error = false
http.ServeFile(w, request, path.Join(document_root, "404.html")) } else {
error_log.Write("Error: 404 Not Found from " + request.RemoteAddr + " @ " + request.RequestURI) exit_error = false
return }
}
}()
title := request.URL.Path
fn(w, request, title)
} }
}
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 ( import (
"os" "os"
"fmt" "fmt"
"database/sql" //"database/sql"
_ "go-mysql-driver/mysql" //_ "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 ( var (
db *sql.DB //db *sql.DB
db mysql.Conn
db_connected = false
) )
func connectToDB() { func connectToSQLServer(usedb bool) {
db, err = sql.Open("mysql",db_username+":"+db_password+"@"+db_host+"/"+db_name+"?charset=utf8&keepalive="+db_persistent_str) //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 { if err != nil {
error_log.Write(err.Error()) error_log.Write(err.Error())
fmt.Println("Failed to connect to the database, see log for details.") fmt.Println("Failed to connect to the database, see log for details.")
os.Exit(2) 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() { 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 { if err != nil {
error_log.Write(err.Error()) 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() { 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) } //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 package main
func getStyleLinks(stylesheet string) (links_str string) { import (
num_styles := len(styles_arr) "bytes"
for l := 0; l < num_styles; l++ { "fmt"
links_str += "<link rel=\"" "io/ioutil"
if l > 0 { "net/http"
links_str += "alternate " "os"
} "text/template"
links_str += "stylesheet\" href=\"/css/"+styles_arr[l]+"/"+stylesheet+".css\" />\n" )
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 package main
import ( import (
"strconv" "fmt"
"os"
"strings" "strings"
"go-logfile/logfile" "go-logfile/logfile"
"goconf/conf" "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 { type BanlistTable struct {
id int id uint
Type bool
expired bool expired bool
allowread bool allow_read bool
ip string ip string
ipmd5 string silent_ban uint8
globalban bool
silentban bool
boards string boards string
by string banned_by string
at int timestamp string
until int expires string
reason string reason string
staffnote string staff_note string
appeal string appeal string
appealat int appeal_at string
} }
type BannedHashesTable struct { type BannedHashesTable struct {
id int id uint
md5 string checksum string
bantime int
description string description string
} }
type BannedTripcodesTable struct { type BannedTripcodesTable struct {
id int id uint
name string name string
tripcode string tripcode string
} }
type BlotterTable struct {
id int
important bool
at int
message string
}
type BoardFiletypesTable struct {
boardid int
typeid int
}
type BoardsTable struct { type BoardsTable struct {
id int id uint8
order int order uint8
name string dir string
Type bool Type uint8
start int first_post uint
uploadtype bool upload_type uint8
desc string title string
image string subtitle string
section int section string
maximagesize int max_image_size int
maxpages int max_pages uint8
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
locale string locale string
showid bool default_style string
compactlist bool locked bool
enablereporting bool created_on string
enablecaptcha bool anonymous string
enablenofile bool forced_anon string
enablearchiving bool max_age uint
enablecatalog bool mark_page uint8
loadbalanceurl string autosage_after uint
loadbalancepassword string 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 { type BoardSectionsTable struct {
id int id uint8
order int order uint8
hidden bool hidden bool
name string name string
abbreviation string abbreviation string
} }
type EmbedsTable struct { type EmbedsTable struct {
id int id uint8
filetype string filetype string
name string name string
videourl string video_url string
width int width uint16
height int height uint16
code string embed_code string
} }
type FiletypesTable struct { type FiletypesTable struct {
id int id uint8
filetype string filetype string
mime string mime string
image string thumb_image string
image_w int image_w uint
image_h int image_h uint
force_thumb bool
} }
type FrontTable struct { type FrontTable struct {
id int id uint16
page int page uint8
order int order uint8
subject string subject string
message string message string
timestamp int timestamp string
poster string poster string
email string email string
} }
type FrontLinksTable struct { type FrontLinksTable struct {
id uint8
title string title string
url string url string
} }
type LoginAttemptsTable struct { type LoginAttemptsTable struct {
username string id uint
ip string ip string
timestamp int timestamp string
} }
type ModLogTable struct { type ModLogTable struct {
id uint
entry string entry string
user string user string
category int category uint8
timestamp int timestamp string
} }
type ModpageAnnouncementsTable struct { type PollResultsTable struct {
id int id uint
parentid int
subject string
postedat int
postedby string
message string
}
type PollTable struct {
ip string ip string
selection string selection string
time int timestamp string
} }
type PostTable struct { type PostTable struct {
id int id uint
boardid int boarid uint8
parentid int parentid uint
name string name string
tripcode string tripcode string
email string email string
subject string subject string
message string message string
password string password string
file string filename string
file_md5 string filename_original string
file_type string file_checksum string
file_original string filesize string
file_size int image_w uint16
file_size_formatted string image_h uint16
image_w int thumb_w uint16
image_h int thumb_h uint16
thumb_w int
thumb_h int
ip string ip string
ipmd5 string
tag string tag string
timestamp int timestamp string
autosage uint8
poster_authority uint8
deleted_timestamp string
bumped string
stickied bool stickied bool
locked bool locked bool
autosage int
posterauthority int
reviewed bool reviewed bool
deleted_timestamp int sillytag bool
IS_DELETED bool }
bumped int
sillytag string 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 { type ReportsTable struct {
id int id uint
cleared bool
board string board string
postid int postid uint
when int timestamp string
ip string ip string
reason string reason string
cleared bool
istemp bool
}
type SessionsTable struct {
id uint
data string
expires string
} }
type StaffTable struct { type StaffTable struct {
id int id uint16
username string username string
password string password_checksum string
salt string salt string
Type int rank uint8
boards string boards string
addedon int addedon string
lastactive int last_active string
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
} }
type WordFiltersTable struct { type WordFiltersTable struct {
id int id uint16
word string from string
replacedby string to string
boards string boards string
time int
regex bool regex bool
} }
var ( // Global variables, most initialized by config.cfg
needs_initial_setup = true
config,_ = conf.ReadConfigFile("config.cfg") type GochanConfig struct {
log_dir,_ = config.GetString("server","log_dir") Domain string
access_log,_ = logfile.OpenLogFile(log_dir+"/access.log",false) Port int
error_log,_ = logfile.OpenLogFile(log_dir+"/error.log",false) FirstPage []string
document_root,_ = config.GetString("server","document_root") Error404Path string
first_page_str,_ = config.GetString("server","first_page") Error500Path string
first_page = strings.Split(first_page_str,",") Username string
domain,_ = config.GetString("server","domain")
port,_ = config.GetInt("server","port") DocumentRoot string
db_type,_ = config.GetString("database","type") TemplateDir string
db_name,_ = config.GetString("database", "name") LogDir string
db_host,_ = config.GetString("database","host")
db_username,_ = config.GetString("database","username") DBtype string
db_password,_ = config.GetString("database","password") DBhost string
db_prefix,_ = config.GetString("database","prefix") DBname string
db_persistent,_ = config.GetBool("database","keepalive") DBusername string
db_persistent_str = strconv.Itoa(Btoi(db_persistent)) DBpassword string
lockdown bool DBprefix string
lockdown_message string DBkeepalive bool
sillytags string
use_sillytags bool Lockdown bool
site_name string LockdownMessage string
site_slogan string Sillytags string
site_headerurl bool UseSillytags bool
site_irc string Modboard string
site_banreason string
site_allowdupes bool SiteName string
webfolder string SiteSlogan string
webpath string SiteHeaderURL string
root_dir string SiteWebfolder string
template_dir string SiteDomain string
cached_template_dir string
styles,_ = config.GetString("styles","styles") Styles_img []string
styles_arr = strings.Split(styles,",") DefaultStyle_img string
default_style string Styles_txt []string
style_switcher bool DefaultStyle_txt string
dropdown_style_switcher bool
styles_txt []string AllowDuplicateImages bool
default_txt_style string NewThreadDelay int
txt_style_switcher bool ReplyDelay int
menu_type string MaxLineLength int
menu_styles []string ReservedTrips string //eventually this will be map[string]string
default_menu_style string
menu_style_switcher bool ThumbWidth int
new_thread_delay int ThumbHeight int
reply_delay int ThumbWidth_reply int
line_length int ThumbHeight_reply int
thumb_width int ThumbWidth_catalog int
thumb_height int ThumbHeight_catalog int
reply_thumb_width int
reply_thumb_height int ThreadsPerPage_img int
catalog_thumb_width int ThreadsPerPage_txt int
catalog_thumb_height int RepliesOnBoardpage int
thumb_method string GenLast50 bool
animated_thumbs bool GenFirst100 bool
new_window bool StickyRepliesOnBoardPage int
make_links bool BanColors string //eventually this will be map[string] string
no_message_thread bool BanMsg string
no_message_reply bool YoutubeWidth int
img_threads_per_page int YoutubeHeight int
txt_threads_per_page int ExpandButton bool
replies_on_boardpage int ImagesOpenNewTab bool
sticky_replies_on_boardpage int MakeURLsHyperlinked bool
thumb_msg bool NewTabOnOutlinks bool
ban_colors []string EnableQuickReply bool
ban_msg string
traditional_read bool DefaultBanReason string
youtube_width int EnableGeoIP bool
youtube_height int GeoIPDBlocation string // set to "cf" or the path to the db
use_dir_title bool MaxRecentPosts int
make_rss bool MakeRSS bool
expand bool MakeSitemap bool
quick_reply bool EnableAppeals bool
watched_threads bool MaxModlogDays int
gen_firstlast_pages bool RandomSeed string
use_blotter bool Version float32
make_sitemap bool }
enable_appeals bool
max_modlog_days int func initConfig() {
random_seed string var err error
use_static_menu bool c,err = conf.ReadConfigFile("config.cfg")
generate_boardlist bool 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 package main
import ( import (
"bufio"
"bytes"
"crypto/md5" "crypto/md5"
"fmt" "crypto/sha1"
"code.google.com/p/go.crypto/bcrypt"
"io" "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 { func md5_sum(str string) string {
hash := md5.New() hash := md5.New()
@ -16,34 +37,51 @@ func md5_sum(str string) string {
return digest return digest
} }
func readFileToString(path string) (str string, err error) { func sha1_sum(str string) string {
var ( hash := sha1.New()
file *os.File io.WriteString(hash,str)
part []byte digest := fmt.Sprintf("%x",hash.Sum(nil))
prefix bool return digest
) }
if file,err = os.Open(path); err != nil { func bcrypt_sum(str string) string {
return 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) func getCookie(name string) *http.Cookie {
buffer := bytes.NewBuffer(make([]byte,0)) num_cookies := len(cookies)
for { for c := 0; c < num_cookies; c += 1 {
if part,prefix,err = reader.ReadLine(); err != nil { if cookies[c].Name == name {
break return cookies[c]
}
buffer.Write(part)
if !prefix {
str = str + buffer.String() + "\n"
buffer.Reset()
} }
} }
if err == io.EOF { return nil
err = 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 { 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 { func Btoi(b bool) int {
if b { return 1 } if b == true { return 1 }
return 0 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> </body>
</html> </html>

View file

@ -1,8 +1,8 @@
<link rel="stylesheet" href="/css/global/front.css" />
<script type="text/javascript"> {{range $i, $style := .Styles_img}}
var board_type = "img"; <link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/img.css" />{{end}}
</script> <script type="text/javascript">
var board_type = "img";
</script>
</head> </head>
<body> <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> <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/jquery/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/javascript/gochan.js"></script> <script type="text/javascript" src="/javascript/gochan.js"></script>
{link css}
</head> </head>
<body> <body>
<div id="content">