commit ee8246a0cc30eb3345823fcc6ac42f0f0158385b Author: sandyx Date: Fri Feb 14 18:33:31 2025 -0600 initial diff --git a/database.rb b/database.rb new file mode 100644 index 0000000..5788e7c --- /dev/null +++ b/database.rb @@ -0,0 +1,55 @@ +require 'fileutils' + +class Database + def initialize(file) + @file = file + File.open(@file, 'w').write unless File.exist? @file + lines = File.readlines(@file) if File.exist? @file + FileUtils.touch(@file) unless File.exist? @file + lines.count = 0 unless File.exist? @file + @start = 0 + @end = lines.count + end + + def message(msg) + puts "#{@file}: #{msg}" + end + + def get(key) + lines= File.readlines(@file) + eval('[' + lines.join() + ']').map { |hash| + hash[key] + } + end + + def prepend(key, val) + lines = File.readlines(@file) + lines.insert(@start, "{:key => '#{key}', :value => '#{val}'},\n") + File.write(@file, lines.join) + end + + def append(key, val) + message "added #{key} => #{val}" + lines = File.readlines(@file) + lines.insert(@end, "{:key => '#{key}', :value => '#{val}'},\n") + File.write(@file, lines.join) + end + + def <<(hash) + lines = File.readlines(@file) + lines.insert(@end, "{:key => '#{hash[:key]}', :value => '#{hash[:value]}'},\n") + File.write(@file, lines.join) + end + + def [](i) + lines = File.readlines(@file) + return eval('[' + lines.join() + ']')[i] + #return eval lines[i] + end + + def []=(i, hash) + lines = File.readlines(@file) + lines.insert(i, "{:key => '#{hash[:key]}', :value => '#{hash[:value]}'},\n") + File.write(@file, lines.join) + end +end diff --git a/main.rb b/main.rb new file mode 100644 index 0000000..46f2cd7 --- /dev/null +++ b/main.rb @@ -0,0 +1,227 @@ +#!/usr/bin/ruby + +require 'socket' +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}" + end + + def tag_href(tag, href) + return "<#{tag} href=#{href}>#{self}" + end + + + def tag_attrs(tag, attrs) + #this is kinda yucky + return "<#{tag} #{attrs.keys[0]}=\"#{attrs.values[0]}\">#{self}" + end +end + +def tmpl_not_found(data) + "404 File not found: #{data}" +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-Type: image/png\r\n\r\n #{data}" if data.class == String + 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 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_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 = "" + 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.puts http_not_found uri + return + end + + cli.puts 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.puts http_not_found uri + return + end + + cli.puts 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? + elsif uri.end_with? ".ico" + lambda { |cli, uri| + unless File.exist? uri.as_root + message "could not open file: #{uri}" + cli.puts http_not_found uri + return + end + cli.puts "HTTP/1.1 200 OK\r\nContent-Type: image/icon\r\n\r\n" + File.read(uri.as_root) + }.call(cli, uri.split("/")[1]) + 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}") + +init_database(db) + +TCPServer.open("127.0.0.1", PORT) { |srv| + mutex = Mutex.new + loop do + Thread.start(srv.accept) { |cli| + mutex.synchronize do + h = Request.new(cli) + + case h.method + when HTTP_GET + cli.puts 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 +} + diff --git a/query.rb b/query.rb new file mode 100644 index 0000000..9f41c4d --- /dev/null +++ b/query.rb @@ -0,0 +1,54 @@ +#!/usr/bin/ruby + +require 'net/http' +require 'socket' + +PORT = 8081 +HTTP_GET = "GET" +HTTP_POST = "POST" +HTTP_QUERY = "QUERY" +QDIR = ARGV[0] + +class Request + attr_reader :method + attr_reader :data + attr_reader :uri + attr_reader :version + attr_reader :headers + + def parse_request(str) + @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 + +TCPServer.open("127.0.0.1", PORT) { |srv| + mutex = Mutex.new + loop do + Thread.start(srv.accept) { |cli| + mutex.synchronize do + h = Request.new(cli) + case h.method + when HTTP_QUERY + cli.write Dir.children(QDIR).join("\n") + else + cli.write("unknown method: #{h.method}") + end + + + cli.close + end + } + + end +}