From 3893ca44b9229793ec799b9f53ad6b9a01c61634 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sat, 2 Jun 2012 12:07:26 +0200 Subject: [PATCH] implementing a crossover hash function --- .gitignore | 1 + project.clj | 1 + src-cljs/main.cljs | 6 +++++ src/NoteHub/crossover/lib.clj | 17 +++++++++++++++ src/NoteHub/views/pages.clj | 41 ++++++++++++++++++++++++++--------- 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 src/NoteHub/crossover/lib.clj diff --git a/.gitignore b/.gitignore index 139c51d..8addd76 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pom.xml /classes/ .lein-* resources/public/js/* +.crossover-cljs diff --git a/project.clj b/project.clj index bb7ed66..e6f5689 100644 --- a/project.clj +++ b/project.clj @@ -15,6 +15,7 @@ :source-path "src-cljs" ; The standard ClojureScript compiler options: ; (See the ClojureScript compiler documentation for details.) + :crossovers [NoteHub.crossover] :compiler { :output-dir "resources/public/cljs/" :output-to "resources/public/cljs/main.js" ; default: main.js in current directory diff --git a/src-cljs/main.cljs b/src-cljs/main.cljs index 2a29ef2..b9d2f42 100644 --- a/src-cljs/main.cljs +++ b/src-cljs/main.cljs @@ -2,6 +2,7 @@ (:use [jayq.core :only [$ css inner val anim show]]) (:require [fetch.remotes :as remotes] [goog.dom :as gdom] + [NoteHub.crossover.lib :as nh] [clojure.browser.dom :as dom] [clojure.browser.event :as event]) (:require-macros [fetch.macros :as fm])) @@ -33,3 +34,8 @@ (show $preview-start-line) (inner $preview result) (scroll-to $preview-start-line))))) + +(.click ($ :#publish-button) + (fn [e] + (val ($ :#session-value) + (nh/hash #(.charCodeAt % 0) (str (val $draft) (val ($ :#session-key))))))) diff --git a/src/NoteHub/crossover/lib.clj b/src/NoteHub/crossover/lib.clj new file mode 100644 index 0000000..0187005 --- /dev/null +++ b/src/NoteHub/crossover/lib.clj @@ -0,0 +1,17 @@ +(ns NoteHub.crossover.lib + (:refer-clojure :exclude [hash])) + +; very simple hash function %) +; (doesn't work for UTF-16!) +(defn hash [f s] + (let [short-mod #(mod % 32767) + ; we cannot use Math/pow because it's imprecise + ; and differs on JVM and JS + pow (fn [n e] + (reduce #(short-mod (* % %2)) 1 + (repeat e n))) + char-codes (map #(f (str %)) s)] + (reduce + #(short-mod (+ % + (short-mod (* (first %2) (pow 31 (second %2)))))) + 0 (map list char-codes (range))))) diff --git a/src/NoteHub/views/pages.clj b/src/NoteHub/views/pages.clj index dbb2567..2323ce0 100644 --- a/src/NoteHub/views/pages.clj +++ b/src/NoteHub/views/pages.clj @@ -1,12 +1,15 @@ (ns NoteHub.views.pages (:require [NoteHub.views.common :as common]) + (:require [NoteHub.crossover.lib :as nh]) (:use [NoteHub.storage] [clojure.string :rename {replace sreplace} :only [split replace lower-case]] [clojure.core.incubator :only [-?>]] [hiccup.form] + [noir.session :only [flash-put! flash-get]] [noir.response :only [redirect]] [noir.core :only [defpage render]] + [noir.util.crypt :only [encrypt]] [noir.statuses] [noir.fetch.remotes]) (:import @@ -20,13 +23,16 @@ (defremote md-to-html [draft] (.markdownToHtml (PegDownProcessor.) draft)) -; Template for the 404 error -(set-page! 404 - (let [message "Page Not Found."] +; Template for the error sites +(defn page-setter [code message] + (set-page! code (common/layout message [:article [:h1 message]]))) +(page-setter 404 "Nothing Found.") +(page-setter 400 "Bad request.") + ; Routes ; ====== @@ -44,9 +50,12 @@ (common/layout "New Markdown Note" [:div.central-element (form-to [:post "/post-note"] - (text-area {:class "max-width"} :draft) + (hidden-field :session-key (let [k (encrypt (str (rand-int Integer/MAX_VALUE)))] + (do (flash-put! k true) (print-str k)))) + (hidden-field {:id :session-value} :session-value) + (text-area {:class :max-width} :draft) [:div#buttons.hidden - (submit-button {:style "float: left" :class "button"} "Publish") + (submit-button {:style "float: left" :class :button :id :publish-button} "Publish") [:button#preview-button.button {:type :button :style "float: right"} "Preview"]])] [:div#preview-start-line.hidden] [:article#preview])) @@ -60,10 +69,10 @@ (common/layout title [:article (md-to-html post)]) - (get-page 400)))) + (get-page 404)))) ; New Note Posting -(defpage [:post "/post-note"] {:keys [draft]} +(defpage [:post "/post-note"] {:keys [draft session-key session-value]} (let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %))) {Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}) untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %)) @@ -75,6 +84,18 @@ title (first (drop-while #(note-exists? date %) (cons proposed-title (map #(str proposed-title "-" (+ 2 %)) (range)))))] - (do - (set-note date title draft) - (redirect (apply str (interpose "/" ["" year month day title])))))) + ; check whether the new note can be added + (let [valid-session (flash-get session-key) ; it was posted from a newly generated form + valid-draft (not (empty? draft)) ; the note is non-empty + valid-hash (= (Short/parseShort session-value) ; the hash code is correct + (nh/hash #(.codePointAt % 0) (str draft session-key)))] + (do + (println "session:" valid-session "draft:" valid-draft "hash:" + (Short/parseShort session-value) + (nh/hash #(.codePointAt % 0) (str draft session-key))) + (if (and valid-session valid-draft valid-hash) + (do + (set-note date title draft) + ; TODO: the redirect is broken if title contains UTF chars + (redirect (apply str (interpose "/" ["" year month day title])))) + (get-page 400))))))