You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
4.6 KiB
Ruby

#!/usr/bin/ruby
require 'socket'
require 'fileutils'
require './database.rb'
ANSI_GREEN = "\e[32m"
ANSI_BLUE = "\e[34m"
ANSI_CLEAR = "\e[0m"
APPNAME = "#{ANSI_GREEN}APOCRYPHA#{ANSI_CLEAR}"
HTTP_GET = "GET"
HTTP_POST = "POST"
HTTP_QUERY = "QUERY"
HTTP_NIL = "NIL"
ROOT = ARGV[0]
PORT = ARGV[1]
db = Database.new('db.rb')
def message(msg)
puts "#{APPNAME}: #{msg}"
end
def error(msg)
puts "#{APPNAME}: #{msg}"
exit 1
end
#just to make the rest of the code look prettier
class String
def as_root
return "#{ROOT}/#{self}"
end
def tag(tag)
return "<#{tag}>#{self}</#{tag}>"
end
def tag_href(tag, href)
return "<#{tag} href=#{href}>#{self}</#{tag}>"
end
def tag_attrs(tag, attrs)
#this is kinda yucky
return "<#{tag} #{attrs.keys[0]}=\"#{attrs.values[0]}\">#{self}</#{tag}>"
end
end
def tmpl_not_found(data)
"<!DOCTYPE html><html><body><h>404 File not found: #{data}</h></body></html>"
end
def http_ok(data)
return "HTTP/1.1 200 OK\r\n\r\n #{data.join}" if data.class == Array
return "HTTP/1.1 200 OK\r\n\r\n #{data}" if data.class == String
message("could not :(")
end
def http_png(data)
return "HTTP/1.1 200 OK\r\nContent-Length: #{data.length}\r\nConnection: close\r\nContent-Type: image/png\r\n\r\n#{data}"
puts data.bytes
#message("could not :(")
end
def http_not_found(uri)
return "HTTP/1.1 404 Not Found\r\n\r\n #{tmpl_not_found(uri)}"
end
def despace(dir)
Dir.children(dir).each { |s|
FileUtils.mv("#{s.as_root}", "#{s.delete(' ').as_root}") unless s == s.delete(' ')
}
end
def init_database(db)
#get all files and check if theyre already in the database
keys = db.get(:key)
files = Dir.children(ROOT).filter {|x| x.end_with? ".pdf"}
not_files = files - keys
threads = []
mtx = Mutex.new
#if not make a thumbnail and add it
not_files.each do |file|
threads << Thread.new {
make_thumbnail "#{ROOT}/#{file}"
mtx.synchronize {
db.append(file, "#{File.basename(file, ".pdf")}.png")
}
}
end
threads.each { |t| t.join }
until threads.map {|t| t.alive?}.include? false
end unless threads.empty?
end
#generate html to send on requesting "/"
def gen_html(db)
keys = db.get(:key)
hrefs = db.get(:value)
key = 0
value = 1
content = keys.zip(hrefs).map { |x|
img_tag = "<img src=#{x[value]}>"
img_tag = img_tag.tag_href("a", x[key])
img_tag = img_tag.tag_attrs("div", {:class => "grid-item"})
}
content = content.join
content = content.tag_attrs("div", {:class => "grid-container"})
stylesheet = File.read("#{ROOT}/style.css")
stylesheet = stylesheet.tag("style")
head = stylesheet.tag("head")
body = content.tag("pre").tag("body")
html = (head + body).tag("html")
html = "<!DOCTYPE html>" + html
end
HANDLERS = {"/" => lambda { |cli| cli.puts http_ok gen_html(db)}}
DEFAULT = lambda { |cli, uri|
unless File.exist? "#{uri.as_root}"
message("could not open file: #{uri}")
cli.write http_not_found uri
return
end
cli.write http_ok File.read(uri.as_root)
}
IMAGE_HANDLER = lambda { |cli, uri|
unless File.exist? "#{uri.as_root}"
message("could not open file: #{uri}")
cli.write http_not_found uri
return
end
cli.write http_png File.read(uri.as_root)
}
def make_thumbnail(file)
`convert -thumbnail "140x200" -background white -alpha remove -crop 178x178+0+0 "#{file}"[0] "#{ROOT}/#{File.basename(file, ".pdf")}.png"`
end
def get_file(cli, uri)
if HANDLERS[uri]
HANDLERS[uri].call(cli)
elsif uri.end_with? ".png"
IMAGE_HANDLER.call(cli, uri.split("/")[1])
#never wants the ico?
else
DEFAULT.call(cli, uri.split("/")[1])
end
end
def put_file(cli, uri, data)
"handle it"
end
class Request
attr_reader :method
attr_reader :data
attr_reader :uri
attr_reader :version
attr_reader :headers
def parse_request(str)
if @data[0].nil?
@method = HTTP_NIL
return
end
@method = @data[0].split(" ")[0]
@uri = @data[0].split(" ")[1]
@version = @data[0].split(" ")[2]
@headers = @data.map.drop(1) { |header|
{header.split(": ")[0] => header.split(": ")[1]}
}
end
def initialize(cli)
@data = [cli.gets]
parse_request(@data)
end
end
error("specify root folder") if ROOT.nil?
error("specify port") if PORT.nil?
message("starting server on port: #{PORT}")
despace(ROOT)
init_database(db)
TCPServer.open("127.0.0.1", PORT) { |srv|
mutex = Mutex.new
loop do
#Thread.start(srv.accept) { |cli|
cli = srv.accept
mutex.synchronize do
h = Request.new(cli)
case h.method
when HTTP_GET
cli.write get_file(cli, h.uri)
when HTTP_POST
message("unknown method: #{h.method}")
when HTTP_QUERY
cli.write Dir.children(ROOT).join("\n")
when HTTP_NIL
break
else
message("unknown method: #{h.method}")
end
cli.close
end
#}
end
}