https://crieit.net/posts/BBS-programming

Files & Directories

BBS
├── bbs.rb
├── bbsdata.db
├── views
   ├── badrequest.erb
   ├── board.erb
   ├── ca.erb
   ├── created.erb
   ├── layout.erb
   ├── login.erb
   ├── loginfail.erb
   └── logout.erb

├── public
   └── css
       └── style.css

  #[ bundle init ] 
├── Gemfile 
  #[ bundle install ] 
├── nender #auto generated
└── Gemfile.lock #auto generated

bbsdata.db

create table bbsdata (
    id integer primary key,
    userid varchar(20),
    entry varchar(150),
    writedata integer,
    v integer,
    addr varchar(20),
    k integer
);

create table accounts (
    id char(20) primary key,
    salt varchar(40),
    hashed varchar(70),
    algorithm char(5)
);


create table keepers (
    id char(20) primary key,
    salt varchar(40),
    hashed varchar(70),
    algorithm char(5)
);

create table kickaccounts (
        id integer primary key,
    userid char(20),
    addr varchar(20)
);

bbs.rb

require 'digest/sha2'
require 'active_record'
require 'sinatra'

set :environment, :production

set :sessions,
  exprre_after: 72000,
  secret: 'assdffgfhjjkklaslllg'

#ActiveRecord::Base.configurations = YAML.load_file('database.yml')
#ActiveRecord::Base.establish_connection :development
ActiveRecord::Base.establish_connection(:adapter=>'sqlite3',:database=>'bbsdata.db')

class BBSdata < ActiveRecord::Base
  self.table_name = 'bbsdata'
end

class Account < ActiveRecord::Base
  self.table_name = 'accounts'
end

# keepers able to delete scum comments. 
class Keeper < ActiveRecord::Base
  self.table_name = 'keepers'
end

class Kickaccount < ActiveRecord::Base
  self.table_name = 'kickaccounts'
end

# html sanitizer
helpers do
  def h(text)
    Rack::Utils.escape_html(text)
  end
end

get '/' do
  redirect '/login' # create account
end

get '/ca' do # create account
  erb :ca
end

get '/ca2' do # create account
  @message = "Change username.The username is already in use." 
  erb :ca
end

post '/create' do
  @username = h(params[:username])
  @username.gsub!("\"|\\","")
  @username.gsub!(/;/,"")
  rawpassword = h(params[:passwd])
  rawpassword.gsub!("\"|\\","")
  rawpassword.gsub!(/;/,"")
  begin
    f = Account.find(@username)
    if f.id == @username
      puts "Change username.The username is already in use."
      redirect '/ca2'
    end
  rescue =>e
    puts "OK.Username has accepted."
  end

  algorithm = "100"
  rand = Random.new
  spice = Digest::SHA256.hexdigest(rand.bytes(20))
  hashed = Digest::SHA256.hexdigest(rawpassword + spice)
  ca = Account.new # create account
  ca.id = @username
  ca.salt = spice
  ca.hashed = hashed
  ca.algorithm = algorithm
  ca.save

  erb :created
end

get '/login' do
  erb :login
end

get '/created' do
  session[:username] = @username
  erb :created
end

