diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/site.clj | 136 | ||||
| -rwxr-xr-x | src/utils.clj | 4 |
2 files changed, 97 insertions, 43 deletions
diff --git a/src/site.clj b/src/site.clj index 8608349..a57cf16 100755 --- a/src/site.clj +++ b/src/site.clj @@ -4,6 +4,7 @@ java.util.Date java.util.TimeZone java.io.File + javax.imageio.ImageIO org.apache.commons.codec.digest.DigestUtils javax.servlet.http.Cookie org.antlr.stringtemplate.StringTemplateGroup) @@ -59,6 +60,7 @@ "http://dump.fm" "http://localhost:8080")) +(def *root-directory* (System/getProperty "user.dir")) (def *image-directory* "images") (def *avatar-directory* "avatars") @@ -74,15 +76,23 @@ (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] (and s (> (count s) 0))) -(defn rel-join [& more] - (str-join (System/getProperty "file.separator") - (cons (System/getProperty "user.dir") - more))) +(defn open-file [dir-comps filename] + (let [d (str-join (System/getProperty "file.separator") + (cons *root-directory* dir-comps)) + f (str-join (System/getProperty "file.separator") + [d filename])] + (.mkdir (new File d)) + (new File f))) (defn sha1-hash [& more] (DigestUtils/shaHex (apply str more))) @@ -162,10 +172,13 @@ (vals @(room :users))))) (defn updates [room since] - {"users" (prepare-user-list room) - "messages" (map process-message-for-json - (new-messages room since)) - "topic" @(room :topic)}) + (let [m {"users" (prepare-user-list room) + "messages" (map process-message-for-json + (new-messages room since))} + topic @(room :topic)] + (if topic + (assoc m "topic" topic) + m))) (def *dumps-per-page* 20) @@ -430,8 +443,8 @@ (.setAttribute st "users" users) (cond (= offset 0) (.setAttribute st "prev" false) (= offset 1) (.setAttribute st "prev" "") - :else (.setAttribute st "prev" (str "/" (- offset 1)))) - (.setAttribute st "next" (str "/" (+ offset 1))) + :else (.setAttribute st "prev" (str "/" (dec offset)))) + (.setAttribute st "next" (str "/" (inc offset))) (.toString st))) ;; Topics @@ -458,21 +471,34 @@ [404 "UNKNOWN_ROOM"])) (defn set-topic! [room topic deadline maker] - (ref-set (room :topic) - {:topic topic - :deadline deadline - :maker maker})) + (dosync (ref-set (room :topic) + {:topic topic + :deadline deadline + :maker maker}))) + +(defn end-topic! [room] + (dosync (ref-set (room :topic) nil))) (defn validate-set-topic [session params] (let [room (@rooms (params :room)) topic (params :topic) - deadline (params :deadline)] + deadline (params :deadline) + maker (params :maker)] (cond (not (session :is_admin)) (resp-error "NOT_VIP") (not (valid-topic? topic)) (resp-error "INVALID_TOPIC") (not (valid-deadline? deadline)) (resp-error "INVALID_DEADLINE") (not room) (resp-error "INVALID_ROOM") + (not maker) (resp-error "NOT_MAKER") :else (do - (dosync (set-topic! room topic deadline (session :nick))) + (set-topic! room topic deadline maker) + (resp-success "OK"))))) + +(defn validate-end-topic [session params] + (let [room (@rooms (params :room))] + (cond (not (session :is_admin)) (resp-error "NOT_VIP") + (not room) (resp-error "INVALID_ROOM") + :else (do + (end-topic! room) (resp-success "OK"))))) ;; Chat @@ -580,17 +606,12 @@ (let [room (@rooms "RoomA") now (System/currentTimeMillis) nick (session :nick) - st (fetch-template "browser" session) - message-list (to-array - (map process-message-for-output - ; TODO: remove db query - (reverse (fetch-messages-by-room (room :room_id) false))))] + st (fetch-template "browser" session)] (if nick (dosync (login-user (user-struct-from-session session) room))) (let [user-list (to-array (prepare-user-list room))] (.setAttribute st "users" user-list)) - (.setAttribute st "messages" message-list) (.setAttribute st "roomkey" (room :key)) (.setAttribute st "isadminroom" (room :admin_only)) (.setAttribute st "json_room_key" (json-str (room :key))) @@ -628,31 +649,60 @@ ;; Upload +(def *max-image-height* 2000) +(def *max-image-width* 2000) +(def *vip-max-file-size* (mbytes 5)) ; don't be nuts guys +(def *max-file-size* (kbytes 750)) +(def *ignore-size-limit-for-vip* true) (def *avatar-dimensions* [50 50]) -(defn is-image-file? [path] - true) +(defn file-size-limit [vip] + (if (and *ignore-size-limit-for-vip* vip) + *vip-max-file-size* + *max-file-size*)) -(defn format-filename [s] +(defn is-file-too-big? [f vip] + (let [limit (file-size-limit vip)] + (if (> (.length f) limit) + (str "FILE_TOO_BIG " limit)))) + +(defn is-image-invalid? [f] + (try + (let [i (ImageIO/read f) + height (.getHeight i) + width (.getWidth i)] + (if (or (> width *max-image-width*) + (> height *max-image-height*)) + (str "INVALID_RESOLUTION " *max-image-width* " " *max-image-height*))) + (catch Exception _ "FILE_NOT_IMAGE"))) + +(defn format-filename [s nick] (let [spaceless (.replace s \space \-) + nick-clean (re-gsub #"[^A-Za-z0-9]" "" nick) subbed (re-gsub #"[^\w.-]" "" spaceless)] - (str (System/currentTimeMillis) "-" subbed))) + (str-join "-" [(System/currentTimeMillis) "dumpfm" nick-clean subbed]))) -(defn image-url-from-file [d f] - (str-join "/" [*server-url* d (.getName f)])) +(defn image-url-from-file [dir date file] + (str-join "/" [*server-url* dir date (.getName file)])) +(defn validate-upload [f vip] + (or (is-file-too-big? f vip) + (is-image-invalid? f))) (defn do-upload [session image room] - (let [filename (format-filename (:filename image)) - dest (File. (rel-join *image-directory* filename)) - url (image-url-from-file "images" dest) - msg-id (msg-db (session :user_id) (room :room_id) url) - msg (struct message-struct (session :nick) url (new Date) msg-id)] - (do - (dosync - (add-message msg room)) - (copy (:tempfile image) dest) - (resp-success url)))) + (if-let [err (validate-upload (image :tempfile) (session :is_admin))] + (resp-error err) + (let [filename (format-filename (:filename image) (session :nick)) + date (today) + dest (open-file [*image-directory* date] filename) + url (image-url-from-file "images" date dest) + msg-id (msg-db (session :user_id) (room :room_id) url) + msg (struct message-struct (session :nick) url (new Date) msg-id)] + (do + (dosync + (add-message msg room)) + (copy (:tempfile image) dest) + (resp-success url))))) (defn upload [session params] (let [room-key (params :room) @@ -660,7 +710,6 @@ image (params :image)] (cond (not nick) [200 "NOT_LOGGED_IN"] (not image) [200 "INVALID_REQUEST"] - (not (is-image-file? (image :filename))) [200 "INVALID_IMAGE"] (not (validate-room-access room-key session)) [200 "UNKNOWN_ROOM"] :else (do-upload session image (@rooms room-key))))) @@ -670,9 +719,10 @@ ;; N.B. -- Upload responses aren't JSON-evaluated (defn do-upload-avatar [session image] - (let [filename (format-filename (:filename image)) - dest (File. (rel-join *avatar-directory* filename)) - url (image-url-from-file "avatars" dest)] + (let [filename (format-filename (:filename image) (session :nick)) + date (today) + dest (open-file [*avatar-directory* date] filename) + url (image-url-from-file "avatars" date dest)] (do (copy-and-resize (:tempfile image) dest) (update-user-db (session :user_id) "avatar" url) @@ -683,7 +733,6 @@ (let [image (params :image)] (cond (not image) [200 "INVALID_REQUEST"] (not (session :nick)) [200 "NOT_LOGGED_IN"] - (not (is-image-file? (image :filename))) [200 "INVALID_IMAGE"] :else (do-upload-avatar session image)))) ;; 404 @@ -739,6 +788,7 @@ (POST "/update-profile" (update-profile session params)) (GET "/topic-list" (validate-topic-list session)) (POST "/set-topic" (validate-set-topic session params)) + (POST "/end-topic" (validate-end-topic session params)) (GET "/directory" (directory session 0)) (GET "/directory/:offset" (directory session (maybe-parse-int (-> request :route-params :offset) 0))) diff --git a/src/utils.clj b/src/utils.clj index 5af4edc..9a95430 100755 --- a/src/utils.clj +++ b/src/utils.clj @@ -19,6 +19,10 @@ (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")) |
