require 'amrita/parts' require 'model' def p(param) $amritabbs_inspect_object << param puts param.inspect if $amritabbs_config[:debug_bbs] end module BBS class Location attr_accessor :theme, :template, :board, :thread, :start, :to, :last def initialize @theme, @template = @board = @thread = @start = @to = @last = nil end def to_top ret = dup ret.board = ret.template = ret.thread = nil ret.start = ret.to = ret.last = nil ret end def theme_change(theme) theme = nil if theme == $amritabbs_config[:default_theme] ret = dup ret.theme = theme ret end def to_board(board) ret = dup ret.template = 'board' ret.board = board ret.thread = ret.start = ret.to = ret.last = nil ret end def to_thread(thread) ret = dup ret.template = 'thread' ret.start = ret.to = ret.last = nil ret.thread = thread ret end def get_all ret = dup ret.start = ret.to = ret.last = nil ret end def get_last(n) ret = dup ret.last = n ret end def get_range(start, to) ret = dup ret.start = ret.to = ret.last = nil ret.start = start ret.to = to ret end def to_s ret = "#{$amritabbs_config[:script_name]}?" ret += "&theme=#@theme" if @theme ret += "&template=#@template" if @template ret += "&board=#@board" if @board ret += "&thread=#@thread" if @thread ret += "&last=#@last" if @last ret += "&start=#@start" if @start ret += "&to=#@to" if @to ret end def Location::new_from_cgi(cgi) ret = new ret.theme = cgi['theme'][0] ret.theme = nil if ret.theme == "" ret.template = cgi['template'][0] ret.board = cgi['board'][0] ret.thread = cgi['thread'][0] ret.last = cgi['last'][0] ret.last = ret.last.to_i if ret.last ret.start = cgi['start'][0] ret.start = ret.start.to_i if ret.start ret.to = cgi['to'][0] ret.to = ret.to.to_i if ret.to ret end def select(ary) if last s = last < ary.size ? last : ary.size ary[(-1*s)..-1] else if start if to ary[(start-1)..(to-1)] else ary[(start-1)..-1] end else if to ary[0..(to-1)] else ary end end end end end class BBSModel include Amrita include ExpandByMember attr_reader :loc, :data_dir, :template_dir, :view_module def initialize(loc, data_dir, template_dir) @loc = loc @data_dir = data_dir @template_dir = template_dir setup_view setup_handlers setup_advertize end def template_path ret = File::join(@theme_dir, loc.template||$amritabbs_config[:default_template]) + ".html" unless File::readable? ret dir = File::join(@template_dir, $amritabbs_config[:default_theme]) ret = File::join(dir, loc.template||$amritabbs_config[:default_template]) + ".html" end ret end def parts_template_path File::join(@theme_dir, "parts.html") end def data_path(board, path) case board when String dir = File::join(@data_dir, board) File::join(dir, path) when Board dir = File::join(@data_dir, board.key) File::join(dir, path) when nil File::join(@data_dir, path) else raise "can't happen" end end def setup_view @theme = loc.theme || $amritabbs_config[:default_theme] @theme_dir = File::join(@template_dir, @theme) require File::join(@theme_dir, "view.rb") @view_module = $amritabbs_config[:view_modules][@theme] raise "theme #{@theme} not found" unless @view_module install_view_module(self) end def install_view_module(obj, modname=nil) modname = obj.type.to_s.gsub(/([A-Z]\w*::)*([A-Z]\w*)/) { $2 } unless modname mod = @view_module.const_get(modname) obj.extend(mod) if mod rescue NameError STDERR.puts $! nil end def setup_handlers @handlers = [ ViewHandler.new(self), NewArticleHandler.new(self), NewThreadHandler.new(self), ] end def process_request(params) handler = @handlers.find do |h| h.can_handle?(params) end raise "can't handle this request" unless handler handler.process_request(params) end def show_view if File::readable?(parts_template_path) t = TemplateFileWithCache.new(parts_template_path) t.install_parts_to(@view_module) end t = TemplateFileWithCache.new(template_path) t.use_compiler = $amritabbs_config[:use_compiler] #t.debug_compiler = true t.install_parts_to(@view_module) t.expand($stdout, self) end def boardlist unless defined? @boardlist @boardlist = BoardList.new(self) end @boardlist end def current_board boardlist.get_board(loc.board) end def current_thread current_board.get_thread(loc.thread) end def setup_advertize @advertize = [] if $amritabbs_config[:advertize_html] and File::readable?($amritabbs_config[:advertize_html]) a = Amrita::HtmlParser::parse_file($amritabbs_config[:advertize_html]) @advertize = a.find_all { |e| e.tagname_symbol == :div and e[:class] == "advertize" } end end def advertize_random r = rand(@advertize.size) @advertize[r] end def theme_selecter $amritabbs_config[:themes].collect do |t| e(:a, :href=>loc.theme_change(t)) { t } end end def whatnew ret = {} ret[:boards] = [] all_thread=boardlist.categories.each do |c| ret[:boards] += c.boards.collect do |b| next if b.url link = e(:a, :href=>loc.to_board(b.key)) { b.name } path = data_path(b.key, "subject.txt") next unless File::readable? path mtime = File::stat(path).mtime { :link => link, :mtime => mtime } end end ret[:boards].delete_if { |b| b == nil} ret[:boards].sort! { |a,b| a[:mtime] <=> b[:mtime] } ret end end class HandlerBase attr_reader :model def initialize(model) @model = model end end class ViewHandler < HandlerBase def can_handle?(param) param["action"][0] == nil end def process_request(param) model.show_view end end class ModelUpdateHandler < HandlerBase def get_thread_key(board) Time.new.to_i.to_s end def lock(&block) path = model.data_path(nil, "board.txt") File::open(path) do |f| f.flock(File::LOCK_EX) begin yield(f) ensure f.flock(File::LOCK_UN) end end end def put_subject(board, key, subject) path1 = model.data_path(board, "subject.txt") path2 = model.data_path(board, "subject.txt.sav") unless subject File::open(path1) do |f| f.each do |l| if l =~ /#{key}.dat<>(.*)/ subject = $1 break end end end end File::rename(path1, path2) File::open(path1, "w") do |newf| newf.puts("#{key}.dat<>#{subject}") File::open(path2) do |oldf| oldf.each do |l| next if l =~ /#{key}/ newf.print l end end end end def put_article(board, key, name, mail, message) path = model.data_path(board, "#{key}.dat") File::open(path, "a") do |f| f.puts "#{name}<>#{mail}<>#{Time.new}<>#{message}" end end def sanitize_text(text) if text ret = text.amrita_sanitize ret.gsub!(/http:\S+/) { "#{$~}" } ret.gsub!(/\r\n|\n/, "
") ret else text end end def redirect_response(url) e(:html) { e(:head) { e(:meta, "http-equiv"=>"refresh", :content=>"0;URL=#{url}") } + e(:body) { [ "wait a second or click here-->", e(:a, :href=>url) { url } ] } } end end class NewThreadHandler < ModelUpdateHandler def can_handle?(param) param["action"][0] == "newthread" end def process_request(cgi) lock do board = sanitize_text(cgi['board'][0]) name = sanitize_text(cgi['from'][0]) mail = sanitize_text(cgi['mail'][0]) subject = sanitize_text(cgi['subject'][0]) message = sanitize_text(cgi['message'][0]) raise "no data" unless board and board != "" raise "no data" unless name and name != "" raise "no data" unless subject and subject != "" raise "no data" unless message and message != "" key = get_thread_key(board) put_subject(board, key, subject) unless mail == "sage" put_article(board, key, name, mail, message) print redirect_response(model.loc.to_board(board)) end end end class NewArticleHandler < ModelUpdateHandler def can_handle?(param) param["action"][0] == "newarticle" end def process_request(cgi) lock do board = sanitize_text(cgi['board'][0]) thread = sanitize_text(cgi['thread'][0]) name = sanitize_text(cgi['from'][0]) mail = sanitize_text(cgi['mail'][0]) message = sanitize_text(cgi['message'][0]) raise "no data" unless board and board != "" raise "no data" unless thread and thread != "" raise "no data" unless message and message != "" put_subject(board, thread, nil) unless mail == "sage" put_article(board, thread, name, mail, message) print redirect_response(model.loc.to_board(board)) end end end end