get '/board' do
  @u_name = session[:username]
  if @u_name == nil
    redirect '/badrequest'
  end

  begin
    kicked_account = Kickaccount.all
    kicked_account.each do |kv|
      if kv.userid == @u_name
        puts "kick out: #{kv.userid}"
        redirect '/logout'
      end
    end
  rescue => e
    puts "error"
  end

  @admin = ""
  puts "you are #{@u_name}."
  begin
    roomkeeper = Keeper.find(@u_name)
    @admin = roomkeeper.id
    puts "you are roomleeper."
  rescue => e
    puts "you are not roomkeeper."
  end

  all_data = BBSdata.all
  if all_data.count == 0
    @table = "<tr><td>This BBS is Empty.</td></tr>"
  else
    @table = ""
    kicked = []
    all_data.reverse_each do |one|
      if one.k == 1
        kicked << one.userid
        kicked.uniq!
      end
      n1 = one.userid.size
      n2 = one.entry.size
      str1 = ""
      str2 = ""
      n1.times do
        str1 += "■" 
      end
      n2.times do
        str2 += "■"
      end

      @table = @table + "<tr>"
      @table = @table + "<td  style=\"witdh: 5%\">#{one.id}</td>"
      if one.v == 0
        @table = @table + "<td>[#{one.userid}]</td>"
      else
        @table = @table + "<td>[#{str1}]</td>"
      end

      @table = @table + "<td><font size=\"-2\">#{Time.at(one.writedata)}</font></td>"
      if (one.userid == @u_name && one.v == 0) || (@admin != "" && one.v == 0)
        @table = @table + "<td><form action=\"/delete\" method=\"post\">"
        @table = @table + "<input type=\"hidden\" name=\"id\" value=\"#{one.id}\">"
        @table = @table + "<input type=\"hidden\" name=\"_method\" value=\"delete\">"
        @table = @table + "<input type=\"submit\" value=\"Delete\"></form></td>"
      else
        @table = @table + "<td></td>"
      end

      @table = @table + "</tr>"
      if one.v == 0
        @table = @table + "<tr><td style=\"word-break: break-word\" colspan=\"4\">#{one.entry}</td></tr>\n"
      else
        @table = @table + "<tr><td style=\"word-break: break-word\" colspan=\"4\">#{str2}</td></tr>\n"
      end

      if @admin != "" && @admin != one.userid && !kicked.include?(one.userid)
        @table = @table + "<tr><td><font size=\"-2\">#{one.addr}</font></td>"

        @table = @table + "<td><form action=\"/kick\" method=\"post\">"
        @table = @table + "<input type=\"hidden\" name=\"bbsdataid\" value=\"#{one.id}\">"
        @table = @table + "<input type=\"hidden\" name=\"_method\" value=\"delete\">"
        @table = @table + "<input type=\"submit\" value=\"Kick\"></form></td></tr>\n"

      elsif @admin != "" && @admin != one.userid && kicked.include?(one.userid)
        @table = @table + "<tr><td><font size=\"-2\">#{one.addr}</font></td>"

        @table = @table + "<td><form action=\"/kickback\" method=\"post\">"
        @table = @table + "<input type=\"hidden\" name=\"bbsdataid\" value=\"#{one.id}\">"
        @table = @table + "<input type=\"hidden\" name=\"_method\" value=\"delete\">"
        @table = @table + "<input type=\"submit\" value=\"Kickback\"></form></td></tr>\n"
      end

      @table = @table + "<tr><td colspan=\"4\">_____________________________________________________________________________</td></tr>"
    end
  end
  erb :board
end

post '/new' do
  maxid = 0
  begin
    last = BBSdata.last
    if last.id > maxid
      maxid = last.id
    end
  rescue =>e
    puts maxid
  end

  new_comment = BBSdata.new
  new_comment.id = maxid + 1
  new_comment.userid = h(session[:username])
  new_comment.entry = h(params[:entry])
  new_comment.writedata = Time.now.to_i
  new_comment.v = 0 # invisible : 1
  new_comment.addr = @env['REMOTE_ADDR']
  new_comment.k = 0 # kicked : 1
  new_comment.save

  redirect '/board' # return to BBS 
end

delete '/kick' do
  maxid = 0
  begin
    last = Kickaccount.last
    if last.id > maxid
      maxid = last.id
    end
  rescue => e
    puts "maxid: #{maxid}"
  end

  kicked = Kickaccount.new
  kicked.id = maxid + 1
  username = ""
  begin
    f1 = BBSdata.find(h(params[:bbsdataid]))
    kicked.userid = f1.userid
    username = f1.userid
    kicked.addr = f1.addr
    kicked.save
  rescue => e
    puts "f1 Error"
  end

  begin
    f2 = BBSdata.where(userid: "#{username}")
    f2.each do |matched_one|
      puts matched_one.id
      matched_one.k = 1
      matched_one.save
    end
  rescue => e
    puts "f2 Error"
  end

  redirect '/board'
end

