(ql:quickload "hunchentoot") (ql:quickload "easy-routes") (ql:quickload "djula") (ql:quickload "frugal-uuid") ;; this is necessary to ensure relative pathnames work (setf *default-pathname-defaults* (uiop:getcwd)) ;; make sure the bin directory exists ;; not externed from uiop, but who cares (uiop::ensure-directories-exist "bin/") ;;;; if you're gonna run a public instance, change to the url other people ;;;; will use to connect! (defvar *server-external-address* "http://localhost:8080/" ;;"https://paste.emin.software/" ) (defvar *server* nil) ;; we probably don't actually need a template for the root, ;; it's more or less static anyhow. ;; i guess retrieving a paste is the only place that actually needs ;; the dynamic templating thingy (defparameter *root-template* (uiop:read-file-string "root.html")) (defparameter *paste-template* (uiop:read-file-string "paste.html")) ;; google code ;; holy hell, actual business logic (defun get-paste-contents (id) "read file lmao. also do some checks. we don't want LFI" (let ((path (probe-file (concatenate 'string "bin/" id)))) (if (and path (not (search "." id)) (not (search "/" id))) (uiop:read-file-string path) nil))) (defun render-root () (djula:render-template* (djula:compile-string *root-template*) nil)) (defun render-paste (id) (djula:render-template* (djula:compile-string *paste-template*) nil :contents (or (get-paste-contents id) "err: paste not found") :link-home *server-external-address* :link-bin (concatenate 'string *server-external-address* "bin/" id) :link-raw (concatenate 'string *server-external-address* "raw/" id))) (defun new-paste-file () (let ((id (fuuid:to-string (fuuid:make-v1)))) (if (get-paste-contents id) (new-paste-file) id))) (defun create-new-paste (contents) "Returns the url to the new paste" (let ((path (new-paste-file))) (with-open-file (s (concatenate 'string "bin/" path) :direction :output) (princ contents s)) path)) (easy-routes:defroute root ("/" :method :get) () (render-root)) (easy-routes:defroute paste ("/bin/:n") () (render-paste n)) (easy-routes:defroute raw ("/raw/:n") () (get-paste-contents n)) ;; todo: handle new pastes TODO: doesn't work, fix. (easy-routes:defroute new ("/new" :method :post) (content) (let ((path (create-new-paste content))) (format t "created new bin ~a~%" path) (hunchentoot:redirect (concatenate 'string "/bin/" path)))) (defun start-server (&key (port 8080)) (format t "Starting server on port ~a~%" port) (force-output) (setf *server* (make-instance 'easy-routes:easy-routes-acceptor :port port)) (hunchentoot:start *server*)) (defun stop-server () (hunchentoot:stop *server*))