(ns utils (:import java.text.SimpleDateFormat java.util.Date java.net.URLDecoder org.antlr.stringtemplate.StringTemplateGroup) (:use clojure.contrib.json.write clojure.contrib.sql)) (let [db-host "localhost" db-port 5432 db-name "dumpfm"] (def *db* {:classname "org.postgresql.Driver" :subprotocol "postgresql" :subname (str "//" db-host ":" db-port "/" db-name) :user "postgres" :password "root"})) ;; moved this to here which doesn't seem right... maybe a 'settings.clj' or something? (def *dumps-per-page* 20) ;; Misc (defn no-args-adaptor [f] (fn [& more] (f))) (defn ms-in-future [ms] (+ ms (System/currentTimeMillis))) (defn swap [f] (fn [& more] (apply f (reverse more)))) (def YYYYMMDD-format (new SimpleDateFormat "yyyyMMdd")) (defn today [] (.format YYYYMMDD-format (new Date))) (def formatter (new SimpleDateFormat "h:mm a EEE M/d")) (defn non-empty-string? [s] (cond (string? s) (> (count s) 0) :else s)) (defn seconds [t] (* t 1000)) (defn minutes [t] (* t 60 1000)) (defn kbytes [b] (* b 1024)) (defn mbytes [b] (* b 1024 1024)) ;; JSON responses (def yyyy-mm-dd-formatter (new SimpleDateFormat "yyyy-MM-dd")) (defmethod print-json Date [d] (print-json (.format yyyy-mm-dd-formatter d))) (defn resp-error [message] {:status 400 :headers {} :body message}) (defn resp-success [message] {:status 200 :headers {} :body (json-str message)}) ;; Database (defn do-cmds [query] (with-connection *db* (do-commands query))) (defn do-select [query] (with-connection *db* (with-query-results rs query (doall rs)))) (defn do-count [query] ((first (with-connection *db* (with-query-results rs query (doall rs)))) :count)) (defn do-delete [table query] (with-connection *db* (delete-rows table query))) (defn do-insert [table cols values] (with-connection *db* (insert-values table cols values))) ;; Parsing (defn maybe-parse-int ([s] (Integer/parseInt s)) ([s default] (try (Integer/parseInt s) (catch NumberFormatException _ default)))) (defn maybe-parse-long [s f] (if s (Long/parseLong s) f)) (defn url-decode [text] (URLDecoder/decode text "UTF-8")) (defn #^String lower-case "Converts string to all lower-case." [#^String s] (.toLowerCase s)) ;; 404 (defn unknown-page [& more] [404 "Page not Found"]) ;; Templates (def template-group (new StringTemplateGroup "dumpfm" "template")) (.setRefreshInterval template-group 3) ;; TODO: handle exception (defn fetch-template [template session] (let [st (.getInstanceOf template-group template)] (if (session :nick) (do (.setAttribute st "user_email" (session :email)) (.setAttribute st "user_nick" (session :nick)) (if (non-empty-string? (session :avatar)) (.setAttribute st "user_avatar" (session :avatar))) (.setAttribute st "isadmin" (session :is_admin)))) ;; TODO: consolidate session/user code st)) (defn serve-template [template session] (.toString (fetch-template template session))) ;; User authentication ; TODO: create user module (defn first-or-nil [l] (if (empty? l) nil (first l))) (def nick-regex #"^[A-Za-z0-9\-_∆˚†]*$") (defn is-invalid-nick? [n] (cond (< (count n) 3) "NICK_TOO_SHORT" (not (re-matches nick-regex n)) "NICK_INVALID_CHARS")) (defn check-nick [nick] (let [query "SELECT * FROM users WHERE LOWER(nick) = ? LIMIT 1"] (> (count (do-select [query (lower-case nick)])) 0))) (defn fetch-nick [nick] (let [q1 "SELECT * FROM users WHERE nick = ? LIMIT 1" ; ORDER BY ensures consistent retrieval of ambiguious names q2 "SELECT * FROM users WHERE lower(nick) = ? ORDER BY nick LIMIT 1"] (or (first-or-nil (do-select [q1 nick])) (first-or-nil (do-select [q2 (lower-case nick)]))))) (defn authorize-nick-hash [nick hash] (let [db-user (fetch-nick nick)] (and db-user (= (db-user :hash) hash) db-user))) (defn is-vip? [session] (session :is_admin)) (defmacro if-vip [e] "Evaluates expr if user is vipm otherwise returns 404 string. Can only be used where session is defined." `(if (is-vip? ~'session) ~e (unknown-page)))