delete '/kickback' do
  puts (h(params[:bbsdataid]))

  username = ""
  begin
    f1 = BBSdata.find(h(params[:bbsdataid]))
    username = f1.userid
    begin
      f2 = Kickaccount.all
      f2.each do |matched_one|
        if matched_one.userid == f1.userid
          matched_one.destroy
        end
      end
    rescue => e
      puts "f2 Error"
    end

  rescue => e
    puts "f1 Error"
  end
  begin 
    f3 = BBSdata.where(userid: "#{username}")
    f3.each do |m|
      puts m.id
      m.k = 0
      m.save
    end
  rescue => e
    puts "f3 Error"
  end

  redirect '/board'
end

delete '/delete' do
  begin
    s = BBSdata.find(h(params[:id]))
   # s.destroy
    s.v = 1 # invisible
    s.save
  rescue => e
    puts "Delete Error"
  end
  redirect '/board'
end

get '/logout' do
  session.clear
  erb :logout
end

get '/loginfail' do
  session.clear
  erb :loginfail
end

#################################
get '/*' do
  redirect '/'
end
#################################

#--------------------------------
post '/auth' do
  user = h(params[:uname])
  user.gsub!("\"","")
  user.gsub!(/;/,"")
  pass = h(params[:pass])
  pass.gsub!("\"","")
  pass.gsub!(/;/,"")

  redirect_flag = checkFlag(user, pass)
  if redirect_flag == "true"
    session[:username] = user
    redirect '/board'
  end

  redirect '/loginfail'
end

def checkFlag(check_username,check_password)
  redirect_flag = "fail" # go to "/loginfail"

  begin
    cf = Account.find(check_username)
    db_username = cf.id
    db_spice = cf.salt
    db_hashed = cf.hashed
    check_hashed = Digest::SHA256.hexdigest(check_password + db_spice)

    if check_hashed == db_hashed
      redirect_flag = "true" 
      puts "go to \"/board\"" 
    end
  rescue => e
    redirect_flag = "error"
    puts "go to \"/loginfail\""
  end

  return(redirect_flag)
end

login.erb

[<%= @env['REMOTE_ADDR'] %>]  Enter your 1) "Username" and 2) "Password".<br>

<form action='/auth' method='post'>
1) Username: <input type="text" name="uname" size="30" maxlength="20"><br>
2) Password: <input type="password" name="pass" size="30" maxlength="20"><br>
<input type="submit" value="Login">
<input type="reset" value="Reset">
</form>
<br>
<br>

Or <a href="/ca">create account</a>

board.erb

Hello <%= @u_name %>.<br>
<br>
(<a href="/logout">logout</a>)
<P>
<table border="0" width="500">
<%= @table %>
<hr>

</table>
<form method="post" action="/new">
<textarea id="input" name="entry" placeholder=""></textarea><br>
<!--<input type="text" name="entry" size=150 maxlength=100>--><br>
<input type="submit" value="Write">
</form>

created.erb

1
2
3
4
Hello <%= @username %>. Your account has created.<br>
<br>
<br>
<a href="/login">go to login page.</a>

layout.erb

1
2
3
4
5
6
7
8
<html>
<head>
<title>BBS</title>
</head>
<body>
<%= yield%>
</body>
</html>

logout.erb

1
2
3
logged out.

<a href="/login">go to login page.</a>

ca.erb

1
2
3
4
5
6
7
8
9
Set your Username and Password.<br>
<%= @message %><br>
<form action="/create" method="post">
username: <input type="text" name="username" size="40" maxlength="20"><br>
password: <input type="password" name="passwd" size="40" maxlength="30"><br>
<br>
<input type="submit" value="Create">
<input type="reset" value="Rset">
</form>

loginfail.erb

1
2
3
login failed.<br>

<a href="/login">go to login page.</a>

badrequest.erb

1
2
3
Bad request.<br>

<a href="/login">go to login page.</a>

style.css

1
2
3
table td {
    padding: 3px 5px;
}
Edit
Pub: 05 Sep 2022 12:07 UTC
Edit: 06 Sep 2022 00:01 UTC
Views: 339