]> gitweb.factorcode.org Git - factor.git/commitdiff
Merge branch 'master' of git://onigirihouse.com/git/yuuki
authorMatthew Willis <matthew.willis@mac.com>
Mon, 2 Jun 2008 23:38:43 +0000 (16:38 -0700)
committerMatthew Willis <matthew.willis@mac.com>
Mon, 2 Jun 2008 23:38:43 +0000 (16:38 -0700)
152 files changed:
core/io/files/files.factor
extra/cairo/gadgets/gadgets.factor
extra/furnace/actions/actions-tests.factor [new file with mode: 0755]
extra/furnace/actions/actions.factor [new file with mode: 0755]
extra/furnace/auth/auth.factor [new file with mode: 0755]
extra/furnace/auth/basic/basic.factor [new file with mode: 0755]
extra/furnace/auth/login/boilerplate.xml [new file with mode: 0644]
extra/furnace/auth/login/edit-profile.xml [new file with mode: 0644]
extra/furnace/auth/login/login-tests.factor [new file with mode: 0755]
extra/furnace/auth/login/login.factor [new file with mode: 0755]
extra/furnace/auth/login/login.xml [new file with mode: 0644]
extra/furnace/auth/login/recover-1.xml [new file with mode: 0644]
extra/furnace/auth/login/recover-2.xml [new file with mode: 0644]
extra/furnace/auth/login/recover-3.xml [new file with mode: 0644]
extra/furnace/auth/login/recover-4.xml [new file with mode: 0755]
extra/furnace/auth/login/register.xml [new file with mode: 0644]
extra/furnace/auth/providers/assoc/assoc-tests.factor [new file with mode: 0755]
extra/furnace/auth/providers/assoc/assoc.factor [new file with mode: 0755]
extra/furnace/auth/providers/db/db-tests.factor [new file with mode: 0755]
extra/furnace/auth/providers/db/db.factor [new file with mode: 0755]
extra/furnace/auth/providers/null/null.factor [new file with mode: 0755]
extra/furnace/auth/providers/providers.factor [new file with mode: 0755]
extra/furnace/boilerplate/boilerplate.factor [new file with mode: 0644]
extra/furnace/db/db-tests.factor [new file with mode: 0644]
extra/furnace/db/db.factor [new file with mode: 0755]
extra/furnace/flows/flows.factor [new file with mode: 0644]
extra/furnace/furnace-tests.factor [new file with mode: 0644]
extra/furnace/furnace.factor [new file with mode: 0644]
extra/furnace/json/json.factor [new file with mode: 0644]
extra/furnace/rss/rss.factor [new file with mode: 0644]
extra/furnace/sessions/authors.txt [new file with mode: 0755]
extra/furnace/sessions/sessions-tests.factor [new file with mode: 0755]
extra/furnace/sessions/sessions.factor [new file with mode: 0755]
extra/html/components/components-tests.factor
extra/html/components/components.factor
extra/html/elements/elements.factor
extra/html/parser/analyzer/analyzer.factor
extra/html/templates/chloe/chloe-tests.factor
extra/html/templates/chloe/chloe.factor
extra/html/templates/chloe/syntax/syntax.factor [new file with mode: 0644]
extra/html/templates/chloe/test/test10.xml [deleted file]
extra/html/templates/chloe/test/test11.xml [deleted file]
extra/html/templates/chloe/test/test6.xml
extra/html/templates/chloe/test/test7.xml
extra/html/templates/chloe/test/test8.xml
extra/html/templates/chloe/test/test9.xml
extra/html/templates/templates.factor
extra/http/client/client-tests.factor
extra/http/client/client.factor
extra/http/http-tests.factor
extra/http/http.factor
extra/http/mime/authors.txt [deleted file]
extra/http/mime/mime.factor [deleted file]
extra/http/server/actions/actions-tests.factor [deleted file]
extra/http/server/actions/actions.factor [deleted file]
extra/http/server/auth/auth.factor [deleted file]
extra/http/server/auth/basic/basic.factor [deleted file]
extra/http/server/auth/login/boilerplate.xml [deleted file]
extra/http/server/auth/login/edit-profile.xml [deleted file]
extra/http/server/auth/login/login-tests.factor [deleted file]
extra/http/server/auth/login/login.factor [deleted file]
extra/http/server/auth/login/login.xml [deleted file]
extra/http/server/auth/login/recover-1.xml [deleted file]
extra/http/server/auth/login/recover-2.xml [deleted file]
extra/http/server/auth/login/recover-3.xml [deleted file]
extra/http/server/auth/login/recover-4.xml [deleted file]
extra/http/server/auth/login/register.xml [deleted file]
extra/http/server/auth/providers/assoc/assoc-tests.factor [deleted file]
extra/http/server/auth/providers/assoc/assoc.factor [deleted file]
extra/http/server/auth/providers/db/db-tests.factor [deleted file]
extra/http/server/auth/providers/db/db.factor [deleted file]
extra/http/server/auth/providers/null/null.factor [deleted file]
extra/http/server/auth/providers/providers.factor [deleted file]
extra/http/server/boilerplate/boilerplate.factor [deleted file]
extra/http/server/callbacks/callbacks-tests.factor [deleted file]
extra/http/server/callbacks/callbacks.factor [deleted file]
extra/http/server/cgi/cgi.factor
extra/http/server/db/db-tests.factor [deleted file]
extra/http/server/db/db.factor [deleted file]
extra/http/server/dispatchers/dispatchers-tests.factor [new file with mode: 0644]
extra/http/server/dispatchers/dispatchers.factor [new file with mode: 0644]
extra/http/server/filters/filters.factor [new file with mode: 0644]
extra/http/server/flows/flows.factor [deleted file]
extra/http/server/redirection/redirection-tests.factor [new file with mode: 0644]
extra/http/server/redirection/redirection.factor [new file with mode: 0644]
extra/http/server/responses/responses.factor [new file with mode: 0644]
extra/http/server/server-tests.factor [deleted file]
extra/http/server/server.factor
extra/http/server/sessions/authors.txt [deleted file]
extra/http/server/sessions/sessions-tests.factor [deleted file]
extra/http/server/sessions/sessions.factor [deleted file]
extra/http/server/static/static.factor
extra/io/pools/pools.factor
extra/lcs/diff2html/diff2html.factor
extra/mime-types/authors.txt [new file with mode: 0755]
extra/mime-types/mime-types-tests.factor [new file with mode: 0644]
extra/mime-types/mime-types.factor [new file with mode: 0755]
extra/mime-types/mime.types [new file with mode: 0644]
extra/opengl/gadgets/gadgets.factor
extra/pango/cairo/cairo.factor
extra/pango/cairo/gadgets/gadgets.factor
extra/pango/cairo/samples/samples.factor [new file with mode: 0644]
extra/rss/rss.factor
extra/tangle/tangle.factor
extra/unicode/collation/collation-docs.factor
extra/unicode/collation/collation-tests.factor
extra/unicode/collation/collation.factor
extra/urls/urls-tests.factor
extra/urls/urls.factor
extra/webapps/counter/counter.factor
extra/webapps/factor-website/factor-website.factor
extra/webapps/factor-website/page.xml
extra/webapps/pastebin/paste.xml
extra/webapps/pastebin/pastebin-common.xml
extra/webapps/pastebin/pastebin.factor
extra/webapps/pastebin/pastebin.xml
extra/webapps/planet/admin.xml
extra/webapps/planet/entry-summary.xml
extra/webapps/planet/entry.xml
extra/webapps/planet/mini-planet.xml
extra/webapps/planet/planet-common.xml
extra/webapps/planet/planet.factor
extra/webapps/planet/planet.xml
extra/webapps/todo/edit-todo.xml
extra/webapps/todo/new-todo.xml [new file with mode: 0644]
extra/webapps/todo/todo-list.xml
extra/webapps/todo/todo.factor
extra/webapps/todo/todo.xml
extra/webapps/user-admin/edit-user.xml
extra/webapps/user-admin/new-user.xml
extra/webapps/user-admin/user-admin.factor
extra/webapps/user-admin/user-admin.xml
extra/webapps/user-admin/user-list.xml
extra/webapps/wiki/articles.xml
extra/webapps/wiki/changes.xml
extra/webapps/wiki/diff.xml
extra/webapps/wiki/edit.xml
extra/webapps/wiki/page-common.xml [new file with mode: 0644]
extra/webapps/wiki/revisions.xml
extra/webapps/wiki/user-edits.xml
extra/webapps/wiki/view.xml
extra/webapps/wiki/wiki-common.xml
extra/webapps/wiki/wiki.css
extra/webapps/wiki/wiki.factor
extra/xmode/code2html/code2html.factor
extra/xmode/code2html/responder/responder.factor
extra/yahoo/authors.txt
extra/yahoo/summary.txt
extra/yahoo/yahoo-tests.factor
extra/yahoo/yahoo.factor
unmaintained/cont-responder/callbacks-tests.factor [new file with mode: 0755]
unmaintained/cont-responder/callbacks.factor [new file with mode: 0755]

index 87e927304b35ed70b61d8a3c23ec73cec0bab11a..ff265e43b16df39cb6c93c8f9f8a4e5742c8df44 100755 (executable)
@@ -147,6 +147,9 @@ PRIVATE>
         ] if
     ] unless ;
 
+: file-extension ( filename -- extension )
+    "." last-split1 nip ;
+
 ! File info
 TUPLE: file-info type size permissions modified ;
 
index 69252f83037a9d0f172c2976ee1fdcf58e76c1a7..b42c47d79b444e786b88f1502377158aaa86f7e3 100644 (file)
@@ -22,8 +22,10 @@ TUPLE: cairo-gadget < texture-gadget quot ;
         swap >>quot
         swap >>dim ;
 
-M: cairo-gadget graft* ( gadget -- )
-    GL_BGRA >>format dup
+M: cairo-gadget format>> drop GL_BGRA ;
+
+M: cairo-gadget render* ( gadget -- )
+    dup
     [ dim>> 2^-bounds ] [ quot>> copy-cairo ] bi
     >>bytes call-next-method ;
 
diff --git a/extra/furnace/actions/actions-tests.factor b/extra/furnace/actions/actions-tests.factor
new file mode 100755 (executable)
index 0000000..8aa0f92
--- /dev/null
@@ -0,0 +1,23 @@
+USING: kernel furnace.actions validators
+tools.test math math.parser multiline namespaces http
+io.streams.string http.server sequences splitting accessors ;
+IN: furnace.actions.tests
+
+<action>
+    [ "a" param "b" param [ string>number ] bi@ + ] >>display
+"action-1" set
+
+: lf>crlf "\n" split "\r\n" join ;
+
+STRING: action-request-test-1
+GET http://foo/bar?a=12&b=13 HTTP/1.1
+
+blah
+;
+
+[ 25 ] [
+    action-request-test-1 lf>crlf
+    [ read-request ] with-string-reader
+    init-request
+    { } "action-1" get call-responder
+] unit-test
diff --git a/extra/furnace/actions/actions.factor b/extra/furnace/actions/actions.factor
new file mode 100755 (executable)
index 0000000..5e237b0
--- /dev/null
@@ -0,0 +1,101 @@
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors sequences kernel assocs combinators\r
+validators http hashtables namespaces fry continuations locals\r
+io arrays math boxes\r
+xml.entities\r
+http.server\r
+http.server.responses\r
+furnace\r
+html.elements\r
+html.components\r
+html.templates.chloe\r
+html.templates.chloe.syntax ;\r
+IN: furnace.actions\r
+\r
+SYMBOL: params\r
+\r
+SYMBOL: rest-param\r
+\r
+: render-validation-messages ( -- )\r
+    validation-messages get\r
+    dup empty? [ drop ] [\r
+        <ul "errors" =class ul>\r
+            [ <li> message>> escape-string write </li> ] each\r
+        </ul>\r
+    ] if ;\r
+\r
+CHLOE: validation-messages drop render-validation-messages ;\r
+\r
+TUPLE: action rest-param init display validate submit ;\r
+\r
+: new-action ( class -- action )\r
+    new\r
+        [ ] >>init\r
+        [ <400> ] >>display\r
+        [ ] >>validate\r
+        [ <400> ] >>submit ;\r
+\r
+: <action> ( -- action )\r
+    action new-action ;\r
+\r
+: handle-get ( action -- response )\r
+    blank-values\r
+    [ init>> call ]\r
+    [ display>> call ]\r
+    bi ;\r
+\r
+: validation-failed ( -- * )\r
+    request get method>> "POST" =\r
+    [ action get display>> call ] [ <400> ] if exit-with ;\r
+\r
+: handle-post ( action -- response )\r
+    init-validation\r
+    blank-values\r
+    [ validate>> call ]\r
+    [ submit>> call ] bi ;\r
+\r
+: handle-rest-param ( arg -- )\r
+    dup length 1 > action get rest-param>> not or\r
+    [ <404> exit-with ] [\r
+        action get rest-param>> associate rest-param set\r
+    ] if ;\r
+\r
+M: action call-responder* ( path action -- response )\r
+    dup action set\r
+    '[\r
+        , dup empty? [ drop ] [ handle-rest-param ] if\r
+\r
+        init-validation\r
+        ,\r
+        request get\r
+        [ request-params rest-param get assoc-union params set ]\r
+        [ method>> ] bi\r
+        {\r
+            { "GET" [ handle-get ] }\r
+            { "HEAD" [ handle-get ] }\r
+            { "POST" [ handle-post ] }\r
+        } case\r
+    ] with-exit-continuation ;\r
+\r
+: param ( name -- value )\r
+    params get at ;\r
+\r
+: check-validation ( -- )\r
+    validation-failed? [ validation-failed ] when ;\r
+\r
+: validate-params ( validators -- )\r
+    params get swap validate-values from-object\r
+    check-validation ;\r
+\r
+: validate-integer-id ( -- )\r
+    { { "id" [ v-number ] } } validate-params ;\r
+\r
+TUPLE: page-action < action template ;\r
+\r
+: <chloe-content> ( path -- response )\r
+    resolve-template-path <chloe> "text/html" <content> ;\r
+\r
+: <page-action> ( -- page )\r
+    page-action new-action\r
+        dup '[ , template>> <chloe-content> ] >>display ;\r
diff --git a/extra/furnace/auth/auth.factor b/extra/furnace/auth/auth.factor
new file mode 100755 (executable)
index 0000000..f78cea3
--- /dev/null
@@ -0,0 +1,43 @@
+! Copyright (c) 2008 Slava Pestov\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors assocs namespaces kernel sequences sets\r
+http.server\r
+http.server.filters\r
+http.server.dispatchers\r
+furnace.sessions\r
+furnace.auth.providers ;\r
+IN: furnace.auth\r
+\r
+SYMBOL: logged-in-user\r
+\r
+GENERIC: init-user-profile ( responder -- )\r
+\r
+M: object init-user-profile drop ;\r
+\r
+M: dispatcher init-user-profile\r
+    default>> init-user-profile ;\r
+\r
+M: filter-responder init-user-profile\r
+    responder>> init-user-profile ;\r
+\r
+: profile ( -- assoc ) logged-in-user get profile>> ;\r
+\r
+: user-changed ( -- )\r
+    logged-in-user get t >>changed? drop ;\r
+\r
+: uget ( key -- value )\r
+    profile at ;\r
+\r
+: uset ( value key -- )\r
+    profile set-at\r
+    user-changed ;\r
+\r
+: uchange ( quot key -- )\r
+    profile swap change-at\r
+    user-changed ; inline\r
+\r
+SYMBOL: capabilities\r
+\r
+V{ } clone capabilities set-global\r
+\r
+: define-capability ( word -- ) capabilities get adjoin ;\r
diff --git a/extra/furnace/auth/basic/basic.factor b/extra/furnace/auth/basic/basic.factor
new file mode 100755 (executable)
index 0000000..c8d542c
--- /dev/null
@@ -0,0 +1,41 @@
+! Copyright (c) 2007 Chris Double.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors quotations assocs kernel splitting\r
+base64 html.elements io combinators sequences\r
+http http.server.filters http.server.responses http.server\r
+furnace.auth.providers furnace.auth.login ;\r
+IN: furnace.auth.basic\r
+\r
+TUPLE: basic-auth < filter-responder realm provider ;\r
+\r
+C: <basic-auth> basic-auth\r
+\r
+: authorization-ok? ( provider header -- ? )\r
+    #! Given the realm and the 'Authorization' header,\r
+    #! authenticate the user.\r
+    dup [\r
+        " " split1 swap "Basic" = [\r
+            base64> ":" split1 spin check-login\r
+        ] [\r
+            2drop f\r
+        ] if\r
+    ] [\r
+        2drop f\r
+    ] if ;\r
+\r
+: <401> ( realm -- response )\r
+    401 "Unauthorized" <trivial-response>\r
+    "Basic realm=\"" rot "\"" 3append\r
+    "WWW-Authenticate" set-header\r
+    [\r
+        <html> <body>\r
+            "Username or Password is invalid" write\r
+        </body> </html>\r
+    ] >>body ;\r
+\r
+: logged-in? ( request responder -- ? )\r
+    provider>> swap "authorization" header authorization-ok? ;\r
+\r
+M: basic-auth call-responder* ( request path responder -- response )\r
+    pick over logged-in?\r
+    [ call-next-method ] [ 2nip realm>> <401> ] if ;\r
diff --git a/extra/furnace/auth/login/boilerplate.xml b/extra/furnace/auth/login/boilerplate.xml
new file mode 100644 (file)
index 0000000..edc8c32
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <h1><t:write-title /></h1>
+
+       <t:call-next-template />
+
+</t:chloe>
diff --git a/extra/furnace/auth/login/edit-profile.xml b/extra/furnace/auth/login/edit-profile.xml
new file mode 100644 (file)
index 0000000..6beaf5d
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Edit Profile</t:title>
+
+       <t:form t:action="$login/edit-profile">
+
+       <table>
+       
+       <tr>
+               <th class="field-label">User name:</th>
+               <td><t:label t:name="username" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Real name:</th>
+               <td><t:field t:name="realname" /></td>
+       </tr>
+       
+       <tr>
+               <td></td>
+               <td>Specifying a real name is optional.</td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Current password:</th>
+               <td><t:password t:name="password" /></td>
+       </tr>
+       
+       <tr>
+               <td></td>
+               <td>If you don't want to change your current password, leave this field blank.</td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">New password:</th>
+               <td><t:password t:name="new-password" /></td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">Verify:</th>
+               <td><t:password t:name="verify-password" /></td>
+       </tr>
+       
+       <tr>
+               <td></td>
+               <td>If you are changing your password, enter it twice to ensure it is correct.</td>
+       </tr>
+       
+       <tr>
+               <th class="field-label">E-mail:</th>
+               <td><t:field t:name="email" /></td>
+       </tr>
+       
+       <tr>
+               <td></td>
+               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
+       </tr>
+       
+       </table>
+
+       <p>
+               <input type="submit" value="Update" />
+               <t:validation-messages />
+       </p>
+
+       </t:form>
+       
+</t:chloe>
diff --git a/extra/furnace/auth/login/login-tests.factor b/extra/furnace/auth/login/login-tests.factor
new file mode 100755 (executable)
index 0000000..5095ebd
--- /dev/null
@@ -0,0 +1,6 @@
+IN: furnace.auth.login.tests\r
+USING: tools.test furnace.auth.login ;\r
+\r
+\ <login> must-infer\r
+\ allow-registration must-infer\r
+\ allow-password-recovery must-infer\r
diff --git a/extra/furnace/auth/login/login.factor b/extra/furnace/auth/login/login.factor
new file mode 100755 (executable)
index 0000000..58ab47e
--- /dev/null
@@ -0,0 +1,364 @@
+! Copyright (c) 2008 Slava Pestov\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors quotations assocs kernel splitting\r
+combinators sequences namespaces hashtables sets\r
+fry arrays threads qualified random validators\r
+io\r
+io.sockets\r
+io.encodings.utf8\r
+io.encodings.string\r
+io.binary\r
+continuations\r
+destructors\r
+checksums\r
+checksums.sha2\r
+validators\r
+html.components\r
+html.elements\r
+urls\r
+http\r
+http.server\r
+http.server.dispatchers\r
+http.server.filters\r
+http.server.responses\r
+furnace\r
+furnace.auth\r
+furnace.auth.providers\r
+furnace.auth.providers.db\r
+furnace.actions\r
+furnace.flows\r
+furnace.sessions\r
+furnace.boilerplate ;\r
+QUALIFIED: smtp\r
+IN: furnace.auth.login\r
+\r
+TUPLE: login < dispatcher users checksum ;\r
+\r
+: users ( -- provider )\r
+    login get users>> ;\r
+\r
+: encode-password ( string salt -- bytes )\r
+    [ utf8 encode ] [ 4 >be ] bi* append\r
+    login get checksum>> checksum-bytes ;\r
+\r
+: >>encoded-password ( user string -- user )\r
+    32 random-bits [ encode-password ] keep\r
+    [ >>password ] [ >>salt ] bi* ; inline\r
+\r
+: valid-login? ( password user -- ? )\r
+    [ salt>> encode-password ] [ password>> ] bi = ;\r
+\r
+: check-login ( password username -- user/f )\r
+    users get-user dup [ [ valid-login? ] keep and ] [ 2drop f ] if ;\r
+\r
+! Destructor\r
+TUPLE: user-saver user ;\r
+\r
+C: <user-saver> user-saver\r
+\r
+M: user-saver dispose\r
+    user>> dup changed?>> [ users update-user ] [ drop ] if ;\r
+\r
+: save-user-after ( user -- )\r
+    <user-saver> &dispose drop ;\r
+\r
+! ! ! Login\r
+: successful-login ( user -- response )\r
+    username>> set-uid URL" $login" end-flow ;\r
+\r
+: login-failed ( -- * )\r
+    "invalid username or password" validation-error\r
+    validation-failed ;\r
+\r
+: <login-action> ( -- action )\r
+    <page-action>\r
+        { login "login" } >>template\r
+\r
+        [\r
+            {\r
+                { "username" [ v-required ] }\r
+                { "password" [ v-required ] }\r
+            } validate-params\r
+\r
+            "password" value\r
+            "username" value check-login\r
+            [ successful-login ] [ login-failed ] if*\r
+        ] >>submit ;\r
+\r
+! ! ! New user registration\r
+\r
+: user-exists ( -- * )\r
+    "username taken" validation-error\r
+    validation-failed ;\r
+\r
+: password-mismatch ( -- * )\r
+    "passwords do not match" validation-error\r
+    validation-failed ;\r
+\r
+: same-password-twice ( -- )\r
+    "new-password" value "verify-password" value =\r
+    [ password-mismatch ] unless ;\r
+\r
+: <register-action> ( -- action )\r
+    <page-action>\r
+        { login "register" } >>template\r
+\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "realname" [ [ v-one-line ] v-optional ] }\r
+                { "new-password" [ v-password ] }\r
+                { "verify-password" [ v-password ] }\r
+                { "email" [ [ v-email ] v-optional ] }\r
+                { "captcha" [ v-captcha ] }\r
+            } validate-params\r
+\r
+            same-password-twice\r
+        ] >>validate\r
+\r
+        [\r
+            "username" value <user>\r
+                "realname" value >>realname\r
+                "new-password" value >>encoded-password\r
+                "email" value >>email\r
+                H{ } clone >>profile\r
+\r
+            users new-user [ user-exists ] unless*\r
+\r
+            login get init-user-profile\r
+\r
+            successful-login\r
+        ] >>submit ;\r
+\r
+! ! ! Editing user profile\r
+\r
+: <edit-profile-action> ( -- action )\r
+    <page-action>\r
+        [\r
+            logged-in-user get\r
+            [ username>> "username" set-value ]\r
+            [ realname>> "realname" set-value ]\r
+            [ email>> "email" set-value ]\r
+            tri\r
+        ] >>init\r
+\r
+        { login "edit-profile" } >>template\r
+\r
+        [\r
+            uid "username" set-value\r
+\r
+            {\r
+                { "realname" [ [ v-one-line ] v-optional ] }\r
+                { "password" [ ] }\r
+                { "new-password" [ [ v-password ] v-optional ] }\r
+                { "verify-password" [ [ v-password ] v-optional ] } \r
+                { "email" [ [ v-email ] v-optional ] }\r
+            } validate-params\r
+\r
+            { "password" "new-password" "verify-password" }\r
+            [ value empty? not ] contains? [\r
+                "password" value uid check-login\r
+                [ "incorrect password" validation-error ] unless\r
+\r
+                same-password-twice\r
+            ] when\r
+        ] >>validate\r
+\r
+        [\r
+            logged-in-user get\r
+\r
+            "new-password" value dup empty?\r
+            [ drop ] [ >>encoded-password ] if\r
+\r
+            "realname" value >>realname\r
+            "email" value >>email\r
+\r
+            t >>changed?\r
+\r
+            drop\r
+\r
+            URL" $login" end-flow\r
+        ] >>submit ;\r
+\r
+! ! ! Password recovery\r
+\r
+SYMBOL: lost-password-from\r
+\r
+: current-host ( -- string )\r
+    request get url>> host>> host-name or ;\r
+\r
+: new-password-url ( user -- url )\r
+    "recover-3"\r
+    swap [\r
+        [ username>> "username" set ]\r
+        [ ticket>> "ticket" set ]\r
+        bi\r
+    ] H{ } make-assoc\r
+    derive-url ;\r
+\r
+: password-email ( user -- email )\r
+    smtp:<email>\r
+        [ "[ " % current-host % " ] password recovery" % ] "" make >>subject\r
+        lost-password-from get >>from\r
+        over email>> 1array >>to\r
+        [\r
+            "This e-mail was sent by the application server on " % current-host % "\n" %\r
+            "because somebody, maybe you, clicked on a ``recover password'' link in the\n" %\r
+            "login form, and requested a new password for the user named ``" %\r
+            over username>> % "''.\n" %\r
+            "\n" %\r
+            "If you believe that this request was legitimate, you may click the below link in\n" %\r
+            "your browser to set a new password for your account:\n" %\r
+            "\n" %\r
+            swap new-password-url %\r
+            "\n\n" %\r
+            "Love,\n" %\r
+            "\n" %\r
+            "  FactorBot\n" %\r
+        ] "" make >>body ;\r
+\r
+: send-password-email ( user -- )\r
+    '[ , password-email smtp:send-email ]\r
+    "E-mail send thread" spawn drop ;\r
+\r
+: <recover-action-1> ( -- action )\r
+    <page-action>\r
+        { login "recover-1" } >>template\r
+\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "email" [ v-email ] }\r
+                { "captcha" [ v-captcha ] }\r
+            } validate-params\r
+        ] >>validate\r
+\r
+        [\r
+            "email" value "username" value\r
+            users issue-ticket [\r
+                send-password-email\r
+            ] when*\r
+\r
+            URL" $login/recover-2" <redirect>\r
+        ] >>submit ;\r
+\r
+: <recover-action-2> ( -- action )\r
+    <page-action>\r
+        { login "recover-2" } >>template ;\r
+\r
+: <recover-action-3> ( -- action )\r
+    <page-action>\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "ticket" [ v-required ] }\r
+            } validate-params\r
+        ] >>init\r
+\r
+        { login "recover-3" } >>template\r
+\r
+        [\r
+            {\r
+                { "username" [ v-username ] }\r
+                { "ticket" [ v-required ] }\r
+                { "new-password" [ v-password ] }\r
+                { "verify-password" [ v-password ] }\r
+            } validate-params\r
+\r
+            same-password-twice\r
+        ] >>validate\r
+\r
+        [\r
+            "ticket" value\r
+            "username" value\r
+            users claim-ticket [\r
+                "new-password" value >>encoded-password\r
+                users update-user\r
+\r
+                URL" $login/recover-4" <redirect>\r
+            ] [\r
+                <403>\r
+            ] if*\r
+        ] >>submit ;\r
+\r
+: <recover-action-4> ( -- action )\r
+    <page-action>\r
+        { login "recover-4" } >>template ;\r
+\r
+! ! ! Logout\r
+: <logout-action> ( -- action )\r
+    <action>\r
+        [\r
+            f set-uid\r
+            URL" $login" end-flow\r
+        ] >>submit ;\r
+\r
+! ! ! Authentication logic\r
+\r
+TUPLE: protected < filter-responder capabilities ;\r
+\r
+C: <protected> protected\r
+\r
+: show-login-page ( -- response )\r
+    begin-flow\r
+    URL" $login/login" <redirect> ;\r
+\r
+: check-capabilities ( responder user -- ? )\r
+    [ capabilities>> ] bi@ subset? ;\r
+\r
+M: protected call-responder* ( path responder -- response )\r
+    uid dup [\r
+        users get-user 2dup check-capabilities [\r
+            [ logged-in-user set ] [ save-user-after ] bi\r
+            call-next-method\r
+        ] [\r
+            3drop show-login-page\r
+        ] if\r
+    ] [\r
+        3drop show-login-page\r
+    ] if ;\r
+\r
+M: login call-responder* ( path responder -- response )\r
+    dup login set\r
+    call-next-method ;\r
+\r
+: <login-boilerplate> ( responder -- responder' )\r
+    <boilerplate>\r
+        { login "boilerplate" } >>template ;\r
+\r
+: <login> ( responder -- auth )\r
+    login new-dispatcher\r
+        swap >>default\r
+        <login-action> <login-boilerplate> "login" add-responder\r
+        <logout-action> <login-boilerplate> "logout" add-responder\r
+        users-in-db >>users\r
+        sha-256 >>checksum ;\r
+\r
+! ! ! Configuration\r
+\r
+: allow-edit-profile ( login -- login )\r
+    <edit-profile-action> f <protected> <login-boilerplate>\r
+        "edit-profile" add-responder ;\r
+\r
+: allow-registration ( login -- login )\r
+    <register-action> <login-boilerplate>\r
+        "register" add-responder ;\r
+\r
+: allow-password-recovery ( login -- login )\r
+    <recover-action-1> <login-boilerplate>\r
+        "recover-password" add-responder\r
+    <recover-action-2> <login-boilerplate>\r
+        "recover-2" add-responder\r
+    <recover-action-3> <login-boilerplate>\r
+        "recover-3" add-responder\r
+    <recover-action-4> <login-boilerplate>\r
+        "recover-4" add-responder ;\r
+\r
+: allow-edit-profile? ( -- ? )\r
+    login get responders>> "edit-profile" swap key? ;\r
+\r
+: allow-registration? ( -- ? )\r
+    login get responders>> "register" swap key? ;\r
+\r
+: allow-password-recovery? ( -- ? )\r
+    login get responders>> "recover-password" swap key? ;\r
diff --git a/extra/furnace/auth/login/login.xml b/extra/furnace/auth/login/login.xml
new file mode 100644 (file)
index 0000000..a52aed5
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Login</t:title>
+
+       <t:form t:action="login">
+
+               <table>
+
+                       <tr>
+                               <th class="field-label">User name:</th>
+                               <td><t:field t:name="username" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Password:</th>
+                               <td><t:password t:name="password" /></td>
+                       </tr>
+
+               </table>
+
+               <p>
+
+                       <input type="submit" value="Log in" />
+                       <t:validation-messages />
+
+               </p>
+
+       </t:form>
+
+       <p>
+               <t:if t:code="furnace.auth.login:allow-registration?">
+                       <t:a t:href="register">Register</t:a>
+               </t:if>
+               |
+               <t:if t:code="furnace.auth.login:allow-password-recovery?">
+                       <t:a t:href="recover-password">Recover Password</t:a>
+               </t:if>
+       </p>
+
+</t:chloe>
diff --git a/extra/furnace/auth/login/recover-1.xml b/extra/furnace/auth/login/recover-1.xml
new file mode 100644 (file)
index 0000000..21fbe6f
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Recover lost password: step 1 of 4</t:title>
+
+       <p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
+
+       <t:form t:action="recover-password">
+
+               <table>
+
+                       <tr>
+                               <th class="field-label">User name:</th>
+                               <td><t:field t:name="username" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">E-mail:</th>
+                               <td><t:field t:name="email" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Captcha:</th>
+                               <td><t:field t:name="captcha" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
+                       </tr>
+
+               </table>
+
+               <input type="submit" value="Recover password" />
+
+       </t:form>
+
+</t:chloe>
diff --git a/extra/furnace/auth/login/recover-2.xml b/extra/furnace/auth/login/recover-2.xml
new file mode 100644 (file)
index 0000000..c7819bd
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Recover lost password: step 2 of 4</t:title>
+
+       <p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
+
+</t:chloe>
diff --git a/extra/furnace/auth/login/recover-3.xml b/extra/furnace/auth/login/recover-3.xml
new file mode 100644 (file)
index 0000000..2e412d1
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>Recover lost password: step 3 of 4</t:title>
+
+       <p>Choose a new password for your account.</p>
+
+       <t:form t:action="new-password">
+
+               <table>
+
+                       <t:hidden t:name="username" />
+                       <t:hidden t:name="ticket" />
+
+                       <tr>
+                               <th class="field-label">Password:</th>
+                               <td><t:password t:name="new-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Verify password:</th>
+                               <td><t:password t:name="verify-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Enter your password twice to ensure it is correct.</td>
+                       </tr>
+
+               </table>
+
+               <p>
+                       <input type="submit" value="Set password" />
+                       <t:validation-messages />
+               </p>
+
+       </t:form>
+
+</t:chloe>
diff --git a/extra/furnace/auth/login/recover-4.xml b/extra/furnace/auth/login/recover-4.xml
new file mode 100755 (executable)
index 0000000..f5d02fa
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' ?>\r
+\r
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">\r
+\r
+       <t:title>Recover lost password: step 4 of 4</t:title>\r
+\r
+       <p>Your password has been reset. You may now <t:a t:href="login">log in</t:a>.</p>\r
+\r
+</t:chloe>\r
diff --git a/extra/furnace/auth/login/register.xml b/extra/furnace/auth/login/register.xml
new file mode 100644 (file)
index 0000000..9815f21
--- /dev/null
@@ -0,0 +1,72 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>New User Registration</t:title>
+
+       <t:form t:action="register">
+
+               <table>
+
+                       <tr>
+                               <th class="field-label">User name:</th>
+                               <td><t:field t:name="username" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Real name:</th>
+                               <td><t:field t:name="realname" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Specifying a real name is optional.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Password:</th>
+                               <td><t:password t:name="new-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Verify:</th>
+                               <td><t:password t:name="verify-password" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Enter your password twice to ensure it is correct.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">E-mail:</th>
+                               <td><t:field t:name="email" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
+                       </tr>
+
+                       <tr>
+                               <th class="field-label">Captcha:</th>
+                               <td><t:field t:name="captcha" /></td>
+                       </tr>
+
+                       <tr>
+                               <td></td>
+                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
+                       </tr>
+
+               </table>
+
+               <p>
+
+                       <input type="submit" value="Register" />
+                       <t:validation-messages />
+
+               </p>
+
+       </t:form>
+
+</t:chloe>
diff --git a/extra/furnace/auth/providers/assoc/assoc-tests.factor b/extra/furnace/auth/providers/assoc/assoc-tests.factor
new file mode 100755 (executable)
index 0000000..8f9eeaa
--- /dev/null
@@ -0,0 +1,35 @@
+IN: furnace.auth.providers.assoc.tests\r
+USING: furnace.actions furnace.auth.providers \r
+furnace.auth.providers.assoc furnace.auth.login\r
+tools.test namespaces accessors kernel ;\r
+\r
+<action> <login>\r
+    <users-in-memory> >>users\r
+login set\r
+\r
+[ t ] [\r
+    "slava" <user>\r
+        "foobar" >>encoded-password\r
+        "slava@factorcode.org" >>email\r
+        H{ } clone >>profile\r
+    users new-user\r
+    username>> "slava" =\r
+] unit-test\r
+\r
+[ f ] [\r
+    "slava" <user>\r
+        H{ } clone >>profile\r
+    users new-user\r
+] unit-test\r
+\r
+[ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
+\r
+[ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
+\r
+[ t ] [ "user" get >boolean ] unit-test\r
+\r
+[ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
+\r
+[ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
+\r
+[ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
diff --git a/extra/furnace/auth/providers/assoc/assoc.factor b/extra/furnace/auth/providers/assoc/assoc.factor
new file mode 100755 (executable)
index 0000000..f5a79d7
--- /dev/null
@@ -0,0 +1,18 @@
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+IN: furnace.auth.providers.assoc\r
+USING: accessors assocs kernel furnace.auth.providers ;\r
+\r
+TUPLE: users-in-memory assoc ;\r
+\r
+: <users-in-memory> ( -- provider )\r
+    H{ } clone users-in-memory boa ;\r
+\r
+M: users-in-memory get-user ( username provider -- user/f )\r
+    assoc>> at ;\r
+\r
+M: users-in-memory update-user ( user provider -- ) 2drop ;\r
+\r
+M: users-in-memory new-user ( user provider -- user/f )\r
+    [ dup username>> ] dip assoc>>\r
+    2dup key? [ 3drop f ] [ pick [ set-at ] dip ] if ;\r
diff --git a/extra/furnace/auth/providers/db/db-tests.factor b/extra/furnace/auth/providers/db/db-tests.factor
new file mode 100755 (executable)
index 0000000..714dcb4
--- /dev/null
@@ -0,0 +1,47 @@
+IN: furnace.auth.providers.db.tests\r
+USING: furnace.actions\r
+furnace.auth.login\r
+furnace.auth.providers\r
+furnace.auth.providers.db tools.test\r
+namespaces db db.sqlite db.tuples continuations\r
+io.files accessors kernel ;\r
+\r
+<action> <login>\r
+    users-in-db >>users\r
+login set\r
+\r
+[ "auth-test.db" temp-file delete-file ] ignore-errors\r
+\r
+"auth-test.db" temp-file sqlite-db [\r
+\r
+    init-users-table\r
+\r
+    [ t ] [\r
+        "slava" <user>\r
+            "foobar" >>encoded-password\r
+            "slava@factorcode.org" >>email\r
+            H{ } clone >>profile\r
+            users new-user\r
+            username>> "slava" =\r
+    ] unit-test\r
+\r
+    [ f ] [\r
+        "slava" <user>\r
+            H{ } clone >>profile\r
+        users new-user\r
+    ] unit-test\r
+\r
+    [ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
+\r
+    [ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
+\r
+    [ t ] [ "user" get >boolean ] unit-test\r
+\r
+    [ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
+\r
+    [ ] [ "user" get users update-user ] unit-test\r
+\r
+    [ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
+\r
+    [ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
+] with-db\r
diff --git a/extra/furnace/auth/providers/db/db.factor b/extra/furnace/auth/providers/db/db.factor
new file mode 100755 (executable)
index 0000000..90306e5
--- /dev/null
@@ -0,0 +1,41 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db db.tuples db.types accessors
+furnace.auth.providers kernel continuations
+classes.singleton ;
+IN: furnace.auth.providers.db
+
+user "USERS"
+{
+    { "username" "USERNAME" { VARCHAR 256 } +user-assigned-id+ }
+    { "realname" "REALNAME" { VARCHAR 256 } }
+    { "password" "PASSWORD" BLOB +not-null+ }
+    { "salt" "SALT" INTEGER +not-null+ }
+    { "email" "EMAIL" { VARCHAR 256 } }
+    { "ticket" "TICKET" { VARCHAR 256 } }
+    { "capabilities" "CAPABILITIES" FACTOR-BLOB }
+    { "profile" "PROFILE" FACTOR-BLOB }
+    { "deleted" "DELETED" INTEGER +not-null+ }
+} define-persistent
+
+: init-users-table user ensure-table ;
+
+SINGLETON: users-in-db
+
+M: users-in-db get-user
+    drop <user> select-tuple ;
+
+M: users-in-db new-user
+    drop
+    [
+        user new
+            over username>> >>username
+        select-tuple [
+            drop f
+        ] [
+            dup insert-tuple
+        ] if
+    ] with-transaction ;
+
+M: users-in-db update-user
+    drop update-tuple ;
diff --git a/extra/furnace/auth/providers/null/null.factor b/extra/furnace/auth/providers/null/null.factor
new file mode 100755 (executable)
index 0000000..39ea812
--- /dev/null
@@ -0,0 +1,14 @@
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: furnace.auth.providers kernel ;\r
+IN: furnace.auth.providers.null\r
+\r
+TUPLE: no-users ;\r
+\r
+: no-users T{ no-users } ;\r
+\r
+M: no-users get-user 2drop f ;\r
+\r
+M: no-users new-user 2drop f ;\r
+\r
+M: no-users update-user 2drop ;\r
diff --git a/extra/furnace/auth/providers/providers.factor b/extra/furnace/auth/providers/providers.factor
new file mode 100755 (executable)
index 0000000..1933fc8
--- /dev/null
@@ -0,0 +1,50 @@
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: kernel accessors random math.parser locals\r
+sequences math ;\r
+IN: furnace.auth.providers\r
+\r
+TUPLE: user\r
+username realname\r
+password salt\r
+email ticket capabilities profile deleted changed? ;\r
+\r
+: <user> ( username -- user )\r
+    user new\r
+        swap >>username\r
+        0 >>deleted ;\r
+\r
+GENERIC: get-user ( username provider -- user/f )\r
+\r
+GENERIC: update-user ( user provider -- )\r
+\r
+GENERIC: new-user ( user provider -- user/f )\r
+\r
+! Password recovery support\r
+\r
+:: issue-ticket ( email username provider -- user/f )\r
+    [let | user [ username provider get-user ] |\r
+        user [\r
+            user email>> length 0 > [\r
+                user email>> email = [\r
+                    user\r
+                    256 random-bits >hex >>ticket\r
+                    dup provider update-user\r
+                ] [ f ] if\r
+            ] [ f ] if\r
+        ] [ f ] if\r
+    ] ;\r
+\r
+:: claim-ticket ( ticket username provider -- user/f )\r
+    [let | user [ username provider get-user ] |\r
+        user [\r
+            user ticket>> ticket = [\r
+                user f >>ticket dup provider update-user\r
+            ] [ f ] if\r
+        ] [ f ] if\r
+    ] ;\r
+\r
+! For configuration\r
+\r
+: add-user ( provider user -- provider )\r
+    over new-user [ "User exists" throw ] when ;\r
diff --git a/extra/furnace/boilerplate/boilerplate.factor b/extra/furnace/boilerplate/boilerplate.factor
new file mode 100644 (file)
index 0000000..42f132a
--- /dev/null
@@ -0,0 +1,25 @@
+! Copyright (c) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel namespaces
+html.templates html.templates.chloe
+locals
+http.server
+http.server.filters
+furnace ;
+IN: furnace.boilerplate
+
+TUPLE: boilerplate < filter-responder template ;
+
+: <boilerplate> f boilerplate boa ;
+
+M:: boilerplate call-responder* ( path responder -- )
+    path responder call-next-method
+    dup content-type>> "text/html" = [
+        clone [| body |
+            [
+                body
+                responder template>> resolve-template-path <chloe>
+                with-boilerplate
+            ]
+        ] change-body
+    ] when ;
diff --git a/extra/furnace/db/db-tests.factor b/extra/furnace/db/db-tests.factor
new file mode 100644 (file)
index 0000000..34357ae
--- /dev/null
@@ -0,0 +1,4 @@
+IN: furnace.db.tests
+USING: tools.test furnace.db ;
+
+\ <db-persistence> must-infer
diff --git a/extra/furnace/db/db.factor b/extra/furnace/db/db.factor
new file mode 100755 (executable)
index 0000000..8487b4b
--- /dev/null
@@ -0,0 +1,18 @@
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: kernel accessors continuations namespaces destructors\r
+db db.pools io.pools http.server http.server.filters\r
+furnace.sessions ;\r
+IN: furnace.db\r
+\r
+TUPLE: db-persistence < filter-responder pool ;\r
+\r
+: <db-persistence> ( responder params db -- responder' )\r
+    <db-pool> db-persistence boa ;\r
+\r
+M: db-persistence call-responder*\r
+    [\r
+        pool>> [ acquire-connection ] keep\r
+        [ return-connection-later ] [ drop db set ] 2bi\r
+    ]\r
+    [ call-next-method ] bi ;\r
diff --git a/extra/furnace/flows/flows.factor b/extra/furnace/flows/flows.factor
new file mode 100644 (file)
index 0000000..eb98c1a
--- /dev/null
@@ -0,0 +1,78 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors namespaces sequences arrays kernel
+assocs assocs.lib hashtables math.parser urls combinators
+furnace http http.server http.server.filters furnace.sessions
+html.elements html.templates.chloe.syntax ;
+IN: furnace.flows
+
+TUPLE: flows < filter-responder ;
+
+C: <flows> flows
+
+: begin-flow* ( -- id )
+    request get
+    [ url>> ] [ post-data>> ] [ method>> ] tri 3array
+    flows sget set-at-unique
+    session-changed ;
+
+: end-flow-post ( url post-data -- response )
+    request [
+        clone
+            "POST" >>method
+            swap >>post-data
+            swap >>url
+    ] change
+    request get url>> path>> split-path
+    flows get responder>> call-responder ;
+
+: end-flow* ( url id -- response )
+    flows sget at [
+        first3 {
+            { "GET" [ drop <redirect> ] }
+            { "HEAD" [ drop <redirect> ] }
+            { "POST" [ end-flow-post ] }
+        } case
+    ] [ <redirect> ] ?if ;
+
+SYMBOL: flow-id
+
+: flow-id-key "factorflowid" ;
+
+: begin-flow ( -- )
+    begin-flow* flow-id set ;
+
+: end-flow ( default -- response )
+    flow-id get end-flow* ;
+
+M: flows call-responder*
+    dup flows set
+    flow-id-key request get request-params at flow-id set
+    call-next-method ;
+
+M: flows init-session*
+    H{ } clone flows sset
+    call-next-method ;
+
+M: flows link-attr ( tag -- )
+    drop
+    "flow" optional-attr {
+        { "none" [ flow-id off ] }
+        { "begin" [ begin-flow ] }
+        { "current" [ ] }
+        { f [ ] }
+    } case ;
+
+M: flows modify-query ( query responder -- query' )
+    drop
+    flow-id get [ flow-id-key associate assoc-union ] when* ;
+
+M: flows hidden-form-field ( responder -- )
+    drop
+    flow-id get [
+        <input
+            "hidden" =type
+            flow-id-key =name
+            =value
+        input/>
+    ] when* ;
diff --git a/extra/furnace/furnace-tests.factor b/extra/furnace/furnace-tests.factor
new file mode 100644 (file)
index 0000000..5cf2dad
--- /dev/null
@@ -0,0 +1,30 @@
+IN: furnace.tests
+USING: http.server.dispatchers http.server.responses
+http.server furnace tools.test kernel namespaces accessors ;
+TUPLE: funny-dispatcher < dispatcher ;
+
+: <funny-dispatcher> funny-dispatcher new-dispatcher ;
+
+TUPLE: base-path-check-responder ;
+
+C: <base-path-check-responder> base-path-check-responder
+
+M: base-path-check-responder call-responder*
+    2drop
+    "$funny-dispatcher" resolve-base-path
+    "text/plain" <content> ;
+
+[ ] [
+    <dispatcher>
+        <dispatcher>
+            <funny-dispatcher>
+                <base-path-check-responder> "c" add-responder
+            "b" add-responder
+        "a" add-responder
+    main-responder set
+] unit-test
+
+[ "/a/b/" ] [
+    V{ } responder-nesting set
+    "a/b/c" split-path main-responder get call-responder body>>
+] unit-test
diff --git a/extra/furnace/furnace.factor b/extra/furnace/furnace.factor
new file mode 100644 (file)
index 0000000..370c4f8
--- /dev/null
@@ -0,0 +1,183 @@
+! Copyright (C) 2003, 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays kernel combinators assocs
+continuations namespaces sequences splitting words
+vocabs.loader classes
+fry urls multiline
+xml
+xml.data
+xml.writer
+xml.utilities
+html.components
+html.elements
+html.templates
+html.templates.chloe
+html.templates.chloe.syntax
+http
+http.server
+http.server.redirection
+http.server.responses
+qualified ;
+QUALIFIED-WITH: assocs a
+IN: furnace
+
+: nested-responders ( -- seq )
+    responder-nesting get a:values ;
+
+: each-responder ( quot -- )
+   nested-responders swap each ; inline
+
+: base-path ( string -- pair )
+    dup responder-nesting get
+    [ second class word-name = ] with find nip
+    [ first ] [ "No such responder: " swap append throw ] ?if ;
+
+: resolve-base-path ( string -- string' )
+    "$" ?head [
+        [
+            "/" split1 [ base-path [  "/" % % ] each "/" % ] dip %
+        ] "" make
+    ] when ;
+
+: vocab-path ( vocab -- path )
+    dup vocab-dir vocab-append-path ;
+
+: resolve-template-path ( pair -- path )
+    [
+        first2 [ word-vocabulary vocab-path % ] [ "/" % % ] bi*
+    ] "" make ;
+
+GENERIC: modify-query ( query responder -- query' )
+
+M: object modify-query drop ;
+
+: adjust-url ( url -- url' )
+    clone
+        [ [ modify-query ] each-responder ] change-query
+        [ resolve-base-path ] change-path
+    relative-to-request ;
+
+: <redirect> ( url -- response )
+    adjust-url request get method>> {
+        { "GET" [ <temporary-redirect> ] }
+        { "HEAD" [ <temporary-redirect> ] }
+        { "POST" [ <permanent-redirect> ] }
+    } case ;
+
+GENERIC: hidden-form-field ( responder -- )
+
+M: object hidden-form-field drop ;
+
+: request-params ( request -- assoc )
+    dup method>> {
+        { "GET" [ url>> query>> ] }
+        { "HEAD" [ url>> query>> ] }
+        { "POST" [ post-data>> ] }
+    } case ;
+
+SYMBOL: exit-continuation
+
+: exit-with exit-continuation get continue-with ;
+
+: with-exit-continuation ( quot -- )
+    '[ exit-continuation set @ ] callcc1 exit-continuation off ;
+
+! Chloe tags
+: parse-query-attr ( string -- assoc )
+    dup empty?
+    [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
+
+CHLOE: atom
+    [ "title" required-attr ]
+    [ "href" required-attr ]
+    [ "query" optional-attr parse-query-attr ] tri
+    <url>
+        swap >>query
+        swap >>path
+    adjust-url relative-to-request
+    add-atom-feed ;
+
+CHLOE: write-atom drop write-atom-feeds ;
+
+GENERIC: link-attr ( tag responder -- )
+
+M: object link-attr 2drop ;
+
+: link-attrs ( tag -- )
+    '[ , _ link-attr ] each-responder ;
+
+: a-start-tag ( tag -- )
+    [
+        <a
+            dup link-attrs
+            dup "value" optional-attr [ value f ] [
+                [ "href" required-attr ]
+                [ "query" optional-attr parse-query-attr ]
+                bi
+            ] ?if
+            <url>
+                swap >>query
+                swap >>path
+            adjust-url relative-to-request =href
+        a>
+    ] with-scope ;
+
+CHLOE: a
+    [ a-start-tag ]
+    [ process-tag-children ]
+    [ drop </a> ]
+    tri ;
+
+: form-start-tag ( tag -- )
+    [
+        [
+            <form
+            "POST" =method
+            [ link-attrs ]
+            [ "action" required-attr resolve-base-path =action ]
+            [ tag-attrs non-chloe-attrs-only print-attrs ]
+            tri
+            form>
+        ] [
+            [ hidden-form-field ] each-responder
+            "for" optional-attr [ hidden render ] when*
+        ] bi
+    ] with-scope ;
+
+CHLOE: form
+    [ form-start-tag ]
+    [ process-tag-children ]
+    [ drop </form> ]
+    tri ;
+
+STRING: button-tag-markup
+<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
+    <button type="submit"></button>
+</t:form>
+;
+
+: add-tag-attrs ( attrs tag -- )
+    tag-attrs swap update ;
+
+CHLOE: button
+    button-tag-markup string>xml delegate
+    {
+        [ [ tag-attrs chloe-attrs-only ] dip add-tag-attrs ]
+        [ [ tag-attrs non-chloe-attrs-only ] dip "button" tag-named add-tag-attrs ]
+        [ [ children>string 1array ] dip "button" tag-named set-tag-children ]
+        [ nip ]
+    } 2cleave process-chloe-tag ;
+
+: attr>word ( value -- word/f )
+    dup ":" split1 swap lookup
+    [ ] [ "No such word: " swap append throw ] ?if ;
+
+: attr>var ( value -- word/f )
+    attr>word dup symbol? [
+        "Must be a symbol: " swap append throw
+    ] unless ;
+
+: if-satisfied? ( tag -- ? )
+    "code" required-attr attr>word execute ;
+
+CHLOE: if dup if-satisfied? [ process-tag-children ] [ drop ] if ;
diff --git a/extra/furnace/json/json.factor b/extra/furnace/json/json.factor
new file mode 100644 (file)
index 0000000..a5188cd
--- /dev/null
@@ -0,0 +1,7 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: json.writer http.server.responses ;
+IN: furnace.json
+
+: <json-content> ( body -- response )
+    >json "application/json" <content> ;
diff --git a/extra/furnace/rss/rss.factor b/extra/furnace/rss/rss.factor
new file mode 100644 (file)
index 0000000..a94ef4f
--- /dev/null
@@ -0,0 +1,14 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel fry
+rss http.server.responses furnace.actions ;
+IN: furnace.rss
+
+: <feed-content> ( body -- response )
+    feed>xml "application/atom+xml" <content> ;
+
+TUPLE: feed-action < action feed ;
+
+: <feed-action> ( -- feed )
+    feed-action new-action
+        dup '[ , feed>> call <feed-content> ] >>display ;
diff --git a/extra/furnace/sessions/authors.txt b/extra/furnace/sessions/authors.txt
new file mode 100755 (executable)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/extra/furnace/sessions/sessions-tests.factor b/extra/furnace/sessions/sessions-tests.factor
new file mode 100755 (executable)
index 0000000..a7a663f
--- /dev/null
@@ -0,0 +1,152 @@
+IN: furnace.sessions.tests\r
+USING: tools.test http furnace.sessions\r
+furnace.actions http.server http.server.responses\r
+math namespaces kernel accessors\r
+prettyprint io.streams.string io.files splitting destructors\r
+sequences db db.sqlite continuations urls math.parser\r
+furnace ;\r
+\r
+: with-session\r
+    [\r
+        [ [ save-session-after ] [ session set ] bi ] dip call\r
+    ] with-destructors ; inline\r
+\r
+TUPLE: foo ;\r
+\r
+C: <foo> foo\r
+\r
+M: foo init-session* drop 0 "x" sset ;\r
+\r
+M: foo call-responder*\r
+    2drop\r
+    "x" [ 1+ ] schange\r
+    "x" sget number>string "text/html" <content> ;\r
+\r
+: url-responder-mock-test\r
+    [\r
+        <request>\r
+            "GET" >>method\r
+            dup url>>\r
+                "id" get session-id-key set-query-param\r
+                "/" >>path drop\r
+        init-request\r
+        { } sessions get call-responder\r
+        [ write-response-body drop ] with-string-writer\r
+    ] with-destructors ;\r
+\r
+: sessions-mock-test\r
+    [\r
+        <request>\r
+            "GET" >>method\r
+            "cookies" get >>cookies\r
+            dup url>> "/" >>path drop\r
+        init-request\r
+        { } sessions get call-responder\r
+        [ write-response-body drop ] with-string-writer\r
+    ] with-destructors ;\r
+\r
+: <exiting-action>\r
+    <action>\r
+        [ [ ] "text/plain" <content> exit-with ] >>display ;\r
+\r
+[ "auth-test.db" temp-file sqlite-db delete-file ] ignore-errors\r
+\r
+"auth-test.db" temp-file sqlite-db [\r
+\r
+    <request> init-request\r
+    init-sessions-table\r
+\r
+    [ ] [\r
+        <foo> <sessions>\r
+        sessions set\r
+    ] unit-test\r
+\r
+    [\r
+        [ ] [\r
+            empty-session\r
+                123 >>id session set\r
+        ] unit-test\r
+\r
+        [ ] [ 3 "x" sset ] unit-test\r
+\r
+        [ 9 ] [ "x" sget sq ] unit-test\r
+\r
+        [ ] [ "x" [ 1- ] schange ] unit-test\r
+\r
+        [ 4 ] [ "x" sget sq ] unit-test\r
+\r
+        [ t ] [ session get changed?>> ] unit-test\r
+    ] with-scope\r
+\r
+    [ t ] [\r
+        begin-session id>>\r
+        get-session session?\r
+    ] unit-test\r
+\r
+    [ { 5 0 } ] [\r
+        [\r
+            begin-session\r
+            dup [ 5 "a" sset ] with-session\r
+            dup [ "a" sget , ] with-session\r
+            dup [ "x" sget , ] with-session\r
+            drop\r
+        ] { } make\r
+    ] unit-test\r
+\r
+    [ 0 ] [\r
+        begin-session id>>\r
+        get-session [ "x" sget ] with-session\r
+    ] unit-test\r
+\r
+    [ { 5 0 } ] [\r
+        [\r
+            begin-session id>>\r
+            dup get-session [ 5 "a" sset ] with-session\r
+            dup get-session [ "a" sget , ] with-session\r
+            dup get-session [ "x" sget , ] with-session\r
+            drop\r
+        ] { } make\r
+    ] unit-test\r
+\r
+    [ ] [\r
+        <foo> <sessions>\r
+        sessions set\r
+    ] unit-test\r
+\r
+    [\r
+        <request>\r
+            "GET" >>method\r
+            dup url>> "/" >>path drop\r
+        request set\r
+        { "etc" } sessions get call-responder response set\r
+        [ "1" ] [ [ response get write-response-body drop ] with-string-writer ] unit-test\r
+        response get\r
+    ] with-destructors\r
+    response set\r
+\r
+    [ ] [ response get cookies>> "cookies" set ] unit-test\r
+\r
+    [ "2" ] [ sessions-mock-test ] unit-test\r
+    [ "3" ] [ sessions-mock-test ] unit-test\r
+    [ "4" ] [ sessions-mock-test ] unit-test\r
+\r
+    [\r
+        [ ] [\r
+            <request>\r
+                "GET" >>method\r
+                dup url>>\r
+                    "id" get session-id-key set-query-param\r
+                    "/" >>path drop\r
+            request set\r
+\r
+            [\r
+                { } <exiting-action> <sessions>\r
+                call-responder\r
+            ] with-destructors response set\r
+        ] unit-test\r
+\r
+        [ "text/plain" ] [ response get content-type>> ] unit-test\r
+\r
+        [ f ] [ response get cookies>> empty? ] unit-test\r
+    ] with-scope\r
+] with-db\r
diff --git a/extra/furnace/sessions/sessions.factor b/extra/furnace/sessions/sessions.factor
new file mode 100755 (executable)
index 0000000..5ea389c
--- /dev/null
@@ -0,0 +1,154 @@
+! Copyright (C) 2008 Doug Coleman, Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: assocs kernel math.intervals math.parser namespaces
+random accessors quotations hashtables sequences continuations
+fry calendar combinators destructors alarms
+db db.tuples db.types
+http http.server http.server.dispatchers http.server.filters
+html.elements furnace ;
+IN: furnace.sessions
+
+TUPLE: session id expires uid namespace changed? ;
+
+: <session> ( id -- session )
+    session new
+        swap >>id ;
+
+session "SESSIONS"
+{
+    { "id" "ID" +random-id+ system-random-generator }
+    { "expires" "EXPIRES" TIMESTAMP +not-null+ }
+    { "uid" "UID" { VARCHAR 255 } }
+    { "namespace" "NAMESPACE" FACTOR-BLOB }
+} define-persistent
+
+: get-session ( id -- session )
+    dup [ <session> select-tuple ] when ;
+
+: init-sessions-table session ensure-table ;
+
+: start-expiring-sessions ( db seq -- )
+    '[
+        , , [
+            session new
+                -1.0/0.0 now [a,b] >>expires
+            delete-tuples
+        ] with-db
+    ] 5 minutes every drop ;
+
+GENERIC: init-session* ( responder -- )
+
+M: object init-session* drop ;
+
+M: dispatcher init-session* default>> init-session* ;
+
+M: filter-responder init-session* responder>> init-session* ;
+
+TUPLE: sessions < filter-responder timeout domain ;
+
+: <sessions> ( responder -- responder' )
+    sessions new
+        swap >>responder
+        20 minutes >>timeout ;
+
+: (session-changed) ( session -- )
+    t >>changed? drop ;
+
+: session-changed ( -- )
+    session get (session-changed) ;
+
+: sget ( key -- value )
+    session get namespace>> at ;
+
+: sset ( value key -- )
+    session get
+    [ namespace>> set-at ] [ (session-changed) ] bi ;
+
+: schange ( key quot -- )
+    session get
+    [ namespace>> swap change-at ] keep
+    (session-changed) ; inline
+
+: uid ( -- uid )
+    session get uid>> ;
+
+: set-uid ( uid -- )
+    session get [ (>>uid) ] [ (session-changed) ] bi ;
+
+: init-session ( session -- )
+    session [ sessions get init-session* ] with-variable ;
+
+: cutoff-time ( -- time )
+    sessions get timeout>> from-now ;
+
+: touch-session ( session -- )
+    cutoff-time >>expires drop ;
+
+: empty-session ( -- session )
+    f <session>
+        H{ } clone >>namespace
+        dup touch-session ;
+
+: begin-session ( -- session )
+    empty-session [ init-session ] [ insert-tuple ] [ ] tri ;
+
+! Destructor
+TUPLE: session-saver session ;
+
+C: <session-saver> session-saver
+
+M: session-saver dispose
+    session>> dup changed?>> [
+        [ touch-session ] [ update-tuple ] bi
+    ] [ drop ] if ;
+
+: save-session-after ( session -- )
+    <session-saver> &dispose drop ;
+
+: existing-session ( path session -- response )
+    [ session set ] [ save-session-after ] bi
+    sessions get responder>> call-responder ;
+
+: session-id-key "factorsessid" ;
+
+: cookie-session-id ( request -- id/f )
+    session-id-key get-cookie
+    dup [ value>> string>number ] when ;
+
+: post-session-id ( request -- id/f )
+    session-id-key swap post-data>> at string>number ;
+
+: request-session-id ( -- id/f )
+    request get dup method>> {
+        { "GET" [ cookie-session-id ] }
+        { "HEAD" [ cookie-session-id ] }
+        { "POST" [ post-session-id ] }
+    } case ;
+
+: request-session ( -- session/f )
+    request-session-id get-session ;
+
+: <session-cookie> ( id -- cookie )
+    session-id-key <cookie>
+        "$sessions" resolve-base-path >>path
+        sessions get timeout>> from-now >>expires
+        sessions get domain>> >>domain ;
+
+: put-session-cookie ( response -- response' )
+    session get id>> number>string <session-cookie> put-cookie ;
+
+M: sessions hidden-form-field ( responder -- )
+    drop
+    <input
+        "hidden" =type
+        session-id-key =name
+        session get id>> number>string =value
+    input/> ;
+
+M: sessions call-responder* ( path responder -- response )
+    sessions set
+    request-session [ begin-session ] unless*
+    existing-session put-session-cookie ;
+
+: logout-all-sessions ( uid -- )
+    session new swap >>uid delete-tuples ;
index 1a0f849a8fc9191cb84816f19d432c1951854dc7..1f77768115fe4be1bfa17bef03b189bde8d85788 100644 (file)
@@ -1,7 +1,7 @@
 IN: html.components.tests
 USING: tools.test kernel io.streams.string
 io.streams.null accessors inspector html.streams
-html.components namespaces ;
+html.elements html.components namespaces ;
 
 [ ] [ blank-values ] unit-test
 
@@ -11,7 +11,7 @@ html.components namespaces ;
 
 TUPLE: color red green blue ;
 
-[ ] [ 1 2 3 color boa from-tuple ] unit-test
+[ ] [ 1 2 3 color boa from-object ] unit-test
 
 [ 1 ] [ "red" value ] unit-test
 
@@ -107,7 +107,7 @@ TUPLE: color red green blue ;
 
 [ ] [ t "delivery" set-value ] unit-test
 
-[ "<input type='checkbox' name='delivery' selected='true'>Delivery</input>" ] [
+[ "<input type='checkbox' name='delivery' checked='true'>Delivery</input>" ] [
     [
         "delivery"
         <checkbox>
index efac730af6e9272d6685f707363901341119d0a4..c013007a144b114b58e45167190b2cf98ea3e363 100644 (file)
@@ -5,7 +5,7 @@ classes.tuple words arrays sequences sequences.lib splitting
 mirrors hashtables combinators continuations math strings
 fry locals calendar calendar.format xml.entities validators
 html.elements html.streams xmode.code2html farkup inspector
-lcs.diff2html ;
+lcs.diff2html urls ;
 IN: html.components
 
 SYMBOL: values
@@ -19,9 +19,9 @@ SYMBOL: values
 : prepare-value ( name object -- value name object )
     [ [ value ] keep ] dip ; inline
 
-: from-assoc ( assoc -- ) values get swap update ;
-
-: from-tuple ( tuple -- ) <mirror> from-assoc ;
+: from-object ( object -- )
+    dup assoc? [ <mirror> ] unless
+    values get swap update ;
 
 : deposit-values ( destination names -- )
     [ dup value ] H{ } map>assoc update ;
@@ -32,24 +32,19 @@ SYMBOL: values
 : with-each-index ( seq quot -- )
     '[
         [
-            blank-values 1+ "index" set-value @
+            values [ clone ] change
+            1+ "index" set-value @
         ] with-scope
     ] each-index ; inline
 
 : with-each-value ( seq quot -- )
     '[ "value" set-value @ ] with-each-index ; inline
 
-: with-each-assoc ( seq quot -- )
-    '[ from-assoc @ ] with-each-index ; inline
-
-: with-each-tuple ( seq quot -- )
-    '[ from-tuple @ ] with-each-index ; inline
-
-: with-assoc-values ( assoc quot -- )
-    '[ blank-values , from-assoc @ ] with-scope ; inline
+: with-each-object ( seq quot -- )
+    '[ from-object @ ] with-each-index ; inline
 
-: with-tuple-values ( assoc quot -- )
-    '[ blank-values , from-tuple @ ] with-scope ; inline
+: with-values ( object quot -- )
+    '[ blank-values , from-object @ ] with-scope ; inline
 
 : nest-values ( name quot -- )
     swap [
@@ -58,22 +53,6 @@ SYMBOL: values
         ] with-scope
     ] dip set-value ; inline
 
-: nest-tuple ( name quot -- )
-    swap [
-        [
-            H{ } clone [ <mirror> values set call ] keep
-        ] with-scope
-    ] dip set-value ; inline
-
-: object>string ( object -- string )
-    {
-        { [ dup real? ] [ number>string ] }
-        { [ dup timestamp? ] [ timestamp>string ] }
-        { [ dup string? ] [ ] }
-        { [ dup word? ] [ word-name ] }
-        { [ dup not ] [ drop "" ] }
-    } cond ;
-
 GENERIC: render* ( value name render -- )
 
 : render ( name renderer -- )
@@ -174,7 +153,7 @@ M: checkbox render*
     <input
         "checkbox" =type
         swap =name
-        swap [ "true" =selected ] when
+        swap [ "true" =checked ] when
     input>
         label>> escape-string write
     </input> ;
index e5377cedf8f168dfbb65d22817d0f5189a29135e..8d92d9f4d74c076c9888290bc022c17ef06b58a0 100644 (file)
@@ -4,7 +4,8 @@
 ! See http://factorcode.org/license.txt for BSD license.
 
 USING: io kernel namespaces prettyprint quotations
-sequences strings words xml.entities compiler.units effects ;
+sequences strings words xml.entities compiler.units effects
+urls math math.parser combinators calendar calendar.format ;
 
 IN: html.elements
 
@@ -126,11 +127,22 @@ SYMBOL: html
     dup def-for-html-word-<foo
     def-for-html-word-foo/> ;
 
+: object>string ( object -- string )
+    #! Should this be generic and in the core?
+    {
+        { [ dup real? ] [ number>string ] }
+        { [ dup timestamp? ] [ timestamp>string ] }
+        { [ dup url? ] [ url>string ] }
+        { [ dup string? ] [ ] }
+        { [ dup word? ] [ word-name ] }
+        { [ dup not ] [ drop "" ] }
+    } cond ;
+
 : write-attr ( value name -- )
     " " write-html
     write-html
     "='" write-html
-    escape-quoted-string write-html
+    object>string escape-quoted-string write-html
     "'" write-html ;
 
 : attribute-effect T{ effect f { "string" } 0 } ;
@@ -162,7 +174,7 @@ SYMBOL: html
     "id" "onclick" "style" "valign" "accesskey"
     "src" "language" "colspan" "onchange" "rel"
     "width" "selected" "onsubmit" "xmlns" "lang" "xml:lang"
-    "media" "title" "multiple"
+    "media" "title" "multiple" "checked"
 ] [ define-attribute-word ] each
 
 >>
@@ -178,7 +190,7 @@ SYMBOL: html
     <html "http://www.w3.org/1999/xhtml" =xmlns "en" =xml:lang "en" =lang html>
         <head> <title> swap write </title> </head>
         <body> call </body>
-    </html> ;
+    </html> ; inline
 
 : render-error ( message -- )
     <span "error" =class span> escape-string write </span> ;
index 9ce45b5c470adb4a9647319c44f2cff9984f0ed6..47d352b6b806ba54a90111518737547db300a1ef 100755 (executable)
@@ -1,6 +1,6 @@
 USING: assocs html.parser kernel math sequences strings ascii
 arrays shuffle unicode.case namespaces splitting http
-sequences.lib accessors io combinators http.client ;
+sequences.lib accessors io combinators http.client urls ;
 IN: html.parser.analyzer
 
 TUPLE: link attributes clickable ;
index eaa0f0dc3dff310f00b96308024de5c91adafbae..d4c02061b2c5ef38c11d61308d5088a9561b66dc 100644 (file)
@@ -1,7 +1,7 @@
 USING: html.templates html.templates.chloe
 tools.test io.streams.string kernel sequences ascii boxes
 namespaces xml html.components
-splitting unicode.categories ;
+splitting unicode.categories furnace ;
 IN: html.templates.chloe.tests
 
 [ f ] [ f parse-query-attr ] unit-test
@@ -27,8 +27,7 @@ IN: html.templates.chloe.tests
 
 : test-template ( name -- template )
     "resource:extra/html/templates/chloe/test/"
-    swap
-    ".xml" 3append <chloe> ;
+    prepend <chloe> ;
 
 [ "Hello world" ] [
     [
@@ -70,24 +69,6 @@ IN: html.templates.chloe.tests
     ] run-template
 ] unit-test
 
-SYMBOL: test6-aux?
-
-[ "True" ] [
-    [
-        test6-aux? on
-        "test6" test-template call-template
-    ] run-template
-] unit-test
-
-SYMBOL: test7-aux?
-
-[ "" ] [
-    [
-        test7-aux? off
-        "test7" test-template call-template
-    ] run-template
-] unit-test
-
 [ ] [ blank-values ] unit-test
 
 [ ] [ "A label" "label" set-value ] unit-test
@@ -128,7 +109,7 @@ M: link-test link-href drop "http://www.apple.com/foo&bar" ;
 
 [ "<ul><li>1</li><li>2</li><li>3</li></ul>" ] [
     [
-        "test9" test-template call-template
+        "test7" test-template call-template
     ] run-template [ blank? not ] filter
 ] unit-test
 
@@ -143,7 +124,7 @@ TUPLE: person first-name last-name ;
 
 [ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
     [
-        "test10" test-template call-template
+        "test8" test-template call-template
     ] run-template [ blank? not ] filter
 ] unit-test
 
@@ -156,6 +137,14 @@ TUPLE: person first-name last-name ;
 
 [ "<table><tr><td>RBaxter</td><td>Unknown</td></tr><tr><td>Doug</td><td>Coleman</td></tr></table>" ] [
     [
-        "test11" test-template call-template
+        "test8" test-template call-template
     ] run-template [ blank? not ] filter
 ] unit-test
+
+[ ] [ 1 "id" set-value ] unit-test
+
+[ "<a name=\"1\">Hello</a>" ] [
+    [
+        "test9" test-template call-template
+    ] run-template
+] unit-test
index 092f79bb36e7b11fed74f3186f3f77a855e07d83..9e0aa3fe1d533b55aa84c4ec3badcdf85577c36c 100644 (file)
@@ -3,19 +3,16 @@
 USING: accessors kernel sequences combinators kernel namespaces
 classes.tuple assocs splitting words arrays memoize
 io io.files io.encodings.utf8 io.streams.string
-unicode.case tuple-syntax mirrors fry math
+unicode.case tuple-syntax mirrors fry math urls
 multiline xml xml.data xml.writer xml.utilities
 html.elements
 html.components
 html.templates
-http.server
-http.server.auth
-http.server.flows
-http.server.actions
-http.server.sessions ;
+html.templates.chloe.syntax ;
 IN: html.templates.chloe
 
 ! Chloe is Ed's favorite web designer
+SYMBOL: tag-stack
 
 TUPLE: chloe path ;
 
@@ -23,8 +20,6 @@ C: <chloe> chloe
 
 DEFER: process-template
 
-: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
-
 : chloe-attrs-only ( assoc -- assoc' )
     [ drop name-url chloe-ns = ] assoc-filter ;
 
@@ -38,35 +33,23 @@ DEFER: process-template
         [ t ]
     } cond nip ;
 
-SYMBOL: tags
-
-MEMO: chloe-name ( string -- name )
-    name new
-        swap >>tag
-        chloe-ns >>url ;
-
-: required-attr ( tag name -- value )
-    dup chloe-name rot at*
-    [ nip ] [ drop " attribute is required" append throw ] if ;
-
-: optional-attr ( tag name -- value )
-    chloe-name swap at ;
-
 : process-tag-children ( tag -- )
     [ process-template ] each ;
 
+CHLOE: chloe process-tag-children ;
+
 : children>string ( tag -- string )
     [ process-tag-children ] with-string-writer ;
 
-: title-tag ( tag -- )
-    children>string set-title ;
+CHLOE: title children>string set-title ;
 
-: write-title-tag ( tag -- )
+CHLOE: write-title
     drop
-    "head" tags get member? "title" tags get member? not and
+    "head" tag-stack get member?
+    "title" tag-stack get member? not and
     [ <title> write-title </title> ] [ write-title ] if ;
 
-: style-tag ( tag -- )
+CHLOE: style
     dup "include" optional-attr dup [
         swap children>string empty? [
             "style tag cannot have both an include attribute and a body" throw
@@ -76,146 +59,12 @@ MEMO: chloe-name ( string -- name )
         drop children>string
     ] if add-style ;
 
-: write-style-tag ( tag -- )
+CHLOE: write-style
     drop <style> write-style </style> ;
 
-: atom-tag ( tag -- )
-    [ "title" required-attr ]
-    [ "href" required-attr ]
-    bi set-atom-feed ;
-
-: write-atom-tag ( tag -- )
-    drop
-    "head" tags get member? [
-        write-atom-feed
-    ] [
-        atom-feed get value>> second write
-    ] if ;
-
-: parse-query-attr ( string -- assoc )
-    dup empty?
-    [ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
-
-: flow-attr ( tag -- )
-    "flow" optional-attr {
-        { "none" [ flow-id off ] }
-        { "begin" [ begin-flow ] }
-        { "current" [ ] }
-        { f [ ] }
-    } case ;
-
-: session-attr ( tag -- )
-    "session" optional-attr {
-        { "none" [ session off flow-id off ] }
-        { "current" [ ] }
-        { f [ ] }
-    } case ;
-
-: a-start-tag ( tag -- )
-    [
-        <a
-        dup flow-attr
-        dup session-attr
-        dup "value" optional-attr [ value f ] [
-            [ "href" required-attr ]
-            [ "query" optional-attr parse-query-attr ]
-            bi
-        ] ?if link>string =href
-        a>
-    ] with-scope ;
-
-: a-tag ( tag -- )
-    [ a-start-tag ]
-    [ process-tag-children ]
-    [ drop </a> ]
-    tri ;
-
-: form-start-tag ( tag -- )
-    [
-        [
-            <form
-            "POST" =method
-            {
-                [ flow-attr ]
-                [ session-attr ]
-                [ "action" required-attr resolve-base-path =action ]
-                [ tag-attrs non-chloe-attrs-only print-attrs ]
-            } cleave
-            form>
-        ] [
-            hidden-form-field
-            "for" optional-attr [ hidden render ] when*
-        ] bi
-    ] with-scope ;
-
-: form-tag ( tag -- )
-    [ form-start-tag ]
-    [ process-tag-children ]
-    [ drop </form> ]
-    tri ;
+CHLOE: even "index" value even? [ process-tag-children ] [ drop ] if ;
 
-DEFER: process-chloe-tag
-
-STRING: button-tag-markup
-<t:form class="inline" xmlns:t="http://factorcode.org/chloe/1.0">
-    <button type="submit"></button>
-</t:form>
-;
-
-: add-tag-attrs ( attrs tag -- )
-    tag-attrs swap update ;
-
-: button-tag ( tag -- )
-    button-tag-markup string>xml delegate
-    {
-        [ [ tag-attrs chloe-attrs-only ] dip add-tag-attrs ]
-        [ [ tag-attrs non-chloe-attrs-only ] dip "button" tag-named add-tag-attrs ]
-        [ [ children>string 1array ] dip "button" tag-named set-tag-children ]
-        [ nip ]
-    } 2cleave process-chloe-tag ;
-
-: attr>word ( value -- word/f )
-    dup ":" split1 swap lookup
-    [ ] [ "No such word: " swap append throw ] ?if ;
-
-: attr>var ( value -- word/f )
-    attr>word dup symbol? [
-        "Must be a symbol: " swap append throw
-    ] unless ;
-
-: if-satisfied? ( tag -- ? )
-    t swap
-    {
-        [ "code"  optional-attr [ attr>word execute and ] when* ]
-        [  "var"  optional-attr [ attr>var      get and ] when* ]
-        [ "svar"  optional-attr [ attr>var     sget and ] when* ]
-        [ "uvar"  optional-attr [ attr>var     uget and ] when* ]
-        [ "value" optional-attr [ value             and ] when* ]
-    } cleave ;
-
-: if-tag ( tag -- )
-    dup if-satisfied? [ process-tag-children ] [ drop ] if ;
-
-: even-tag ( tag -- )
-    "index" value even? [ process-tag-children ] [ drop ] if ;
-
-: odd-tag ( tag -- )
-    "index" value odd? [ process-tag-children ] [ drop ] if ;
-
-: (each-tag) ( tag quot -- )
-    [
-        [ "values" required-attr value ] keep
-        '[ , process-tag-children ]
-    ] dip call ; inline
-
-: each-tag ( tag -- )
-    [ with-each-value ] (each-tag) ;
-
-: each-tuple-tag ( tag -- )
-    [ with-each-tuple ] (each-tag) ;
-
-: each-assoc-tag ( tag -- )
-    [ with-each-assoc ] (each-tag) ;
+CHLOE: odd "index" value odd? [ process-tag-children ] [ drop ] if ;
 
 : (bind-tag) ( tag quot -- )
     [
@@ -223,94 +72,56 @@ STRING: button-tag-markup
         '[ , process-tag-children ]
     ] dip call ; inline
 
-: bind-tuple-tag ( tag -- )
-    [ with-tuple-values ] (bind-tag) ;
+CHLOE: each [ with-each-value ] (bind-tag) ;
 
-: bind-assoc-tag ( tag -- )
-    [ with-assoc-values ] (bind-tag) ;
+CHLOE: bind-each [ with-each-object ] (bind-tag) ;
+
+CHLOE: bind [ with-values ] (bind-tag) ;
 
 : error-message-tag ( tag -- )
     children>string render-error ;
 
-: validation-messages-tag ( tag -- )
-    drop render-validation-messages ;
+CHLOE: comment drop ;
 
-: singleton-component-tag ( tag class -- )
-    [ "name" required-attr ] dip render ;
+CHLOE: call-next-template drop call-next-template ;
 
-: attrs>slots ( tag tuple -- )
-    [ attrs>> ] [ <mirror> ] bi*
-    '[
-        swap tag>> dup "name" =
-        [ 2drop ] [ , set-at ] if
-    ] assoc-each ;
+CHLOE-SINGLETON: label
+CHLOE-SINGLETON: link
+CHLOE-SINGLETON: farkup
+CHLOE-SINGLETON: inspector
+CHLOE-SINGLETON: comparison
+CHLOE-SINGLETON: html
+CHLOE-SINGLETON: hidden
 
-: tuple-component-tag ( tag class -- )
-    [ drop "name" required-attr ]
-    [ new [ attrs>slots ] keep ]
-    2bi render ;
+CHLOE-TUPLE: field
+CHLOE-TUPLE: textarea
+CHLOE-TUPLE: password
+CHLOE-TUPLE: choice
+CHLOE-TUPLE: checkbox
+CHLOE-TUPLE: code
 
 : process-chloe-tag ( tag -- )
-    dup name-tag {
-        { "chloe" [ process-tag-children ] }
-
-        ! HTML head
-        { "title" [ title-tag ] }
-        { "write-title" [ write-title-tag ] }
-        { "style" [ style-tag ] }
-        { "write-style" [ write-style-tag ] }
-        { "atom" [ atom-tag ] }
-        { "write-atom" [ write-atom-tag ] }
-
-        ! HTML elements
-        { "a" [ a-tag ] }
-        { "button" [ button-tag ] }
-
-        ! Components
-        { "label" [ label singleton-component-tag ] }
-        { "link" [ link singleton-component-tag ] }
-        { "code" [ code tuple-component-tag ] }
-        { "farkup" [ farkup singleton-component-tag ] }
-        { "inspector" [ inspector singleton-component-tag ] }
-        { "comparison" [ comparison singleton-component-tag ] }
-        { "html" [ html singleton-component-tag ] }
-
-        ! Forms
-        { "form" [ form-tag ] }
-        { "error-message" [ error-message-tag ] }
-        { "validation-messages" [ validation-messages-tag ] }
-        { "hidden" [ hidden singleton-component-tag ] }
-        { "field" [ field tuple-component-tag ] }
-        { "password" [ password tuple-component-tag ] }
-        { "textarea" [ textarea tuple-component-tag ] }
-        { "choice" [ choice tuple-component-tag ] }
-        { "checkbox" [ checkbox tuple-component-tag ] }
-
-        ! Control flow
-        { "if" [ if-tag ] }
-        { "even" [ even-tag ] }
-        { "odd" [ odd-tag ] }
-        { "each" [ each-tag ] }
-        { "each-assoc" [ each-assoc-tag ] }
-        { "each-tuple" [ each-tuple-tag ] }
-        { "bind-assoc" [ bind-assoc-tag ] }
-        { "bind-tuple" [ bind-tuple-tag ] }
-        { "comment" [ drop ] }
-        { "call-next-template" [ drop call-next-template ] }
-
-        [ "Unknown chloe tag: " prepend throw ]
-    } case ;
+    dup name-tag dup tags get at
+    [ call ] [ "Unknown chloe tag: " prepend throw ] ?if ;
 
 : process-tag ( tag -- )
     {
-        [ name-tag >lower tags get push ]
+        [ name-tag >lower tag-stack get push ]
         [ write-start-tag ]
         [ process-tag-children ]
         [ write-end-tag ]
-        [ drop tags get pop* ]
+        [ drop tag-stack get pop* ]
     } cleave ;
 
+: expand-attrs ( tag -- tag )
+    dup [ tag? ] is? [
+        clone [
+            [ "@" ?head [ value object>string ] when ] assoc-map
+        ] change-attrs
+    ] when ;
+
 : process-template ( xml -- )
+    expand-attrs
     {
         { [ dup [ chloe-tag? ] is? ] [ process-chloe-tag ] }
         { [ dup [ tag? ] is? ] [ process-tag ] }
@@ -319,7 +130,7 @@ STRING: button-tag-markup
 
 : process-chloe ( xml -- )
     [
-        V{ } clone tags set
+        V{ } clone tag-stack set
 
         nested-template? get [
             process-template
@@ -334,6 +145,6 @@ STRING: button-tag-markup
     ] with-scope ;
 
 M: chloe call-template*
-    path>> utf8 <file-reader> read-xml process-chloe ;
+    path>> ".xml" append utf8 <file-reader> read-xml process-chloe ;
 
 INSTANCE: chloe template
diff --git a/extra/html/templates/chloe/syntax/syntax.factor b/extra/html/templates/chloe/syntax/syntax.factor
new file mode 100644 (file)
index 0000000..7eeb756
--- /dev/null
@@ -0,0 +1,61 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+IN: html.templates.chloe.syntax
+USING: accessors kernel sequences combinators kernel namespaces
+classes.tuple assocs splitting words arrays memoize parser
+io io.files io.encodings.utf8 io.streams.string
+unicode.case tuple-syntax mirrors fry math urls
+multiline xml xml.data xml.writer xml.utilities
+html.elements
+html.components
+html.templates ;
+
+SYMBOL: tags
+
+tags global [ H{ } clone or ] change-at
+
+: define-chloe-tag ( name quot -- ) swap tags get set-at ;
+
+: CHLOE:
+    scan parse-definition define-chloe-tag ; parsing
+
+: chloe-ns "http://factorcode.org/chloe/1.0" ; inline
+
+MEMO: chloe-name ( string -- name )
+    name new
+        swap >>tag
+        chloe-ns >>url ;
+
+: required-attr ( tag name -- value )
+    dup chloe-name rot at*
+    [ nip ] [ drop " attribute is required" append throw ] if ;
+
+: optional-attr ( tag name -- value )
+    chloe-name swap at ;
+
+: singleton-component-tag ( tag class -- )
+    [ "name" required-attr ] dip render ;
+
+: CHLOE-SINGLETON:
+    scan-word
+    [ word-name ] [ '[ , singleton-component-tag ] ] bi
+    define-chloe-tag ;
+    parsing
+
+: attrs>slots ( tag tuple -- )
+    [ attrs>> ] [ <mirror> ] bi*
+    '[
+        swap tag>> dup "name" =
+        [ 2drop ] [ , set-at ] if
+    ] assoc-each ;
+
+: tuple-component-tag ( tag class -- )
+    [ drop "name" required-attr ]
+    [ new [ attrs>slots ] keep ]
+    2bi render ;
+
+: CHLOE-TUPLE:
+    scan-word
+    [ word-name ] [ '[ , tuple-component-tag ] ] bi
+    define-chloe-tag ;
+    parsing
diff --git a/extra/html/templates/chloe/test/test10.xml b/extra/html/templates/chloe/test/test10.xml
deleted file mode 100644 (file)
index afded93..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <table>
-               <t:each-tuple t:values="people">
-                       <tr>
-                               <td><t:label t:name="first-name"/></td>
-                               <td><t:label t:name="last-name"/></td>
-                       </tr>
-               </t:each-tuple>
-       </table>
-
-</t:chloe>
diff --git a/extra/html/templates/chloe/test/test11.xml b/extra/html/templates/chloe/test/test11.xml
deleted file mode 100644 (file)
index 17e31b1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <table>
-               <t:each-assoc t:values="people">
-                       <tr>
-                               <td><t:label t:name="first-name"/></td>
-                               <td><t:label t:name="last-name"/></td>
-                       </tr>
-               </t:each-assoc>
-       </table>
-
-</t:chloe>
index b3f649333f639d735d11146bb68b15e1324e6d3c..8e2ff2e8ad5d9932b089bd5c26449bf2d7dfdb1c 100644 (file)
@@ -2,8 +2,26 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:if t:var="html.templates.chloe.tests:test6-aux?">
-               True
-       </t:if>
+       <t:label t:name="label" />
+
+       <t:link t:name="link" />
+
+       <t:code t:name="code" mode="mode" />
+
+       <t:farkup t:name="farkup" />
+
+       <t:inspector t:name="inspector" />
+
+       <t:html t:name="html" />
+
+       <t:field t:name="field" t:size="13" />
+
+       <t:password t:name="password" t:size="10" />
+
+       <t:textarea t:name="textarea" t:rows="5" t:cols="10" />
+
+       <t:choice t:name="choice" t:choices="choices" />
+
+       <t:checkbox t:name="checkbox">Checkbox</t:checkbox>
 
 </t:chloe>
index 338595e556e4019965facf53017f1a0c543c4d69..6166c800eddbe2cb4893e2aa7b927df30e7f6ce0 100644 (file)
@@ -2,8 +2,10 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:if t:var="html.templates.chloe.tests:test7-aux?">
-               True
-       </t:if>
+       <ul>
+               <t:each t:name="numbers">
+                       <li><t:label t:name="value"/></li>
+               </t:each>
+       </ul>
 
 </t:chloe>
index 8e2ff2e8ad5d9932b089bd5c26449bf2d7dfdb1c..fd4a64ad0ae6a870df0e5dcf4083beb0810ca28c 100644 (file)
@@ -2,26 +2,13 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:label t:name="label" />
-
-       <t:link t:name="link" />
-
-       <t:code t:name="code" mode="mode" />
-
-       <t:farkup t:name="farkup" />
-
-       <t:inspector t:name="inspector" />
-
-       <t:html t:name="html" />
-
-       <t:field t:name="field" t:size="13" />
-
-       <t:password t:name="password" t:size="10" />
-
-       <t:textarea t:name="textarea" t:rows="5" t:cols="10" />
-
-       <t:choice t:name="choice" t:choices="choices" />
-
-       <t:checkbox t:name="checkbox">Checkbox</t:checkbox>
+       <table>
+               <t:bind-each t:name="people">
+                       <tr>
+                               <td><t:label t:name="first-name"/></td>
+                               <td><t:label t:name="last-name"/></td>
+                       </tr>
+               </t:bind-each>
+       </table>
 
 </t:chloe>
index bcfc468738326db7c21519aa0546110ec19516eb..a9b2769445ca17a71047338e141c4348b68f87bf 100644 (file)
@@ -1,11 +1,3 @@
 <?xml version='1.0' ?>
 
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <ul>
-               <t:each t:values="numbers">
-                       <li><t:label t:name="value"/></li>
-               </t:each>
-       </ul>
-
-</t:chloe>
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0"><a name="@id">Hello</a></t:chloe>
index 580af58ecc96ac8b7c92dedea62cc908bf7a1bb8..de774f0864d1c29846e95bbb132610027499fcf3 100644 (file)
@@ -2,7 +2,8 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors kernel fry io io.encodings.utf8 io.files
 debugger prettyprint continuations namespaces boxes sequences
-arrays strings html.elements io.streams.string quotations ;
+arrays strings html.elements io.streams.string
+quotations xml.data xml.writer ;
 IN: html.templates
 
 MIXIN: template
@@ -13,6 +14,8 @@ M: string call-template* write ;
 
 M: callable call-template* call ;
 
+M: xml call-template* write-xml ;
+
 M: object call-template* output-stream get stream-copy ;
 
 ERROR: template-error template error ;
@@ -43,17 +46,17 @@ SYMBOL: style
 : write-style ( -- )
     style get >string write ;
 
-SYMBOL: atom-feed
+SYMBOL: atom-feeds
 
-: set-atom-feed ( title url -- )
-    2array atom-feed get >box ;
+: add-atom-feed ( title url -- )
+    2array atom-feeds get push ;
 
-: write-atom-feed ( -- )
-    atom-feed get value>> [
+: write-atom-feeds ( -- )
+    atom-feeds get [
         <link "alternate" =rel "application/atom+xml" =type
-        [ first =title ] [ second =href ] bi
+        first2 [ =title ] [ =href ] bi*
         link/>
-    ] when* ;
+    ] each ;
 
 SYMBOL: nested-template?
 
@@ -66,9 +69,9 @@ M: f call-template* drop call-next-template ;
 
 : with-boilerplate ( body template -- )
     [
-        title get [ <box> title set ] unless
-        atom-feed get [ <box> atom-feed set ] unless
-        style get [ SBUF" " clone style set ] unless
+        title [ <box> or ] change
+        style [ SBUF" " clone or ] change
+        atom-feeds [ V{ } like ] change
 
         [
             [
index db90f746acffeddd8a09494c2d7bf15897188dd8..daf4ad88d33c1445bfa96a08a3ee2b52d11dade3 100755 (executable)
@@ -1,5 +1,5 @@
 USING: http.client http.client.private http tools.test
-tuple-syntax namespaces ;
+tuple-syntax namespaces urls ;
 [ "localhost" f ] [ "localhost" parse-host ] unit-test
 [ "localhost" 8888 ] [ "localhost:8888" parse-host ] unit-test
 
@@ -10,36 +10,26 @@ tuple-syntax namespaces ;
 
 [
     TUPLE{ request
-        protocol: http
+        url: TUPLE{ url protocol: "http" host: "www.apple.com" port: 80 path: "/index.html" }
         method: "GET"
-        host: "www.apple.com"
-        port: 80
-        path: "/index.html"
         version: "1.1"
         cookies: V{ }
         header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
     }
 ] [
-    [
-        "http://www.apple.com/index.html"
-        <get-request>
-    ] with-scope
+    "http://www.apple.com/index.html"
+    <get-request>
 ] unit-test
 
 [
     TUPLE{ request
-        protocol: https
+        url: TUPLE{ url protocol: "https" host: "www.amazon.com" port: 443 path: "/index.html" }
         method: "GET"
-        host: "www.amazon.com"
-        port: 443
-        path: "/index.html"
         version: "1.1"
         cookies: V{ }
         header: H{ { "connection" "close" } { "user-agent" "Factor http.client vocabulary" } }
     }
 ] [
-    [
-        "https://www.amazon.com/index.html"
-        <get-request>
-    ] with-scope
+    "https://www.amazon.com/index.html"
+    <get-request>
 ] unit-test
index 7b156a4b9b2f76135687ac5cf3c832c892ad6256..e6c8791e20e37f4253d98fb9e3320d12428b21f1 100755 (executable)
@@ -4,7 +4,7 @@ USING: assocs http kernel math math.parser namespaces sequences
 io io.sockets io.streams.string io.files io.timeouts strings
 splitting calendar continuations accessors vectors math.order
 io.encodings.8-bit io.encodings.binary io.streams.duplex
-fry debugger inspector ascii ;
+fry debugger inspector ascii urls ;
 IN: http.client
 
 : max-redirects 10 ;
@@ -21,14 +21,16 @@ DEFER: http-request
 
 SYMBOL: redirects
 
+: redirect-url ( request url -- request )
+    '[ , >url derive-url ensure-port ] change-url ;
+
 : do-redirect ( response data -- response data )
     over code>> 300 399 between? [
         drop
         redirects inc
         redirects get max-redirects < [
             request get
-            swap "location" header dup absolute-url?
-            [ request-with-url ] [ request-with-path ] if
+            swap "location" header redirect-url
             "GET" >>method http-request
         ] [
             too-many-redirects
@@ -51,7 +53,7 @@ PRIVATE>
 
 : http-request ( request -- response data )
     dup request [
-        dup request-addr latin1 [
+        dup url>> url-addr latin1 [
             1 minutes timeouts
             write-request
             read-response
@@ -62,8 +64,8 @@ PRIVATE>
 
 : <get-request> ( url -- request )
     <request>
-        swap request-with-url
-        "GET" >>method ;
+        "GET" >>method
+        swap >url ensure-port >>url ;
 
 : http-get* ( url -- response data )
     <get-request> http-request ;
@@ -101,7 +103,7 @@ M: download-failed error.
 : <post-request> ( content-type content url -- request )
     <request>
         "POST" >>method
-        swap request-with-url
+        swap >url ensure-port >>url
         swap >>post-data
         swap >>post-data-type ;
 
index 151d1ce84f2cd4e12dd0bc96bf1cf3a866f78be8..471d7e276bcc03bde8e8dae04b5f2816faa2c390 100755 (executable)
@@ -1,37 +1,8 @@
 USING: http tools.test multiline tuple-syntax
 io.streams.string kernel arrays splitting sequences
-assocs io.sockets db db.sqlite continuations ;
+assocs io.sockets db db.sqlite continuations urls ;
 IN: http.tests
 
-[ "hello%20world" ] [ "hello world" url-encode ] unit-test
-[ "hello world" ] [ "hello%20world" url-decode ] unit-test
-[ "~hello world" ] [ "%7ehello+world" url-decode ] unit-test
-[ f ] [ "%XX%XX%XX" url-decode ] unit-test
-[ f ] [ "%XX%XX%X" url-decode ] unit-test
-
-[ "hello world"   ] [ "hello+world"    url-decode ] unit-test
-[ "hello world"   ] [ "hello%20world"  url-decode ] unit-test
-[ " ! "           ] [ "%20%21%20"      url-decode ] unit-test
-[ "hello world"   ] [ "hello world%"   url-decode ] unit-test
-[ "hello world"   ] [ "hello world%x"  url-decode ] unit-test
-[ "hello%20world" ] [ "hello world"    url-encode ] unit-test
-[ "%20%21%20"     ] [ " ! "            url-encode ] unit-test
-
-[ "\u001234hi\u002045" ] [ "\u001234hi\u002045" url-encode url-decode ] unit-test
-
-[ "/" ] [ "http://foo.com" url>path ] unit-test
-[ "/" ] [ "http://foo.com/" url>path ] unit-test
-[ "/bar" ] [ "http://foo.com/bar" url>path ] unit-test
-[ "/bar" ] [ "/bar" url>path ] unit-test
-
-[ "a=b&a=c" ] [ { { "a" { "b" "c" } } } assoc>query ] unit-test
-
-[ H{ { "a" "b" } } ] [ "a=b" query>assoc ] unit-test
-
-[ H{ { "a" { "b" "c" } } } ] [ "a=b&a=c" query>assoc ] unit-test
-
-[ "a=3" ] [ { { "a" 3 } } assoc>query ] unit-test
-
 : lf>crlf "\n" split "\r\n" join ;
 
 STRING: read-request-test-1
@@ -45,11 +16,8 @@ blah
 
 [
     TUPLE{ request
-        protocol: http
-        port: 80
+        url: TUPLE{ url protocol: "http" port: 80 path: "/bar" }
         method: "GET"
-        path: "/bar"
-        query: H{ }
         version: "1.1"
         header: H{ { "some-header" "1; 2" } { "content-length" "4" } }
         post-data: "blah"
@@ -85,14 +53,10 @@ Host: www.sex.com
 
 [
     TUPLE{ request
-        protocol: http
-        port: 80
+        url: TUPLE{ url protocol: "http" port: 80 host: "www.sex.com" path: "/bar" }
         method: "HEAD"
-        path: "/bar"
-        query: H{ }
         version: "1.1"
         header: H{ { "host" "www.sex.com" } }
-        host: "www.sex.com"
         cookies: V{ }
     }
 ] [
@@ -101,6 +65,15 @@ Host: www.sex.com
     ] with-string-reader
 ] unit-test
 
+STRING: read-request-test-3
+GET nested HTTP/1.0
+
+;
+
+[ read-request-test-3 [ read-request ] with-string-reader ]
+[ "Bad request: URL" = ]
+must-fail-with
+
 STRING: read-response-test-1
 HTTP/1.1 404 not found
 Content-Type: text/html; charset=UTF8
@@ -145,14 +118,16 @@ read-response-test-1' 1array [
 ] unit-test
 
 ! Live-fire exercise
-USING: http.server http.server.static http.server.sessions
-http.server.actions http.server.auth.login http.server.db http.client
+USING: http.server http.server.static furnace.sessions
+furnace.actions furnace.auth.login furnace.db http.client
 io.server io.files io io.encodings.ascii
-accessors namespaces threads ;
+accessors namespaces threads
+http.server.responses http.server.redirection
+http.server.dispatchers ;
 
 : add-quit-action
     <action>
-        [ stop-server [ "Goodbye" write ] <html-content> ] >>display
+        [ stop-server "Goodbye" "text/html" <content> ] >>display
     "quit" add-responder ;
 
 : test-db "test.db" temp-file sqlite-db ;
@@ -171,7 +146,7 @@ test-db [
                 "resource:extra/http/test" <static> >>default
             "nested" add-responder
             <action>
-                [ "redirect-loop" f <standard-redirect> ] >>display
+                [ URL" redirect-loop" <temporary-redirect> ] >>display
             "redirect-loop" add-responder
         main-responder set
 
@@ -186,16 +161,6 @@ test-db [
     "http://localhost:1237/nested/foo.html" http-get =
 ] unit-test
 
-! Try with a slightly malformed request
-[ t ] [
-    "localhost" 1237 <inet> ascii [
-        "GET nested HTTP/1.0\r\n" write flush
-        "\r\n" write flush
-        read-crlf drop
-        read-header
-    ] with-client "location" swap at "/" head?
-] unit-test
-
 [ "http://localhost:1237/redirect-loop" http-get ]
 [ too-many-redirects? ] must-fail-with
 
@@ -237,7 +202,7 @@ test-db [
 [ ] [
     [
         <dispatcher>
-            <action> [ [ "Hi" write ] <text-content> ] >>display
+            <action> [ [ "Hi" write ] "text/plain" <content> ] >>display
             <login>
             <sessions>
             "" add-responder
index 89c8f62d5c3d86fb6333578810ba43ddf269c428..e8f7189f7524b81a9835472d2176ea30d93391c7 100755 (executable)
@@ -6,90 +6,16 @@ assocs sequences splitting sorting sets debugger
 strings vectors hashtables quotations arrays byte-arrays
 math.parser calendar calendar.format
 
-io io.streams.string io.encodings.utf8 io.encodings.string
-io.sockets io.sockets.secure
+io io.server io.sockets.secure
 
 unicode.case unicode.categories qualified
 
-html.templates ;
+urls html.templates ;
 
 EXCLUDE: fry => , ;
 
 IN: http
 
-SINGLETON: http
-
-SINGLETON: https
-
-GENERIC: http-port ( protocol -- port )
-
-M: http http-port drop 80 ;
-
-M: https http-port drop 443 ;
-
-GENERIC: protocol>string ( protocol -- string )
-
-M: http protocol>string drop "http" ;
-
-M: https protocol>string drop "https" ;
-
-: string>protocol ( string -- protocol )
-    {
-        { "http" [ http ] }
-        { "https" [ https ] }
-        [ "Unknown protocol: " swap append throw ]
-    } case ;
-
-: absolute-url? ( url -- ? )
-    [ "http://" head? ] [ "https://" head? ] bi or ;
-
-: url-quotable? ( ch -- ? )
-    #! In a URL, can this character be used without
-    #! URL-encoding?
-    {
-        { [ dup letter? ] [ t ] }
-        { [ dup LETTER? ] [ t ] }
-        { [ dup digit? ] [ t ] }
-        { [ dup "/_-.:" member? ] [ t ] }
-        [ f ]
-    } cond nip ; foldable
-
-: push-utf8 ( ch -- )
-    1string utf8 encode
-    [ CHAR: % , >hex 2 CHAR: 0 pad-left % ] each ;
-
-: url-encode ( str -- str )
-    [
-        [ dup url-quotable? [ , ] [ push-utf8 ] if ] each
-    ] "" make ;
-
-: url-decode-hex ( index str -- )
-    2dup length 2 - >= [
-        2drop
-    ] [
-        [ 1+ dup 2 + ] dip subseq  hex> [ , ] when*
-    ] if ;
-
-: url-decode-% ( index str -- index str )
-    2dup url-decode-hex [ 3 + ] dip ;
-
-: url-decode-+-or-other ( index str ch -- index str )
-    dup CHAR: + = [ drop CHAR: \s ] when , [ 1+ ] dip ;
-
-: url-decode-iter ( index str -- )
-    2dup length >= [
-        2drop
-    ] [
-        2dup nth dup CHAR: % = [
-            drop url-decode-%
-        ] [
-            url-decode-+-or-other
-        ] if url-decode-iter
-    ] if ;
-
-: url-decode ( str -- str )
-    [ 0 swap url-decode-iter ] "" make utf8 decode ;
-
 : crlf "\r\n" write ;
 
 : add-header ( value key assoc -- )
@@ -130,6 +56,7 @@ M: https protocol>string drop "https" ;
     {
         { [ dup number? ] [ number>string ] }
         { [ dup timestamp? ] [ timestamp>http-string ] }
+        { [ dup url? ] [ url>string ] }
         { [ dup string? ] [ ] }
         { [ dup sequence? ] [ [ header-value>string ] map "; " join ] }
     } cond ;
@@ -145,42 +72,6 @@ M: https protocol>string drop "https" ;
         header-value>string check-header-string write crlf
     ] assoc-each crlf ;
 
-: add-query-param ( value key assoc -- )
-    [
-        at [
-            {
-                { [ dup string? ] [ swap 2array ] }
-                { [ dup array? ] [ swap suffix ] }
-                { [ dup not ] [ drop ] }
-            } cond
-        ] when*
-    ] 2keep set-at ;
-
-: query>assoc ( query -- assoc )
-    dup [
-        "&" split H{ } clone [
-            [
-                [ "=" split1 [ dup [ url-decode ] when ] bi@ swap ] dip
-                add-query-param
-            ] curry each
-        ] keep
-    ] when ;
-
-: assoc>query ( hash -- str )
-    [
-        {
-            { [ dup number? ] [ number>string 1array ] }
-            { [ dup string? ] [ 1array ] }
-            { [ dup sequence? ] [ ] }
-        } cond
-    ] assoc-map
-    [
-        [
-            [ url-encode ] dip
-            [ url-encode "=" swap 3append , ] with each
-        ] assoc-each
-    ] { } make "&" join ;
-
 TUPLE: cookie name value path domain expires max-age http-only ;
 
 : <cookie> ( value name -- cookie )
@@ -236,12 +127,8 @@ TUPLE: cookie name value path domain expires max-age http-only ;
     [ unparse-cookie ] map concat "; " join ;
 
 TUPLE: request
-protocol
-host
-port
 method
-path
-query
+url
 version
 header
 post-data
@@ -254,51 +141,30 @@ cookies ;
 : <request>
     request new
         "1.1" >>version
-        http >>protocol
+        <url>
+            "http" >>protocol
+            H{ } clone >>query
+        >>url
         H{ } clone >>header
-        H{ } clone >>query
         V{ } clone >>cookies
         "close" "connection" set-header
         "Factor http.client vocabulary" "user-agent" set-header ;
 
-: query-param ( request key -- value )
-    swap query>> at ;
-
-: set-query-param ( request value key -- request )
-    pick query>> set-at ;
-
-: chop-hostname ( str -- str' )
-    ":" split1 "//" ?head drop nip
-    CHAR: / over index over length or tail
-    dup empty? [ drop "/" ] when ;
-
-: url>path ( url -- path )
-    #! Technically, only proxies are meant to support hostnames
-    #! in HTTP requests, but IE sends these sometimes so we
-    #! just chop the hostname part.
-    url-decode
-    dup { "http://" "https://" } [ head? ] with contains?
-    [ chop-hostname ] when ;
-
 : read-method ( request -- request )
     " " read-until [ "Bad request: method" throw ] unless
     >>method ;
 
-: read-query ( request -- request )
-    " " read-until
-    [ "Bad request: query params" throw ] unless
-    query>assoc >>query ;
+: check-absolute ( url -- url )
+    dup path>> "/" head? [ "Bad request: URL" throw ] unless ; inline
 
 : read-url ( request -- request )
-    " ?" read-until {
-        { CHAR: \s [ dup empty? [ drop read-url ] [ url>path >>path ] if ] }
-        { CHAR: ? [ url>path >>path read-query ] }
-        [ "Bad request: URL" throw ]
-    } case ;
+    " " read-until [
+        dup empty? [ drop read-url ] [ >url check-absolute >>url ] if
+    ] [ "Bad request: URL" throw ] if ;
 
 : parse-version ( string -- version )
-    "HTTP/" ?head [ "Bad version" throw ] unless
-    dup { "1.0" "1.1" } member? [ "Bad version" throw ] unless ;
+    "HTTP/" ?head [ "Bad request: version" throw ] unless
+    dup { "1.0" "1.1" } member? [ "Bad request: version" throw ] unless ;
 
 : read-request-version ( request -- request )
     read-crlf [ CHAR: \s = ] left-trim
@@ -325,13 +191,11 @@ SYMBOL: max-post-request
 : read-post-data ( request -- request )
     dup header>> content-length [ read >>post-data ] when* ;
 
-: parse-host ( string -- host port )
-    "." ?tail drop ":" split1
-    dup [ string>number ] when ;
-
 : extract-host ( request -- request )
-    dup [ "host" header parse-host ] keep protocol>> http-port or
-    [ >>host ] [ >>port ] bi* ;
+    [ ] [ url>> ] [ "host" header parse-host ] tri
+    [ >>host ] [ >>port ] bi*
+    ensure-port
+    drop ;
 
 : extract-post-data-type ( request -- request )
     dup "content-type" header >>post-data-type ;
@@ -349,6 +213,9 @@ SYMBOL: max-post-request
 : parse-content-type ( content-type -- type encoding )
     ";" split1 parse-content-type-attributes "charset" swap at ;
 
+: detect-protocol ( request -- request )
+    dup url>> remote-address get secure? "https" "http" ? >>protocol drop ;
+
 : read-request ( -- request )
     <request>
     read-method
@@ -356,6 +223,7 @@ SYMBOL: max-post-request
     read-request-version
     read-request-header
     read-post-data
+    detect-protocol
     extract-host
     extract-post-data-type
     parse-post-data
@@ -364,15 +232,8 @@ SYMBOL: max-post-request
 : write-method ( request -- request )
     dup method>> write bl ;
 
-: (link>string) ( url query -- url' )
-    [ url-encode ] [ assoc>query ] bi*
-    dup empty? [ drop ] [ "?" swap 3append ] if ;
-
-: write-url ( request -- )
-    [ path>> ] [ query>> ] bi (link>string) write ;
-
 : write-request-url ( request -- request )
-    dup write-url bl ;
+    dup url>> relative-url url>string write bl ;
 
 : write-version ( request -- request )
     "HTTP/" write dup request-version write crlf ;
@@ -383,24 +244,13 @@ SYMBOL: max-post-request
         "application/x-www-form-urlencoded" >>post-data-type
     ] if ;
 
-GENERIC: protocol-addr ( request protocol -- addr )
-
-M: object protocol-addr
-    drop [ host>> ] [ port>> ] bi <inet> ;
-
-M: https protocol-addr
-    call-next-method <secure> ;
-
-: request-addr ( request -- addr )
-    dup protocol>> protocol-addr ;
-
-: request-host ( request -- string )
-    [ host>> ] [ port>> ] bi dup http http-port =
+: url-host ( url -- string )
+    [ host>> ] [ port>> ] bi dup "http" protocol-port =
     [ drop ] [ ":" swap number>string 3append ] if ;
 
 : write-request-header ( request -- request )
     dup header>> >hashtable
-    over host>> [ over request-host "host" pick set-at ] when
+    over url>> host>> [ over url>> url-host "host" pick set-at ] when
     over post-data>> [ length "content-length" pick set-at ] when*
     over post-data-type>> [ "content-type" pick set-at ] when*
     over cookies>> f like [ unparse-cookies "cookie" pick set-at ] when*
@@ -419,39 +269,6 @@ M: https protocol-addr
     flush
     drop ;
 
-: request-with-path ( request path -- request )
-    [ "/" prepend ] [ "/" ] if*
-    "?" split1 [ >>path ] [ dup [ query>assoc ] when >>query ] bi* ;
-
-: request-with-url ( request url -- request )
-    ":" split1
-    [ string>protocol >>protocol ]
-    [
-        "//" ?head [ "Invalid URL" throw ] unless
-        "/" split1
-        [
-            parse-host [ >>host ] [ >>port ] bi*
-            dup protocol>> http-port '[ , or ] change-port
-        ]
-        [ request-with-path ]
-        bi*
-    ] bi* ;
-
-: request-url ( request -- url )
-    [
-        [
-            dup host>> [
-                [ protocol>> protocol>string write "://" write ]
-                [ host>> url-encode write ":" write ]
-                [ [ port>> ] [ protocol>> http-port or ] bi number>string write ]
-                tri
-            ] [ drop ] if
-        ]
-        [ path>> "/" head? [ "/" write ] unless ]
-        [ write-url ]
-        tri
-    ] with-string-writer ;
-
 GENERIC: write-response ( response -- )
 
 GENERIC: write-full-response ( request response -- )
@@ -556,7 +373,7 @@ body ;
 
 : <raw-response> ( -- response )
     raw-response new
-    "1.1" >>version ;
+        "1.1" >>version ;
 
 M: raw-response write-response ( respose -- )
     write-response-version
diff --git a/extra/http/mime/authors.txt b/extra/http/mime/authors.txt
deleted file mode 100755 (executable)
index 1901f27..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Slava Pestov
diff --git a/extra/http/mime/mime.factor b/extra/http/mime/mime.factor
deleted file mode 100755 (executable)
index f9097ec..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-! Copyright (C) 2004, 2005 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: io assocs kernel sequences math namespaces splitting ;
-
-IN: http.mime
-
-: file-extension ( filename -- extension )
-    "." split dup length 1 <= [ drop f ] [ peek ] if ;
-
-: mime-type ( filename -- mime-type )
-    file-extension "mime-types" get at "application/octet-stream" or ;
-
-H{
-    { "html"   "text/html"                        }
-    { "txt"    "text/plain"                       }
-    { "xml"    "text/xml"                         }
-    { "css"    "text/css"                         }
-                                                    
-    { "gif"    "image/gif"                        }
-    { "png"    "image/png"                        }
-    { "jpg"    "image/jpeg"                       }
-    { "jpeg"   "image/jpeg"                       }
-                                                    
-    { "jar"    "application/octet-stream"         }
-    { "zip"    "application/octet-stream"         }
-    { "tgz"    "application/octet-stream"         }
-    { "tar.gz" "application/octet-stream"         }
-    { "gz"     "application/octet-stream"         }
-
-    { "pdf"    "application/pdf"                  }
-
-    { "factor" "text/plain"                       }
-    { "cgi"    "application/x-cgi-script"         }
-    { "fhtml"  "application/x-factor-server-page" }
-} "mime-types" set-global
diff --git a/extra/http/server/actions/actions-tests.factor b/extra/http/server/actions/actions-tests.factor
deleted file mode 100755 (executable)
index 480cbc8..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-USING: kernel http.server.actions validators
-tools.test math math.parser multiline namespaces http
-io.streams.string http.server sequences splitting accessors ;
-IN: http.server.actions.tests
-
-<action>
-    [ "a" param "b" param [ string>number ] bi@ + ] >>display
-"action-1" set
-
-: lf>crlf "\n" split "\r\n" join ;
-
-STRING: action-request-test-1
-GET http://foo/bar?a=12&b=13 HTTP/1.1
-
-blah
-;
-
-[ 25 ] [
-    init-request
-    action-request-test-1 lf>crlf
-    [ read-request ] with-string-reader
-    request set
-    { } "action-1" get call-responder
-] unit-test
diff --git a/extra/http/server/actions/actions.factor b/extra/http/server/actions/actions.factor
deleted file mode 100755 (executable)
index eb5b8bf..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors sequences kernel assocs combinators http.server\r
-validators http hashtables namespaces fry continuations locals\r
-boxes xml.entities html.elements html.components io arrays math ;\r
-IN: http.server.actions\r
-\r
-SYMBOL: params\r
-\r
-SYMBOL: rest-param\r
-\r
-: render-validation-messages ( -- )\r
-    validation-messages get\r
-    dup empty? [ drop ] [\r
-        <ul "errors" =class ul>\r
-            [ <li> message>> escape-string write </li> ] each\r
-        </ul>\r
-    ] if ;\r
-\r
-TUPLE: action rest-param init display validate submit ;\r
-\r
-: new-action ( class -- action )\r
-    new\r
-        [ ] >>init\r
-        [ <400> ] >>display\r
-        [ ] >>validate\r
-        [ <400> ] >>submit ;\r
-\r
-: <action> ( -- action )\r
-    action new-action ;\r
-\r
-: handle-get ( action -- response )\r
-    blank-values\r
-    [ init>> call ]\r
-    [ display>> call ]\r
-    bi ;\r
-\r
-: validation-failed ( -- * )\r
-    request get method>> "POST" =\r
-    [ action get display>> call ] [ <400> ] if exit-with ;\r
-\r
-: handle-post ( action -- response )\r
-    init-validation\r
-    blank-values\r
-    [ validate>> call ]\r
-    [ submit>> call ] bi ;\r
-\r
-: handle-rest-param ( arg -- )\r
-    dup length 1 > action get rest-param>> not or\r
-    [ <404> exit-with ] [\r
-        action get rest-param>> associate rest-param set\r
-    ] if ;\r
-\r
-M: action call-responder* ( path action -- response )\r
-    dup action set\r
-    '[\r
-        , dup empty? [ drop ] [ handle-rest-param ] if\r
-\r
-        init-validation\r
-        ,\r
-        request get\r
-        [ request-params rest-param get assoc-union params set ]\r
-        [ method>> ] bi\r
-        {\r
-            { "GET" [ handle-get ] }\r
-            { "HEAD" [ handle-get ] }\r
-            { "POST" [ handle-post ] }\r
-        } case\r
-    ] with-exit-continuation ;\r
-\r
-: param ( name -- value )\r
-    params get at ;\r
-\r
-: check-validation ( -- )\r
-    validation-failed? [ validation-failed ] when ;\r
-\r
-: validate-params ( validators -- )\r
-    params get swap validate-values from-assoc\r
-    check-validation ;\r
-\r
-: validate-integer-id ( -- )\r
-    { { "id" [ v-number ] } } validate-params ;\r
-\r
-TUPLE: page-action < action template ;\r
-\r
-: <page-action> ( -- page )\r
-    page-action new-action\r
-        dup '[ , template>> <html-content> ] >>display ;\r
-\r
-TUPLE: feed-action < action feed ;\r
-\r
-: <feed-action> ( -- feed )\r
-    feed-action new\r
-        dup '[ , feed>> call <feed-content> ] >>display ;\r
diff --git a/extra/http/server/auth/auth.factor b/extra/http/server/auth/auth.factor
deleted file mode 100755 (executable)
index 4b34fbe..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-! Copyright (c) 2008 Slava Pestov\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors assocs namespaces kernel sequences sets\r
-http.server\r
-http.server.sessions\r
-http.server.auth.providers ;\r
-IN: http.server.auth\r
-\r
-SYMBOL: logged-in-user\r
-\r
-GENERIC: init-user-profile ( responder -- )\r
-\r
-M: object init-user-profile drop ;\r
-\r
-M: dispatcher init-user-profile\r
-    default>> init-user-profile ;\r
-\r
-M: filter-responder init-user-profile\r
-    responder>> init-user-profile ;\r
-\r
-: profile ( -- assoc ) logged-in-user get profile>> ;\r
-\r
-: user-changed ( -- )\r
-    logged-in-user get t >>changed? drop ;\r
-\r
-: uget ( key -- value )\r
-    profile at ;\r
-\r
-: uset ( value key -- )\r
-    profile set-at\r
-    user-changed ;\r
-\r
-: uchange ( quot key -- )\r
-    profile swap change-at\r
-    user-changed ; inline\r
-\r
-SYMBOL: capabilities\r
-\r
-V{ } clone capabilities set-global\r
-\r
-: define-capability ( word -- ) capabilities get adjoin ;\r
diff --git a/extra/http/server/auth/basic/basic.factor b/extra/http/server/auth/basic/basic.factor
deleted file mode 100755 (executable)
index ff071b3..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-! Copyright (c) 2007 Chris Double.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors quotations assocs kernel splitting\r
-base64 html.elements io combinators http.server\r
-http.server.auth.providers http.server.auth.login\r
-http sequences ;\r
-IN: http.server.auth.basic\r
-\r
-TUPLE: basic-auth < filter-responder realm provider ;\r
-\r
-C: <basic-auth> basic-auth\r
-\r
-: authorization-ok? ( provider header -- ? )\r
-    #! Given the realm and the 'Authorization' header,\r
-    #! authenticate the user.\r
-    dup [\r
-        " " split1 swap "Basic" = [\r
-            base64> ":" split1 spin check-login\r
-        ] [\r
-            2drop f\r
-        ] if\r
-    ] [\r
-        2drop f\r
-    ] if ;\r
-\r
-: <401> ( realm -- response )\r
-    401 "Unauthorized" <trivial-response>\r
-    "Basic realm=\"" rot "\"" 3append\r
-    "WWW-Authenticate" set-header\r
-    [\r
-        <html> <body>\r
-            "Username or Password is invalid" write\r
-        </body> </html>\r
-    ] >>body ;\r
-\r
-: logged-in? ( request responder -- ? )\r
-    provider>> swap "authorization" header authorization-ok? ;\r
-\r
-M: basic-auth call-responder* ( request path responder -- response )\r
-    pick over logged-in?\r
-    [ call-next-method ] [ 2nip realm>> <401> ] if ;\r
diff --git a/extra/http/server/auth/login/boilerplate.xml b/extra/http/server/auth/login/boilerplate.xml
deleted file mode 100644 (file)
index edc8c32..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <h1><t:write-title /></h1>
-
-       <t:call-next-template />
-
-</t:chloe>
diff --git a/extra/http/server/auth/login/edit-profile.xml b/extra/http/server/auth/login/edit-profile.xml
deleted file mode 100644 (file)
index 6beaf5d..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Edit Profile</t:title>
-
-       <t:form t:action="$login/edit-profile">
-
-       <table>
-       
-       <tr>
-               <th class="field-label">User name:</th>
-               <td><t:label t:name="username" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Real name:</th>
-               <td><t:field t:name="realname" /></td>
-       </tr>
-       
-       <tr>
-               <td></td>
-               <td>Specifying a real name is optional.</td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Current password:</th>
-               <td><t:password t:name="password" /></td>
-       </tr>
-       
-       <tr>
-               <td></td>
-               <td>If you don't want to change your current password, leave this field blank.</td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">New password:</th>
-               <td><t:password t:name="new-password" /></td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">Verify:</th>
-               <td><t:password t:name="verify-password" /></td>
-       </tr>
-       
-       <tr>
-               <td></td>
-               <td>If you are changing your password, enter it twice to ensure it is correct.</td>
-       </tr>
-       
-       <tr>
-               <th class="field-label">E-mail:</th>
-               <td><t:field t:name="email" /></td>
-       </tr>
-       
-       <tr>
-               <td></td>
-               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
-       </tr>
-       
-       </table>
-
-       <p>
-               <input type="submit" value="Update" />
-               <t:validation-messages />
-       </p>
-
-       </t:form>
-       
-</t:chloe>
diff --git a/extra/http/server/auth/login/login-tests.factor b/extra/http/server/auth/login/login-tests.factor
deleted file mode 100755 (executable)
index b69630a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-IN: http.server.auth.login.tests\r
-USING: tools.test http.server.auth.login ;\r
-\r
-\ <login> must-infer\r
-\ allow-registration must-infer\r
-\ allow-password-recovery must-infer\r
diff --git a/extra/http/server/auth/login/login.factor b/extra/http/server/auth/login/login.factor
deleted file mode 100755 (executable)
index fd4fbab..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-! Copyright (c) 2008 Slava Pestov\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors quotations assocs kernel splitting\r
-combinators sequences namespaces hashtables sets\r
-fry arrays threads qualified random validators\r
-io\r
-io.sockets\r
-io.encodings.utf8\r
-io.encodings.string\r
-io.binary\r
-continuations\r
-destructors\r
-checksums\r
-checksums.sha2\r
-validators\r
-html.components\r
-html.elements\r
-html.templates\r
-html.templates.chloe\r
-http\r
-http.server\r
-http.server.auth\r
-http.server.auth.providers\r
-http.server.auth.providers.db\r
-http.server.actions\r
-http.server.flows\r
-http.server.sessions\r
-http.server.boilerplate ;\r
-QUALIFIED: smtp\r
-IN: http.server.auth.login\r
-\r
-TUPLE: login < dispatcher users checksum ;\r
-\r
-: users ( -- provider )\r
-    login get users>> ;\r
-\r
-: encode-password ( string salt -- bytes )\r
-    [ utf8 encode ] [ 4 >be ] bi* append\r
-    login get checksum>> checksum-bytes ;\r
-\r
-: >>encoded-password ( user string -- user )\r
-    32 random-bits [ encode-password ] keep\r
-    [ >>password ] [ >>salt ] bi* ; inline\r
-\r
-: valid-login? ( password user -- ? )\r
-    [ salt>> encode-password ] [ password>> ] bi = ;\r
-\r
-: check-login ( password username -- user/f )\r
-    users get-user dup [ [ valid-login? ] keep and ] [ 2drop f ] if ;\r
-\r
-! Destructor\r
-TUPLE: user-saver user ;\r
-\r
-C: <user-saver> user-saver\r
-\r
-M: user-saver dispose\r
-    user>> dup changed?>> [ users update-user ] [ drop ] if ;\r
-\r
-: save-user-after ( user -- )\r
-    <user-saver> &dispose drop ;\r
-\r
-: login-template ( name -- template )\r
-    "resource:extra/http/server/auth/login/" swap ".xml"\r
-    3append <chloe> ;\r
-\r
-! ! ! Login\r
-: successful-login ( user -- response )\r
-    username>> set-uid "$login" end-flow ;\r
-\r
-: login-failed ( -- * )\r
-    "invalid username or password" validation-error\r
-    validation-failed ;\r
-\r
-: <login-action> ( -- action )\r
-    <action>\r
-        [ "login" login-template <html-content> ] >>display\r
-\r
-        [\r
-            {\r
-                { "username" [ v-required ] }\r
-                { "password" [ v-required ] }\r
-            } validate-params\r
-\r
-            "password" value\r
-            "username" value check-login\r
-            [ successful-login ] [ login-failed ] if*\r
-        ] >>submit ;\r
-\r
-! ! ! New user registration\r
-\r
-: user-exists ( -- * )\r
-    "username taken" validation-error\r
-    validation-failed ;\r
-\r
-: password-mismatch ( -- * )\r
-    "passwords do not match" validation-error\r
-    validation-failed ;\r
-\r
-: same-password-twice ( -- )\r
-    "new-password" value "verify-password" value =\r
-    [ password-mismatch ] unless ;\r
-\r
-: <register-action> ( -- action )\r
-    <page-action>\r
-        "register" login-template >>template\r
-\r
-        [\r
-            {\r
-                { "username" [ v-username ] }\r
-                { "realname" [ [ v-one-line ] v-optional ] }\r
-                { "new-password" [ v-password ] }\r
-                { "verify-password" [ v-password ] }\r
-                { "email" [ [ v-email ] v-optional ] }\r
-                { "captcha" [ v-captcha ] }\r
-            } validate-params\r
-\r
-            same-password-twice\r
-        ] >>validate\r
-\r
-        [\r
-            "username" value <user>\r
-                "realname" value >>realname\r
-                "new-password" value >>encoded-password\r
-                "email" value >>email\r
-                H{ } clone >>profile\r
-\r
-            users new-user [ user-exists ] unless*\r
-\r
-            login get init-user-profile\r
-\r
-            successful-login\r
-        ] >>submit ;\r
-\r
-! ! ! Editing user profile\r
-\r
-: <edit-profile-action> ( -- action )\r
-    <action>\r
-        [\r
-            logged-in-user get\r
-            [ username>> "username" set-value ]\r
-            [ realname>> "realname" set-value ]\r
-            [ email>> "email" set-value ]\r
-            tri\r
-        ] >>init\r
-\r
-        [ "edit-profile" login-template <html-content> ] >>display\r
-\r
-        [\r
-            uid "username" set-value\r
-\r
-            {\r
-                { "realname" [ [ v-one-line ] v-optional ] }\r
-                { "password" [ ] }\r
-                { "new-password" [ [ v-password ] v-optional ] }\r
-                { "verify-password" [ [ v-password ] v-optional ] } \r
-                { "email" [ [ v-email ] v-optional ] }\r
-            } validate-params\r
-\r
-            { "password" "new-password" "verify-password" }\r
-            [ value empty? not ] contains? [\r
-                "password" value uid check-login\r
-                [ "incorrect password" validation-error ] unless\r
-\r
-                same-password-twice\r
-            ] when\r
-        ] >>validate\r
-\r
-        [\r
-            logged-in-user get\r
-\r
-            "new-password" value dup empty?\r
-            [ drop ] [ >>encoded-password ] if\r
-\r
-            "realname" value >>realname\r
-            "email" value >>email\r
-\r
-            t >>changed?\r
-\r
-            drop\r
-\r
-            "$login" end-flow\r
-        ] >>submit ;\r
-\r
-! ! ! Password recovery\r
-\r
-SYMBOL: lost-password-from\r
-\r
-: current-host ( -- string )\r
-    request get host>> host-name or ;\r
-\r
-: new-password-url ( user -- url )\r
-    "new-password"\r
-    swap [\r
-        [ username>> "username" set ]\r
-        [ ticket>> "ticket" set ]\r
-        bi\r
-    ] H{ } make-assoc\r
-    derive-url ;\r
-\r
-: password-email ( user -- email )\r
-    smtp:<email>\r
-        [ "[ " % current-host % " ] password recovery" % ] "" make >>subject\r
-        lost-password-from get >>from\r
-        over email>> 1array >>to\r
-        [\r
-            "This e-mail was sent by the application server on " % current-host % "\n" %\r
-            "because somebody, maybe you, clicked on a ``recover password'' link in the\n" %\r
-            "login form, and requested a new password for the user named ``" %\r
-            over username>> % "''.\n" %\r
-            "\n" %\r
-            "If you believe that this request was legitimate, you may click the below link in\n" %\r
-            "your browser to set a new password for your account:\n" %\r
-            "\n" %\r
-            swap new-password-url %\r
-            "\n\n" %\r
-            "Love,\n" %\r
-            "\n" %\r
-            "  FactorBot\n" %\r
-        ] "" make >>body ;\r
-\r
-: send-password-email ( user -- )\r
-    '[ , password-email smtp:send-email ]\r
-    "E-mail send thread" spawn drop ;\r
-\r
-: <recover-action-1> ( -- action )\r
-    <action>\r
-        [ "recover-1" login-template <html-content> ] >>display\r
-\r
-        [\r
-            {\r
-                { "username" [ v-username ] }\r
-                { "email" [ v-email ] }\r
-                { "captcha" [ v-captcha ] }\r
-            } validate-params\r
-        ] >>validate\r
-\r
-        [\r
-            "email" value "username" value\r
-            users issue-ticket [\r
-                send-password-email\r
-            ] when*\r
-\r
-            "recover-2" login-template <html-content>\r
-        ] >>submit ;\r
-\r
-: <recover-action-3> ( -- action )\r
-    <action>\r
-        [\r
-            {\r
-                { "username" [ v-username ] }\r
-                { "ticket" [ v-required ] }\r
-            } validate-params\r
-        ] >>init\r
-\r
-        [ "recover-3" login-template <html-content> ] >>display\r
-\r
-        [\r
-            {\r
-                { "username" [ v-username ] }\r
-                { "ticket" [ v-required ] }\r
-                { "new-password" [ v-password ] }\r
-                { "verify-password" [ v-password ] }\r
-            } validate-params\r
-\r
-            same-password-twice\r
-        ] >>validate\r
-\r
-        [\r
-            "ticket" value\r
-            "username" value\r
-            users claim-ticket [\r
-                "new-password" value >>encoded-password\r
-                users update-user\r
-\r
-                "recover-4" login-template <html-content>\r
-            ] [\r
-                <400>\r
-            ] if*\r
-        ] >>submit ;\r
-\r
-! ! ! Logout\r
-: <logout-action> ( -- action )\r
-    <action>\r
-        [\r
-            f set-uid\r
-            "$login/login" end-flow\r
-        ] >>submit ;\r
-\r
-! ! ! Authentication logic\r
-\r
-TUPLE: protected < filter-responder capabilities ;\r
-\r
-C: <protected> protected\r
-\r
-: show-login-page ( -- response )\r
-    begin-flow\r
-    "$login/login" f <standard-redirect> ;\r
-\r
-: check-capabilities ( responder user -- ? )\r
-    [ capabilities>> ] bi@ subset? ;\r
-\r
-M: protected call-responder* ( path responder -- response )\r
-    uid dup [\r
-        users get-user 2dup check-capabilities [\r
-            [ logged-in-user set ] [ save-user-after ] bi\r
-            call-next-method\r
-        ] [\r
-            3drop show-login-page\r
-        ] if\r
-    ] [\r
-        3drop show-login-page\r
-    ] if ;\r
-\r
-M: login call-responder* ( path responder -- response )\r
-    dup login set\r
-    call-next-method ;\r
-\r
-: <login-boilerplate> ( responder -- responder' )\r
-    <boilerplate>\r
-        "boilerplate" login-template >>template ;\r
-\r
-: <login> ( responder -- auth )\r
-    login new-dispatcher\r
-        swap >>default\r
-        <login-action> <login-boilerplate> "login" add-responder\r
-        <logout-action> <login-boilerplate> "logout" add-responder\r
-        users-in-db >>users\r
-        sha-256 >>checksum ;\r
-\r
-! ! ! Configuration\r
-\r
-: allow-edit-profile ( login -- login )\r
-    <edit-profile-action> f <protected> <login-boilerplate>\r
-        "edit-profile" add-responder ;\r
-\r
-: allow-registration ( login -- login )\r
-    <register-action> <login-boilerplate>\r
-        "register" add-responder ;\r
-\r
-: allow-password-recovery ( login -- login )\r
-    <recover-action-1> <login-boilerplate>\r
-        "recover-password" add-responder\r
-    <recover-action-3> <login-boilerplate>\r
-        "new-password" add-responder ;\r
-\r
-: allow-edit-profile? ( -- ? )\r
-    login get responders>> "edit-profile" swap key? ;\r
-\r
-: allow-registration? ( -- ? )\r
-    login get responders>> "register" swap key? ;\r
-\r
-: allow-password-recovery? ( -- ? )\r
-    login get responders>> "recover-password" swap key? ;\r
diff --git a/extra/http/server/auth/login/login.xml b/extra/http/server/auth/login/login.xml
deleted file mode 100644 (file)
index 545d7e0..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Login</t:title>
-
-       <t:form t:action="login">
-
-               <table>
-
-                       <tr>
-                               <th class="field-label">User name:</th>
-                               <td><t:field t:name="username" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Password:</th>
-                               <td><t:password t:name="password" /></td>
-                       </tr>
-
-               </table>
-
-               <p>
-
-                       <input type="submit" value="Log in" />
-                       <t:validation-messages />
-
-               </p>
-
-       </t:form>
-
-       <p>
-               <t:if code="http.server.auth.login:login-failed?">
-                       <t:a t:href="register">Register</t:a>
-               </t:if>
-               |
-               <t:if code="http.server.auth.login:allow-password-recovery?">
-                       <t:a t:href="recover-password">Recover Password</t:a>
-               </t:if>
-       </p>
-
-</t:chloe>
diff --git a/extra/http/server/auth/login/recover-1.xml b/extra/http/server/auth/login/recover-1.xml
deleted file mode 100644 (file)
index 21fbe6f..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Recover lost password: step 1 of 4</t:title>
-
-       <p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
-
-       <t:form t:action="recover-password">
-
-               <table>
-
-                       <tr>
-                               <th class="field-label">User name:</th>
-                               <td><t:field t:name="username" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">E-mail:</th>
-                               <td><t:field t:name="email" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Captcha:</th>
-                               <td><t:field t:name="captcha" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
-                       </tr>
-
-               </table>
-
-               <input type="submit" value="Recover password" />
-
-       </t:form>
-
-</t:chloe>
diff --git a/extra/http/server/auth/login/recover-2.xml b/extra/http/server/auth/login/recover-2.xml
deleted file mode 100644 (file)
index c7819bd..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Recover lost password: step 2 of 4</t:title>
-
-       <p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
-
-</t:chloe>
diff --git a/extra/http/server/auth/login/recover-3.xml b/extra/http/server/auth/login/recover-3.xml
deleted file mode 100644 (file)
index 2e412d1..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>Recover lost password: step 3 of 4</t:title>
-
-       <p>Choose a new password for your account.</p>
-
-       <t:form t:action="new-password">
-
-               <table>
-
-                       <t:hidden t:name="username" />
-                       <t:hidden t:name="ticket" />
-
-                       <tr>
-                               <th class="field-label">Password:</th>
-                               <td><t:password t:name="new-password" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Verify password:</th>
-                               <td><t:password t:name="verify-password" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Enter your password twice to ensure it is correct.</td>
-                       </tr>
-
-               </table>
-
-               <p>
-                       <input type="submit" value="Set password" />
-                       <t:validation-messages />
-               </p>
-
-       </t:form>
-
-</t:chloe>
diff --git a/extra/http/server/auth/login/recover-4.xml b/extra/http/server/auth/login/recover-4.xml
deleted file mode 100755 (executable)
index f5d02fa..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version='1.0' ?>\r
-\r
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">\r
-\r
-       <t:title>Recover lost password: step 4 of 4</t:title>\r
-\r
-       <p>Your password has been reset. You may now <t:a t:href="login">log in</t:a>.</p>\r
-\r
-</t:chloe>\r
diff --git a/extra/http/server/auth/login/register.xml b/extra/http/server/auth/login/register.xml
deleted file mode 100644 (file)
index 9815f21..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version='1.0' ?>
-
-<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-       <t:title>New User Registration</t:title>
-
-       <t:form t:action="register">
-
-               <table>
-
-                       <tr>
-                               <th class="field-label">User name:</th>
-                               <td><t:field t:name="username" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Real name:</th>
-                               <td><t:field t:name="realname" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Specifying a real name is optional.</td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Password:</th>
-                               <td><t:password t:name="new-password" /></td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Verify:</th>
-                               <td><t:password t:name="verify-password" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Enter your password twice to ensure it is correct.</td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">E-mail:</th>
-                               <td><t:field t:name="email" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
-                       </tr>
-
-                       <tr>
-                               <th class="field-label">Captcha:</th>
-                               <td><t:field t:name="captcha" /></td>
-                       </tr>
-
-                       <tr>
-                               <td></td>
-                               <td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
-                       </tr>
-
-               </table>
-
-               <p>
-
-                       <input type="submit" value="Register" />
-                       <t:validation-messages />
-
-               </p>
-
-       </t:form>
-
-</t:chloe>
diff --git a/extra/http/server/auth/providers/assoc/assoc-tests.factor b/extra/http/server/auth/providers/assoc/assoc-tests.factor
deleted file mode 100755 (executable)
index 91e802b..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-IN: http.server.auth.providers.assoc.tests\r
-USING: http.server.actions http.server.auth.providers \r
-http.server.auth.providers.assoc http.server.auth.login\r
-tools.test namespaces accessors kernel ;\r
-\r
-<action> <login>\r
-    <users-in-memory> >>users\r
-login set\r
-\r
-[ t ] [\r
-    "slava" <user>\r
-        "foobar" >>encoded-password\r
-        "slava@factorcode.org" >>email\r
-        H{ } clone >>profile\r
-    users new-user\r
-    username>> "slava" =\r
-] unit-test\r
-\r
-[ f ] [\r
-    "slava" <user>\r
-        H{ } clone >>profile\r
-    users new-user\r
-] unit-test\r
-\r
-[ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
-\r
-[ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
-\r
-[ t ] [ "user" get >boolean ] unit-test\r
-\r
-[ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
-\r
-[ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
-\r
-[ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
diff --git a/extra/http/server/auth/providers/assoc/assoc.factor b/extra/http/server/auth/providers/assoc/assoc.factor
deleted file mode 100755 (executable)
index d6ba587..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-IN: http.server.auth.providers.assoc\r
-USING: accessors assocs kernel\r
-http.server.auth.providers ;\r
-\r
-TUPLE: users-in-memory assoc ;\r
-\r
-: <users-in-memory> ( -- provider )\r
-    H{ } clone users-in-memory boa ;\r
-\r
-M: users-in-memory get-user ( username provider -- user/f )\r
-    assoc>> at ;\r
-\r
-M: users-in-memory update-user ( user provider -- ) 2drop ;\r
-\r
-M: users-in-memory new-user ( user provider -- user/f )\r
-    [ dup username>> ] dip assoc>>\r
-    2dup key? [ 3drop f ] [ pick [ set-at ] dip ] if ;\r
diff --git a/extra/http/server/auth/providers/db/db-tests.factor b/extra/http/server/auth/providers/db/db-tests.factor
deleted file mode 100755 (executable)
index a6a9235..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-IN: http.server.auth.providers.db.tests\r
-USING: http.server.actions\r
-http.server.auth.login\r
-http.server.auth.providers\r
-http.server.auth.providers.db tools.test\r
-namespaces db db.sqlite db.tuples continuations\r
-io.files accessors kernel ;\r
-\r
-<action> <login>\r
-    users-in-db >>users\r
-login set\r
-\r
-[ "auth-test.db" temp-file delete-file ] ignore-errors\r
-\r
-"auth-test.db" temp-file sqlite-db [\r
-\r
-    init-users-table\r
-\r
-    [ t ] [\r
-        "slava" <user>\r
-            "foobar" >>encoded-password\r
-            "slava@factorcode.org" >>email\r
-            H{ } clone >>profile\r
-            users new-user\r
-            username>> "slava" =\r
-    ] unit-test\r
-\r
-    [ f ] [\r
-        "slava" <user>\r
-            H{ } clone >>profile\r
-        users new-user\r
-    ] unit-test\r
-\r
-    [ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
-\r
-    [ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
-\r
-    [ t ] [ "user" get >boolean ] unit-test\r
-\r
-    [ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
-\r
-    [ ] [ "user" get users update-user ] unit-test\r
-\r
-    [ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
-\r
-    [ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
-] with-db\r
diff --git a/extra/http/server/auth/providers/db/db.factor b/extra/http/server/auth/providers/db/db.factor
deleted file mode 100755 (executable)
index 3ed4845..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: db db.tuples db.types accessors
-http.server.auth.providers kernel continuations
-classes.singleton ;
-IN: http.server.auth.providers.db
-
-user "USERS"
-{
-    { "username" "USERNAME" { VARCHAR 256 } +user-assigned-id+ }
-    { "realname" "REALNAME" { VARCHAR 256 } }
-    { "password" "PASSWORD" BLOB +not-null+ }
-    { "salt" "SALT" INTEGER +not-null+ }
-    { "email" "EMAIL" { VARCHAR 256 } }
-    { "ticket" "TICKET" { VARCHAR 256 } }
-    { "capabilities" "CAPABILITIES" FACTOR-BLOB }
-    { "profile" "PROFILE" FACTOR-BLOB }
-    { "deleted" "DELETED" INTEGER +not-null+ }
-} define-persistent
-
-: init-users-table user ensure-table ;
-
-SINGLETON: users-in-db
-
-M: users-in-db get-user
-    drop <user> select-tuple ;
-
-M: users-in-db new-user
-    drop
-    [
-        user new
-            over username>> >>username
-        select-tuple [
-            drop f
-        ] [
-            dup insert-tuple
-        ] if
-    ] with-transaction ;
-
-M: users-in-db update-user
-    drop update-tuple ;
diff --git a/extra/http/server/auth/providers/null/null.factor b/extra/http/server/auth/providers/null/null.factor
deleted file mode 100755 (executable)
index 30f6dbd..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: http.server.auth.providers kernel ;\r
-IN: http.server.auth.providers.null\r
-\r
-TUPLE: no-users ;\r
-\r
-: no-users T{ no-users } ;\r
-\r
-M: no-users get-user 2drop f ;\r
-\r
-M: no-users new-user 2drop f ;\r
-\r
-M: no-users update-user 2drop ;\r
diff --git a/extra/http/server/auth/providers/providers.factor b/extra/http/server/auth/providers/providers.factor
deleted file mode 100755 (executable)
index a51c4da..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: kernel accessors random math.parser locals\r
-sequences math ;\r
-IN: http.server.auth.providers\r
-\r
-TUPLE: user\r
-username realname\r
-password salt\r
-email ticket capabilities profile deleted changed? ;\r
-\r
-: <user> ( username -- user )\r
-    user new\r
-        swap >>username\r
-        0 >>deleted ;\r
-\r
-GENERIC: get-user ( username provider -- user/f )\r
-\r
-GENERIC: update-user ( user provider -- )\r
-\r
-GENERIC: new-user ( user provider -- user/f )\r
-\r
-! Password recovery support\r
-\r
-:: issue-ticket ( email username provider -- user/f )\r
-    [let | user [ username provider get-user ] |\r
-        user [\r
-            user email>> length 0 > [\r
-                user email>> email = [\r
-                    user\r
-                    256 random-bits >hex >>ticket\r
-                    dup provider update-user\r
-                ] [ f ] if\r
-            ] [ f ] if\r
-        ] [ f ] if\r
-    ] ;\r
-\r
-:: claim-ticket ( ticket username provider -- user/f )\r
-    [let | user [ username provider get-user ] |\r
-        user [\r
-            user ticket>> ticket = [\r
-                user f >>ticket dup provider update-user\r
-            ] [ f ] if\r
-        ] [ f ] if\r
-    ] ;\r
-\r
-! For configuration\r
-\r
-: add-user ( provider user -- provider )\r
-    over new-user [ "User exists" throw ] when ;\r
diff --git a/extra/http/server/boilerplate/boilerplate.factor b/extra/http/server/boilerplate/boilerplate.factor
deleted file mode 100644 (file)
index 96c59ed..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-! Copyright (c) 2008 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: accessors kernel namespaces http.server html.templates
-locals ;
-IN: http.server.boilerplate
-
-TUPLE: boilerplate < filter-responder template ;
-
-: <boilerplate> f boilerplate boa ;
-
-M:: boilerplate call-responder* ( path responder -- )
-    path responder call-next-method
-    dup content-type>> "text/html" = [
-        clone [| body |
-            [ body responder template>> with-boilerplate ]
-        ] change-body
-    ] when ;
diff --git a/extra/http/server/callbacks/callbacks-tests.factor b/extra/http/server/callbacks/callbacks-tests.factor
deleted file mode 100755 (executable)
index 31ea164..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-IN: http.server.callbacks\r
-USING: http.server.actions http.server.callbacks accessors\r
-http.server http tools.test namespaces io fry sequences\r
-splitting kernel hashtables continuations ;\r
-\r
-[ 123 ] [\r
-    [\r
-        init-request\r
-\r
-        <request> "GET" >>method request set\r
-        [\r
-            exit-continuation set\r
-            { }\r
-            <action> [ [ "hello" print 123 ] show-final ] >>display\r
-            <callback-responder>\r
-            call-responder\r
-        ] callcc1\r
-    ] with-scope\r
-] unit-test\r
-\r
-[\r
-    init-request\r
-\r
-    <action> [\r
-        [\r
-            "hello" print\r
-            '[ , write ] <html-content>\r
-        ] show-page\r
-        "byebye" print\r
-        [ 123 ] show-final\r
-    ] >>display\r
-    <callback-responder> "r" set\r
-\r
-    [ 123 ] [\r
-        [\r
-            exit-continuation set\r
-            <request> "GET" >>method request set\r
-            { } "r" get call-responder\r
-        ] callcc1\r
-\r
-        body>> first\r
-\r
-        <request>\r
-            "GET" >>method\r
-            swap cont-id associate >>query\r
-            "/" >>path\r
-        request set\r
-\r
-        [\r
-            exit-continuation set\r
-            { }\r
-            "r" get call-responder\r
-        ] callcc1\r
-\r
-        ! get-post-get\r
-        <request>\r
-            "GET" >>method\r
-            swap "location" header "=" last-split1 nip cont-id associate >>query\r
-            "/" >>path\r
-        request set\r
-\r
-        [\r
-            exit-continuation set\r
-            { }\r
-            "r" get call-responder\r
-        ] callcc1\r
-    ] unit-test\r
-] with-scope\r
diff --git a/extra/http/server/callbacks/callbacks.factor b/extra/http/server/callbacks/callbacks.factor
deleted file mode 100755 (executable)
index 3b819e0..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-! Copyright (C) 2004 Chris Double.\r
-! Copyright (C) 2006, 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: http http.server io kernel math namespaces\r
-continuations calendar sequences assocs hashtables\r
-accessors arrays alarms quotations combinators fry assocs.lib ;\r
-IN: http.server.callbacks\r
-\r
-SYMBOL: responder\r
-\r
-TUPLE: callback-responder responder callbacks ;\r
-\r
-: <callback-responder> ( responder -- responder' )\r
-    #! A continuation responder is a special type of session\r
-    #! manager. However it works entirely differently from\r
-    #! the URL and cookie session managers.\r
-    H{ } clone callback-responder boa ;\r
-\r
-TUPLE: callback cont quot expires alarm responder ;\r
-\r
-: timeout 20 minutes ;\r
-\r
-: timeout-callback ( callback -- )\r
-    [ alarm>> cancel-alarm ]\r
-    [ dup responder>> callbacks>> delete-at ]\r
-    bi ;\r
-\r
-: touch-callback ( callback -- )\r
-    dup expires>> [\r
-        dup alarm>> [ cancel-alarm ] when*\r
-        dup '[ , timeout-callback ] timeout later >>alarm\r
-    ] when drop ;\r
-\r
-: <callback> ( cont quot expires? -- callback )\r
-    f callback-responder get callback boa\r
-    dup touch-callback ;\r
-\r
-: invoke-callback ( callback -- response )\r
-    [ touch-callback ]\r
-    [ quot>> request get exit-continuation get 3array ]\r
-    [ cont>> continue-with ]\r
-    tri ;\r
-\r
-: register-callback ( cont quot expires? -- id )\r
-    <callback> callback-responder get callbacks>> set-at-unique ;\r
-\r
-: forward-to-url ( url query -- * )\r
-    #! When executed inside a 'show' call, this will force a\r
-    #! HTTP 302 to occur to instruct the browser to forward to\r
-    #! the request URL.\r
-    <temporary-redirect> exit-with ;\r
-\r
-: cont-id "factorcontid" ;\r
-\r
-: forward-to-id ( id -- * )\r
-    #! When executed inside a 'show' call, this will force a\r
-    #! HTTP 302 to occur to instruct the browser to forward to\r
-    #! the request URL.\r
-    f swap cont-id associate forward-to-url ;\r
-\r
-: restore-request ( pair -- )\r
-    first3 exit-continuation set request set call ;\r
-\r
-SYMBOL: post-refresh-get?\r
-\r
-: redirect-to-here ( -- )\r
-    #! Force a redirect to the client browser so that the browser\r
-    #! goes to the current point in the code. This forces an URL\r
-    #! change on the browser so that refreshing that URL will\r
-    #! immediately run from this code point. This prevents the\r
-    #! "this request will issue a POST" warning from the browser\r
-    #! and prevents re-running the previous POST logic. This is\r
-    #! known as the 'post-refresh-get' pattern.\r
-    post-refresh-get? get [\r
-        [\r
-            [ ] t register-callback forward-to-id\r
-        ] callcc1 restore-request\r
-    ] [\r
-        post-refresh-get? on\r
-    ] if ;\r
-\r
-SYMBOL: current-show\r
-\r
-: store-current-show ( -- )\r
-    #! Store the current continuation in the variable 'current-show'\r
-    #! so it can be returned to later by 'quot-id'. Note that it\r
-    #! recalls itself when the continuation is called to ensure that\r
-    #! it resets its value back to the most recent show call.\r
-    [ current-show set f ] callcc1\r
-    [ restore-request store-current-show ] when* ;\r
-\r
-: show-final ( quot -- * )\r
-    [ redirect-to-here store-current-show ] dip\r
-    call exit-with ; inline\r
-\r
-: resuming-callback ( responder request -- id )\r
-    cont-id query-param swap callbacks>> at ;\r
-\r
-M: callback-responder call-responder* ( path responder -- response )\r
-    '[\r
-        , ,\r
-\r
-        [ callback-responder set ]\r
-        [ request get resuming-callback ] bi\r
-\r
-        [\r
-            invoke-callback\r
-        ] [\r
-            callback-responder get responder>> call-responder\r
-        ] ?if\r
-    ] with-exit-continuation ;\r
-\r
-: show-page ( quot -- )\r
-    [ redirect-to-here store-current-show ] dip\r
-    [\r
-        [ ] t register-callback swap call exit-with\r
-    ] callcc1 restore-request ; inline\r
-\r
-: quot-id ( quot -- id )\r
-    current-show get swap t register-callback ;\r
-\r
-: quot-url ( quot -- url )\r
-    quot-id f swap cont-id associate derive-url ;\r
index 20eb7318d0d6fc9d6c230418dee028a36d025378..cf8a35f141ce67d1de0f247d024837d0e23820b0 100755 (executable)
@@ -2,7 +2,7 @@
 ! See http://factorcode.org/license.txt for BSD license.\r
 USING: namespaces kernel assocs io.files io.streams.duplex\r
 combinators arrays io.launcher io http.server.static http.server\r
-http accessors sequences strings math.parser fry ;\r
+http accessors sequences strings math.parser fry urls ;\r
 IN: http.server.cgi\r
 \r
 : post? request get method>> "POST" = ;\r
@@ -14,13 +14,12 @@ IN: http.server.cgi
         "HTTP/" request get version>> append "SERVER_PROTOCOL" set\r
         "Factor" "SERVER_SOFTWARE" set\r
 \r
-        dup "PATH_TRANSLATED" set\r
-        "SCRIPT_FILENAME" set\r
+        [ "PATH_TRANSLATED" set ] [ "SCRIPT_FILENAME" set ] bi\r
 \r
-        request get path>> "SCRIPT_NAME" set\r
+        request get url>> path>> "SCRIPT_NAME" set\r
 \r
-        request get host>> "SERVER_NAME" set\r
-        request get port>> number>string "SERVER_PORT" set\r
+        request get url>> host>> "SERVER_NAME" set\r
+        request get url>> port>> number>string "SERVER_PORT" set\r
         "" "PATH_INFO" set\r
         "" "REMOTE_HOST" set\r
         "" "REMOTE_ADDR" set\r
@@ -29,7 +28,7 @@ IN: http.server.cgi
         "" "REMOTE_IDENT" set\r
 \r
         request get method>> "REQUEST_METHOD" set\r
-        request get query>> assoc>query "QUERY_STRING" set\r
+        request get url>> query>> assoc>query "QUERY_STRING" set\r
         request get "cookie" header "HTTP_COOKIE" set \r
 \r
         request get "user-agent" header "HTTP_USER_AGENT" set\r
diff --git a/extra/http/server/db/db-tests.factor b/extra/http/server/db/db-tests.factor
deleted file mode 100644 (file)
index 0c34745..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-IN: http.server.db.tests
-USING: tools.test http.server.db ;
-
-\ <db-persistence> must-infer
diff --git a/extra/http/server/db/db.factor b/extra/http/server/db/db.factor
deleted file mode 100755 (executable)
index 73d4c35..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.\r
-! See http://factorcode.org/license.txt for BSD license.\r
-USING: db db.pools io.pools http.server http.server.sessions\r
-kernel accessors continuations namespaces destructors ;\r
-IN: http.server.db\r
-\r
-TUPLE: db-persistence < filter-responder pool ;\r
-\r
-: <db-persistence> ( responder params db -- responder' )\r
-    <db-pool> db-persistence boa ;\r
-\r
-M: db-persistence call-responder*\r
-    [\r
-        pool>> [ acquire-connection ] keep\r
-        [ return-connection-later ] [ drop db set ] 2bi\r
-    ]\r
-    [ call-next-method ] bi ;\r
diff --git a/extra/http/server/dispatchers/dispatchers-tests.factor b/extra/http/server/dispatchers/dispatchers-tests.factor
new file mode 100644 (file)
index 0000000..5b5b30a
--- /dev/null
@@ -0,0 +1,97 @@
+USING: http.server http.server.dispatchers http.server.responses
+tools.test kernel namespaces accessors io http math sequences
+assocs arrays classes words urls ;
+IN: http.server.dispatchers.tests
+
+\ find-responder must-infer
+\ http-error. must-infer
+
+TUPLE: mock-responder path ;
+
+C: <mock-responder> mock-responder
+
+M: mock-responder call-responder*
+    nip
+    path>> on
+    [ ] "text/plain" <content> ;
+
+: check-dispatch ( tag path -- ? )
+    V{ } clone responder-nesting set
+    over off
+    split-path
+    main-responder get call-responder
+    write-response get ;
+
+[
+    <dispatcher>
+        "foo" <mock-responder> "foo" add-responder
+        "bar" <mock-responder> "bar" add-responder
+        <dispatcher>
+            "123" <mock-responder> "123" add-responder
+            "default" <mock-responder> >>default
+        "baz" add-responder
+    main-responder set
+
+    [ "foo" ] [
+        { "foo" } main-responder get find-responder path>> nip
+    ] unit-test
+
+    [ "bar" ] [
+        { "bar" } main-responder get find-responder path>> nip
+    ] unit-test
+
+    [ t ] [ "foo" "foo" check-dispatch ] unit-test
+    [ f ] [ "foo" "bar" check-dispatch ] unit-test
+    [ t ] [ "bar" "bar" check-dispatch ] unit-test
+    [ t ] [ "default" "baz/xxx" check-dispatch ] unit-test
+    [ t ] [ "default" "baz/xxx//" check-dispatch ] unit-test
+    [ t ] [ "default" "/baz/xxx//" check-dispatch ] unit-test
+    [ t ] [ "123" "baz/123" check-dispatch ] unit-test
+    [ t ] [ "123" "baz///123" check-dispatch ] unit-test
+
+] with-scope
+
+[
+    <dispatcher>
+        "default" <mock-responder> >>default
+    main-responder set
+
+    [ "/default" ] [ "/default" main-responder get find-responder drop ] unit-test
+] with-scope
+
+! Make sure path for default responder isn't chopped
+TUPLE: path-check-responder ;
+
+C: <path-check-responder> path-check-responder
+
+M: path-check-responder call-responder*
+    drop
+    >array "text/plain" <content> ;
+
+[ { "c" } ] [
+    V{ } clone responder-nesting set
+
+    { "b" "c" }
+    <dispatcher>
+        <dispatcher>
+            <path-check-responder> >>default
+        "b" add-responder
+    call-responder
+    body>>
+] unit-test
+
+! Test that "" dispatcher works with default>>
+[ ] [
+    <dispatcher>
+        "" <mock-responder> "" add-responder
+        "bar" <mock-responder> "bar" add-responder
+        "baz" <mock-responder> >>default
+    main-responder set
+
+    [ t ] [ "" "" check-dispatch ] unit-test
+    [ f ] [ "" "quux" check-dispatch ] unit-test
+    [ t ] [ "baz" "quux" check-dispatch ] unit-test
+    [ f ] [ "foo" "bar" check-dispatch ] unit-test
+    [ t ] [ "bar" "bar" check-dispatch ] unit-test
+    [ t ] [ "baz" "xxx" check-dispatch ] unit-test
+] unit-test
diff --git a/extra/http/server/dispatchers/dispatchers.factor b/extra/http/server/dispatchers/dispatchers.factor
new file mode 100644 (file)
index 0000000..36eb447
--- /dev/null
@@ -0,0 +1,47 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel namespaces sequences assocs accessors
+http http.server http.server.responses ;
+IN: http.server.dispatchers
+
+TUPLE: dispatcher default responders ;
+
+: new-dispatcher ( class -- dispatcher )
+    new
+        <404> <trivial-responder> >>default
+        H{ } clone >>responders ; inline
+
+: <dispatcher> ( -- dispatcher )
+    dispatcher new-dispatcher ;
+
+: find-responder ( path dispatcher -- path responder )
+    over empty? [
+        "" over responders>> at*
+        [ nip ] [ drop default>> ] if
+    ] [
+        over first over responders>> at*
+        [ [ drop rest-slice ] dip ] [ drop default>> ] if
+    ] if ;
+
+M: dispatcher call-responder* ( path dispatcher -- response )
+    find-responder call-responder ;
+
+TUPLE: vhost-dispatcher default responders ;
+
+: <vhost-dispatcher> ( -- dispatcher )
+    vhost-dispatcher new-dispatcher ;
+
+: find-vhost ( dispatcher -- responder )
+    request get url>> host>> over responders>> at*
+    [ nip ] [ drop default>> ] if ;
+
+M: vhost-dispatcher call-responder* ( path dispatcher -- response )
+    find-vhost call-responder ;
+
+: add-responder ( dispatcher responder path -- dispatcher )
+    pick responders>> set-at ;
+
+: add-main-responder ( dispatcher responder path -- dispatcher )
+    [ add-responder drop ]
+    [ drop "" add-responder drop ]
+    [ 2drop ] 3tri ;
diff --git a/extra/http/server/filters/filters.factor b/extra/http/server/filters/filters.factor
new file mode 100644 (file)
index 0000000..4f70113
--- /dev/null
@@ -0,0 +1,9 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: http.server accessors ;
+IN: http.server.filters
+
+TUPLE: filter-responder responder ;
+
+M: filter-responder call-responder*
+    responder>> call-responder ;
diff --git a/extra/http/server/flows/flows.factor b/extra/http/server/flows/flows.factor
deleted file mode 100644 (file)
index 7a9b362..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: accessors namespaces sequences arrays kernel
-assocs assocs.lib hashtables math.parser
-html.elements http http.server http.server.sessions ;
-IN: http.server.flows
-
-TUPLE: flows < filter-responder ;
-
-C: <flows> flows
-
-: begin-flow* ( -- id )
-    request get
-    [ path>> ] [ request-params ] [ method>> ] tri 3array
-    flows sget set-at-unique
-    session-changed ;
-
-: end-flow-post ( path params -- response )
-    request [
-        clone
-            "POST" >>method
-            swap >>post-data
-            swap >>path
-    ] change
-    request get path>> split-path
-    flows get responder>> call-responder ;
-
-: end-flow* ( default id -- response )
-    flows sget at
-    [ first3 "POST" = [ end-flow-post ] [ <standard-redirect> ] if ]
-    [ f <standard-redirect> ] ?if ;
-
-SYMBOL: flow-id
-
-: flow-id-key "factorflowid" ;
-
-: begin-flow ( -- )
-    begin-flow* flow-id set ;
-
-: end-flow ( default -- response )
-    flow-id get end-flow* ;
-
-: add-flow-id ( query -- query' )
-    flow-id get [ flow-id-key associate assoc-union ] when* ;
-
-: flow-form-field ( -- )
-    flow-id get [
-        <input
-            "hidden" =type
-            flow-id-key =name
-            =value
-        input/>
-    ] when* ;
-
-M: flows call-responder*
-    dup flows set
-    [ add-flow-id ] add-link-hook
-    [ flow-form-field ] add-form-hook
-    flow-id-key request get request-params at flow-id set
-    call-next-method ;
-
-M: flows init-session*
-    H{ } clone flows sset
-    call-next-method ;
diff --git a/extra/http/server/redirection/redirection-tests.factor b/extra/http/server/redirection/redirection-tests.factor
new file mode 100644 (file)
index 0000000..0b88231
--- /dev/null
@@ -0,0 +1,48 @@
+IN: http.server.redirection.tests
+USING: http http.server.redirection urls accessors
+namespaces tools.test ;
+
+\ relative-to-request must-infer
+
+[
+    <request>
+        <url>
+            "http" >>protocol
+            "www.apple.com" >>host
+            "/xxx/bar" >>path
+            { { "a" "b" } } >>query
+        >>url
+    request set
+
+    [ "http://www.apple.com:80/xxx/bar" ] [ 
+        <url> relative-to-request url>string 
+    ] unit-test
+
+    [ "http://www.apple.com:80/xxx/baz" ] [
+        <url> "baz" >>path relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.apple.com:80/xxx/baz?c=d" ] [
+        <url> "baz" >>path { { "c" "d" } } >>query relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.apple.com:80/xxx/bar?c=d" ] [
+        <url> { { "c" "d" } } >>query relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.apple.com:80/flip" ] [
+        <url> "/flip" >>path relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.apple.com:80/flip?c=d" ] [
+        <url> "/flip" >>path { { "c" "d" } } >>query relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.jedit.org:80/" ] [
+        "http://www.jedit.org" >url relative-to-request url>string
+    ] unit-test
+    
+    [ "http://www.jedit.org:80/?a=b" ] [
+        "http://www.jedit.org" >url { { "a" "b" } } >>query relative-to-request url>string
+    ] unit-test
+] with-scope
diff --git a/extra/http/server/redirection/redirection.factor b/extra/http/server/redirection/redirection.factor
new file mode 100644 (file)
index 0000000..3cd0134
--- /dev/null
@@ -0,0 +1,24 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel accessors combinators namespaces
+logging urls http http.server http.server.responses ;
+IN: http.server.redirection
+
+: relative-to-request ( url -- url' )
+    request get url>>
+        clone
+        f >>query
+    swap derive-url ensure-port ;
+
+: <custom-redirect> ( url code message -- response )
+    <trivial-response>
+        swap dup url? [ relative-to-request ] when
+        "location" set-header ;
+
+\ <custom-redirect> DEBUG add-input-logging
+
+: <permanent-redirect> ( url -- response )
+    301 "Moved Permanently" <custom-redirect> ;
+
+: <temporary-redirect> ( url -- response )
+    307 "Temporary Redirect" <custom-redirect> ;
diff --git a/extra/http/server/responses/responses.factor b/extra/http/server/responses/responses.factor
new file mode 100644 (file)
index 0000000..277ca39
--- /dev/null
@@ -0,0 +1,37 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: html.elements math.parser http accessors kernel
+io io.streams.string ;
+IN: http.server.responses
+
+: <content> ( body content-type -- response )
+    <response>
+        200 >>code
+        "Document follows" >>message
+        swap >>content-type
+        swap >>body ;
+    
+: trivial-response-body ( code message -- )
+    <html>
+        <body>
+            <h1> [ number>string write bl ] [ write ] bi* </h1>
+        </body>
+    </html> ;
+
+: <trivial-response> ( code message -- response )
+    2dup [ trivial-response-body ] with-string-writer
+    "text/html" <content>
+        swap >>message
+        swap >>code ;
+
+: <304> ( -- response )
+    304 "Not modified" <trivial-response> ;
+
+: <403> ( -- response )
+    403 "Forbidden" <trivial-response> ;
+
+: <400> ( -- response )
+    400 "Bad request" <trivial-response> ;
+
+: <404> ( -- response )
+    404 "Not found" <trivial-response> ;
diff --git a/extra/http/server/server-tests.factor b/extra/http/server/server-tests.factor
deleted file mode 100755 (executable)
index 0aed425..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-USING: http.server tools.test kernel namespaces accessors
-io http math sequences assocs arrays classes words ;
-IN: http.server.tests
-
-\ find-responder must-infer
-
-[
-    <request>
-    http >>protocol
-    "www.apple.com" >>host
-    "/xxx/bar" >>path
-    { { "a" "b" } } >>query
-    request set
-
-    [ ] link-hook set
-
-    [ "http://www.apple.com:80/xxx/bar?a=b" ] [ f f derive-url ] unit-test
-    [ "http://www.apple.com:80/xxx/baz?a=b" ] [ "baz" f derive-url ] unit-test
-    [ "http://www.apple.com:80/xxx/baz?c=d" ] [ "baz" { { "c" "d" } } derive-url ] unit-test
-    [ "http://www.apple.com:80/xxx/bar?c=d" ] [ f { { "c" "d" } } derive-url ] unit-test
-    [ "http://www.apple.com:80/flip?a=b" ] [ "/flip" f derive-url ] unit-test
-    [ "http://www.apple.com:80/flip?c=d" ] [ "/flip" { { "c" "d" } } derive-url ] unit-test
-    [ "http://www.jedit.org" ] [ "http://www.jedit.org" f derive-url ] unit-test
-    [ "http://www.jedit.org?a=b" ] [ "http://www.jedit.org" { { "a" "b" } } derive-url ] unit-test
-] with-scope
-
-TUPLE: mock-responder path ;
-
-C: <mock-responder> mock-responder
-
-M: mock-responder call-responder*
-    nip
-    path>> on
-    [ ] <text-content> ;
-
-: check-dispatch ( tag path -- ? )
-    H{ } clone base-paths set
-    over off
-    split-path
-    main-responder get call-responder
-    write-response get ;
-
-[
-    <dispatcher>
-        "foo" <mock-responder> "foo" add-responder
-        "bar" <mock-responder> "bar" add-responder
-        <dispatcher>
-            "123" <mock-responder> "123" add-responder
-            "default" <mock-responder> >>default
-        "baz" add-responder
-    main-responder set
-
-    [ "foo" ] [
-        { "foo" } main-responder get find-responder path>> nip
-    ] unit-test
-
-    [ "bar" ] [
-        { "bar" } main-responder get find-responder path>> nip
-    ] unit-test
-
-    [ t ] [ "foo" "foo" check-dispatch ] unit-test
-    [ f ] [ "foo" "bar" check-dispatch ] unit-test
-    [ t ] [ "bar" "bar" check-dispatch ] unit-test
-    [ t ] [ "default" "baz/xxx" check-dispatch ] unit-test
-    [ t ] [ "default" "baz/xxx//" check-dispatch ] unit-test
-    [ t ] [ "default" "/baz/xxx//" check-dispatch ] unit-test
-    [ t ] [ "123" "baz/123" check-dispatch ] unit-test
-    [ t ] [ "123" "baz///123" check-dispatch ] unit-test
-
-] with-scope
-
-[
-    <dispatcher>
-        "default" <mock-responder> >>default
-    main-responder set
-
-    [ "/default" ] [ "/default" main-responder get find-responder drop ] unit-test
-] with-scope
-
-! Make sure path for default responder isn't chopped
-TUPLE: path-check-responder ;
-
-C: <path-check-responder> path-check-responder
-
-M: path-check-responder call-responder*
-    drop
-    >array <text-content> ;
-
-[ { "c" } ] [
-    H{ } clone base-paths set
-
-    { "b" "c" }
-    <dispatcher>
-        <dispatcher>
-            <path-check-responder> >>default
-        "b" add-responder
-    call-responder
-    body>>
-] unit-test
-
-! Test that "" dispatcher works with default>>
-[ ] [
-    <dispatcher>
-        "" <mock-responder> "" add-responder
-        "bar" <mock-responder> "bar" add-responder
-        "baz" <mock-responder> >>default
-    main-responder set
-
-    [ t ] [ "" "" check-dispatch ] unit-test
-    [ f ] [ "" "quux" check-dispatch ] unit-test
-    [ t ] [ "baz" "quux" check-dispatch ] unit-test
-    [ f ] [ "foo" "bar" check-dispatch ] unit-test
-    [ t ] [ "bar" "bar" check-dispatch ] unit-test
-    [ t ] [ "baz" "xxx" check-dispatch ] unit-test
-] unit-test
-
-TUPLE: funny-dispatcher < dispatcher ;
-
-: <funny-dispatcher> funny-dispatcher new-dispatcher ;
-
-TUPLE: base-path-check-responder ;
-
-C: <base-path-check-responder> base-path-check-responder
-
-M: base-path-check-responder call-responder*
-    2drop
-    "$funny-dispatcher" resolve-base-path
-    <text-content> ;
-
-[ ] [
-    <dispatcher>
-        <dispatcher>
-            <funny-dispatcher>
-                <base-path-check-responder> "c" add-responder
-            "b" add-responder
-        "a" add-responder
-    main-responder set
-] unit-test
-
-[ "/a/b/" ] [
-    "a/b/c" split-path main-responder get call-responder body>>
-] unit-test
index d68c66b829643629a4060e740c668e424069818d..02424ef97442e0dc9f13c7b323a512f5c683c4a4 100755 (executable)
 ! Copyright (C) 2003, 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: assocs kernel namespaces io io.timeouts strings splitting
-threads sequences prettyprint io.server logging calendar http
-html.streams html.elements accessors math.parser
-combinators.lib tools.vocabs debugger continuations random
-combinators destructors io.encodings.8-bit fry classes words
-math rss json.writer ;
+USING: kernel accessors sequences arrays namespaces splitting
+vocabs.loader http http.server.responses logging calendar
+destructors html.elements html.streams io.server
+io.encodings.8-bit io.timeouts io assocs debugger continuations
+fry tools.vocabs math ;
 IN: http.server
 
-! path is a sequence of path component strings
-
-GENERIC: call-responder* ( path responder -- response )
-
-: request-params ( request -- assoc )
-    dup method>> {
-        { "GET" [ query>> ] }
-        { "HEAD" [ query>> ] }
-        { "POST" [ post-data>> ] }
-    } case ;
+SYMBOL: responder-nesting
 
-: <content> ( body content-type -- response )
-    <response>
-        200 >>code
-        "Document follows" >>message
-        swap >>content-type
-        swap >>body ;
-
-: <text-content> ( body -- response )
-    "text/plain" <content> ;
-
-: <html-content> ( body -- response )
-    "text/html" <content> ;
-
-: <xml-content> ( body -- response )
-    "text/xml" <content> ;
+SYMBOL: main-responder
 
-: <feed-content> ( feed -- response )
-    '[ , feed>xml ] "text/xml" <content> ;
+SYMBOL: development-mode
 
-: <json-content> ( obj -- response )
-    '[ , >json ] "application/json" <content> ;
+! path is a sequence of path component strings
+GENERIC: call-responder* ( path responder -- response )
 
 TUPLE: trivial-responder response ;
 
 C: <trivial-responder> trivial-responder
 
-M: trivial-responder call-responder* nip response>> call ;
-
-: trivial-response-body ( code message -- )
-    <html>
-        <body>
-            <h1> [ number>string write bl ] [ write ] bi* </h1>
-        </body>
-    </html> ;
-
-: <trivial-response> ( code message -- response )
-    2dup '[ , , trivial-response-body ] <html-content>
-        swap >>message
-        swap >>code ;
-
-: <400> ( -- response )
-    400 "Bad request" <trivial-response> ;
-
-: <404> ( -- response )
-    404 "Not Found" <trivial-response> ;
+M: trivial-responder call-responder* nip response>> clone ;
 
-SYMBOL: 404-responder
-
-[ <404> ] <trivial-responder> 404-responder set-global
-
-SYMBOL: base-paths
+main-responder global [ <404> <trivial-responder> get-global or ] change-at
 
 : invert-slice ( slice -- slice' )
-    dup slice? [
-        [ seq>> ] [ from>> ] bi head-slice
-    ] [
-        drop { }
-    ] if ;
+    dup slice? [ [ seq>> ] [ from>> ] bi head-slice ] [ drop { } ] if ;
 
-: add-base-path ( path dispatcher -- )
-    [ invert-slice ] [ class word-name ] bi*
-    base-paths get set-at ;
+: add-responder-nesting ( path responder -- )
+    [ invert-slice ] dip 2array responder-nesting get push ;
 
 : call-responder ( path responder -- response )
-    [ add-base-path ] [ call-responder* ] 2bi ;
-
-SYMBOL: link-hook
-
-: add-link-hook ( quot -- )
-    link-hook [ compose ] change ; inline
-
-: modify-query ( query -- query )
-    link-hook get call ;
-
-: base-path ( string -- path )
-    dup base-paths get at
-    [ ] [ "No such responder: " swap append throw ] ?if ;
-
-: resolve-base-path ( string -- string' )
-    "$" ?head [
-        [
-            "/" split1 [ base-path [ "/" % % ] each "/" % ] dip %
-        ] "" make
-    ] when ;
-
-: link>string ( url query -- url' )
-    [ resolve-base-path ] [ modify-query ] bi* (link>string) ;
-
-: write-link ( url query -- )
-    link>string write ;
-
-SYMBOL: form-hook
-
-: add-form-hook ( quot -- )
-    form-hook [ compose ] change ;
-
-: hidden-form-field ( -- )
-    form-hook get call ;
-
-: absolute-redirect ( to query -- url )
-    #! Same host.
-    request get clone
-    swap [ >>query ] when*
-    swap url-encode >>path
-    [ modify-query ] change-query
-    request-url ;
-
-: replace-last-component ( path with -- path' )
-    [ "/" last-split1 drop "/" ] dip 3append ;
-
-: relative-redirect ( to query -- url )
-    request get clone
-    swap [ >>query ] when*
-    swap [ '[ , replace-last-component ] change-path ] when*
-    [ modify-query ] change-query
-    request-url ;
-
-: derive-url ( to query -- url )
-    {
-        { [ over "http://" head? ] [ link>string ] }
-        { [ over "/" head? ] [ absolute-redirect ] }
-        { [ over "$" head? ] [ [ resolve-base-path ] dip derive-url ] }
-        [ relative-redirect ]
-    } cond ;
-
-: <redirect> ( to query code message -- response )
-    <trivial-response> -rot derive-url "location" set-header ;
-
-\ <redirect> DEBUG add-input-logging
-
-: <permanent-redirect> ( to query -- response )
-    301 "Moved Permanently" <redirect> ;
-
-: <temporary-redirect> ( to query -- response )
-    307 "Temporary Redirect" <redirect> ;
-
-: <standard-redirect> ( to query -- response )
-    request get method>> "POST" =
-    [ <permanent-redirect> ] [ <temporary-redirect> ] if ;
-
-TUPLE: dispatcher default responders ;
-
-: new-dispatcher ( class -- dispatcher )
-    new
-        404-responder get >>default
-        H{ } clone >>responders ; inline
-
-: <dispatcher> ( -- dispatcher )
-    dispatcher new-dispatcher ;
-
-: find-responder ( path dispatcher -- path responder )
-    over empty? [
-        "" over responders>> at*
-        [ nip ] [ drop default>> ] if
-    ] [
-        over first over responders>> at*
-        [ [ drop rest-slice ] dip ] [ drop default>> ] if
-    ] if ;
-
-M: dispatcher call-responder* ( path dispatcher -- response )
-    find-responder call-responder ;
-
-TUPLE: vhost-dispatcher default responders ;
-
-: <vhost-dispatcher> ( -- dispatcher )
-    404-responder get H{ } clone vhost-dispatcher boa ;
-
-: find-vhost ( dispatcher -- responder )
-    request get host>> over responders>> at*
-    [ nip ] [ drop default>> ] if ;
-
-M: vhost-dispatcher call-responder* ( path dispatcher -- response )
-    find-vhost call-responder ;
-
-: add-responder ( dispatcher responder path -- dispatcher )
-    pick responders>> set-at ;
-
-: add-main-responder ( dispatcher responder path -- dispatcher )
-    [ add-responder drop ]
-    [ drop "" add-responder drop ]
-    [ 2drop ] 3tri ;
-
-TUPLE: filter-responder responder ;
-
-M: filter-responder call-responder*
-    responder>> call-responder ;
-
-SYMBOL: main-responder
-
-main-responder global
-[ drop 404-responder get-global ] cache
-drop
-
-SYMBOL: development-mode
+    [ add-responder-nesting ] [ call-responder* ] 2bi ;
 
 : http-error. ( error -- )
     "Internal server error" [
-        development-mode get [
-            [ print-error nl :c ] with-html-stream
-        ] [
-            500 "Internal server error"
-            trivial-response-body
-        ] if
+        [ print-error nl :c ] with-html-stream
     ] simple-page ;
 
 : <500> ( error -- response )
     500 "Internal server error" <trivial-response>
-    swap '[ , http-error. ] >>body ;
+    development-mode get [ swap '[ , http-error. ] >>body ] [ drop ] if ;
 
 : do-response ( response -- )
     dup write-response
     request get method>> "HEAD" =
-    [ drop ] [
-        '[
-            , write-response-body
-        ] [
-            http-error.
-        ] recover
-    ] if ;
+    [ drop ] [ '[ , write-response-body ] [ http-error. ] recover ] if ;
 
 LOG: httpd-hit NOTICE
 
 : log-request ( request -- )
-    { method>> host>> path>> } map-exec-with httpd-hit ;
-
-SYMBOL: exit-continuation
-
-: exit-with exit-continuation get continue-with ;
-
-: with-exit-continuation ( quot -- )
-    '[ exit-continuation set @ ] callcc1 exit-continuation off ;
+    [ method>> ] [ url>> [ host>> ] [ path>> ] bi ] bi 3array httpd-hit ;
 
 : split-path ( string -- path )
     "/" split harvest ;
 
-: init-request ( -- )
-    H{ } clone base-paths set
-    [ ] link-hook set
-    [ ] form-hook set ;
+: init-request ( request -- )
+    request set
+    V{ } clone responder-nesting set ;
+
+: dispatch-request ( request -- response )
+    url>> path>> split-path main-responder get call-responder ;
 
 : do-request ( request -- response )
-    [
-        init-request
-        [ request set ]
+    '[
+        ,
+        [ init-request ]
         [ log-request ]
-        [ path>> split-path main-responder get call-responder ] tri
-        [ <404> ] unless*
-    ] [
-        [ \ do-request log-error ]
-        [ <500> ]
-        bi
-    ] recover ;
+        [ dispatch-request ] tri
+    ] [ [ \ do-request log-error ] [ <500> ] bi ] recover ;
 
 : ?refresh-all ( -- )
     development-mode get-global
@@ -287,8 +85,7 @@ SYMBOL: exit-continuation
 
 : httpd ( port -- )
     dup integer? [ internet-server ] when
-    "http.server" latin1
-    [ handle-client ] with-server ;
+    "http.server" latin1 [ handle-client ] with-server ;
 
 : httpd-main ( -- )
     8888 httpd ;
diff --git a/extra/http/server/sessions/authors.txt b/extra/http/server/sessions/authors.txt
deleted file mode 100755 (executable)
index 7c1b2f2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Doug Coleman
diff --git a/extra/http/server/sessions/sessions-tests.factor b/extra/http/server/sessions/sessions-tests.factor
deleted file mode 100755 (executable)
index 8ea312d..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-IN: http.server.sessions.tests\r
-USING: tools.test http http.server.sessions\r
-http.server.actions http.server math namespaces kernel accessors\r
-prettyprint io.streams.string io.files splitting destructors\r
-sequences db db.sqlite continuations ;\r
-\r
-: with-session\r
-    [\r
-        [ [ save-session-after ] [ session set ] bi ] dip call\r
-    ] with-destructors ; inline\r
-\r
-TUPLE: foo ;\r
-\r
-C: <foo> foo\r
-\r
-M: foo init-session* drop 0 "x" sset ;\r
-\r
-M: foo call-responder*\r
-    2drop\r
-    "x" [ 1+ ] schange\r
-    [ "x" sget pprint ] <html-content> ;\r
-\r
-: url-responder-mock-test\r
-    [\r
-        <request>\r
-            "GET" >>method\r
-            "id" get session-id-key set-query-param\r
-            "/" >>path\r
-        request set\r
-        { } sessions get call-responder\r
-        [ write-response-body drop ] with-string-writer\r
-    ] with-destructors ;\r
-\r
-: sessions-mock-test\r
-    [\r
-        <request>\r
-            "GET" >>method\r
-            "cookies" get >>cookies\r
-            "/" >>path\r
-        request set\r
-        { } sessions get call-responder\r
-        [ write-response-body drop ] with-string-writer\r
-    ] with-destructors ;\r
-\r
-: <exiting-action>\r
-    <action>\r
-        [ [ ] <text-content> exit-with ] >>display ;\r
-\r
-[ "auth-test.db" temp-file sqlite-db delete-file ] ignore-errors\r
-\r
-"auth-test.db" temp-file sqlite-db [\r
-\r
-    init-request\r
-    init-sessions-table\r
-\r
-    [ ] [\r
-        <foo> <sessions>\r
-        sessions set\r
-    ] unit-test\r
-\r
-    [\r
-        [ ] [\r
-            empty-session\r
-                123 >>id session set\r
-        ] unit-test\r
-\r
-        [ ] [ 3 "x" sset ] unit-test\r
-\r
-        [ 9 ] [ "x" sget sq ] unit-test\r
-\r
-        [ ] [ "x" [ 1- ] schange ] unit-test\r
-\r
-        [ 4 ] [ "x" sget sq ] unit-test\r
-\r
-        [ t ] [ session get changed?>> ] unit-test\r
-    ] with-scope\r
-\r
-    [ t ] [\r
-        begin-session id>>\r
-        get-session session?\r
-    ] unit-test\r
-\r
-    [ { 5 0 } ] [\r
-        [\r
-            begin-session\r
-            dup [ 5 "a" sset ] with-session\r
-            dup [ "a" sget , ] with-session\r
-            dup [ "x" sget , ] with-session\r
-            drop\r
-        ] { } make\r
-    ] unit-test\r
-\r
-    [ 0 ] [\r
-        begin-session id>>\r
-        get-session [ "x" sget ] with-session\r
-    ] unit-test\r
-\r
-    [ { 5 0 } ] [\r
-        [\r
-            begin-session id>>\r
-            dup get-session [ 5 "a" sset ] with-session\r
-            dup get-session [ "a" sget , ] with-session\r
-            dup get-session [ "x" sget , ] with-session\r
-            drop\r
-        ] { } make\r
-    ] unit-test\r
-\r
-    [ ] [\r
-        <foo> <sessions>\r
-        sessions set\r
-    ] unit-test\r
-\r
-    [\r
-        <request>\r
-        "GET" >>method\r
-        "/" >>path\r
-        request set\r
-        { "etc" } sessions get call-responder response set\r
-        [ "1" ] [ [ response get write-response-body drop ] with-string-writer ] unit-test\r
-        response get\r
-    ] with-destructors\r
-    response set\r
-\r
-    [ ] [ response get cookies>> "cookies" set ] unit-test\r
-\r
-    [ "2" ] [ sessions-mock-test ] unit-test\r
-    [ "3" ] [ sessions-mock-test ] unit-test\r
-    [ "4" ] [ sessions-mock-test ] unit-test\r
-\r
-    [\r
-        [ ] [\r
-            <request>\r
-                "GET" >>method\r
-                "id" get session-id-key set-query-param\r
-                "/" >>path\r
-            request set\r
-\r
-            [\r
-                { } <exiting-action> <sessions>\r
-                call-responder\r
-            ] with-destructors response set\r
-        ] unit-test\r
-\r
-        [ "text/plain" ] [ response get content-type>> ] unit-test\r
-\r
-        [ f ] [ response get cookies>> empty? ] unit-test\r
-    ] with-scope\r
-] with-db\r
diff --git a/extra/http/server/sessions/sessions.factor b/extra/http/server/sessions/sessions.factor
deleted file mode 100755 (executable)
index a7e1a14..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-! Copyright (C) 2008 Doug Coleman, Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: assocs kernel math.intervals math.parser namespaces
-random accessors quotations hashtables sequences continuations
-fry calendar combinators destructors alarms
-db db.tuples db.types
-http http.server html.elements ;
-IN: http.server.sessions
-
-TUPLE: session id expires uid namespace changed? ;
-
-: <session> ( id -- session )
-    session new
-        swap >>id ;
-
-session "SESSIONS"
-{
-    { "id" "ID" +random-id+ system-random-generator }
-    { "expires" "EXPIRES" TIMESTAMP +not-null+ }
-    { "uid" "UID" { VARCHAR 255 } }
-    { "namespace" "NAMESPACE" FACTOR-BLOB }
-} define-persistent
-
-: get-session ( id -- session )
-    dup [ <session> select-tuple ] when ;
-
-: init-sessions-table session ensure-table ;
-
-: start-expiring-sessions ( db seq -- )
-    '[
-        , , [
-            session new
-                -1.0/0.0 now [a,b] >>expires
-            delete-tuples
-        ] with-db
-    ] 5 minutes every drop ;
-
-GENERIC: init-session* ( responder -- )
-
-M: object init-session* drop ;
-
-M: dispatcher init-session* default>> init-session* ;
-
-M: filter-responder init-session* responder>> init-session* ;
-
-TUPLE: sessions < filter-responder timeout domain ;
-
-: <sessions> ( responder -- responder' )
-    sessions new
-        swap >>responder
-        20 minutes >>timeout ;
-
-: (session-changed) ( session -- )
-    t >>changed? drop ;
-
-: session-changed ( -- )
-    session get (session-changed) ;
-
-: sget ( key -- value )
-    session get namespace>> at ;
-
-: sset ( value key -- )
-    session get
-    [ namespace>> set-at ] [ (session-changed) ] bi ;
-
-: schange ( key quot -- )
-    session get
-    [ namespace>> swap change-at ] keep
-    (session-changed) ; inline
-
-: uid ( -- uid )
-    session get uid>> ;
-
-: set-uid ( uid -- )
-    session get [ (>>uid) ] [ (session-changed) ] bi ;
-
-: init-session ( session -- )
-    session [ sessions get init-session* ] with-variable ;
-
-: cutoff-time ( -- time )
-    sessions get timeout>> from-now ;
-
-: touch-session ( session -- )
-    cutoff-time >>expires drop ;
-
-: empty-session ( -- session )
-    f <session>
-        H{ } clone >>namespace
-        dup touch-session ;
-
-: begin-session ( -- session )
-    empty-session [ init-session ] [ insert-tuple ] [ ] tri ;
-
-! Destructor
-TUPLE: session-saver session ;
-
-C: <session-saver> session-saver
-
-M: session-saver dispose
-    session>> dup changed?>> [
-        [ touch-session ] [ update-tuple ] bi
-    ] [ drop ] if ;
-
-: save-session-after ( session -- )
-    <session-saver> &dispose drop ;
-
-: existing-session ( path session -- response )
-    [ session set ] [ save-session-after ] bi
-    sessions get responder>> call-responder ;
-
-: session-id-key "factorsessid" ;
-
-: cookie-session-id ( request -- id/f )
-    session-id-key get-cookie
-    dup [ value>> string>number ] when ;
-
-: post-session-id ( request -- id/f )
-    session-id-key swap post-data>> at string>number ;
-
-: request-session-id ( -- id/f )
-    request get dup method>> {
-        { "GET" [ cookie-session-id ] }
-        { "HEAD" [ cookie-session-id ] }
-        { "POST" [ post-session-id ] }
-    } case ;
-
-: request-session ( -- session/f )
-    request-session-id get-session ;
-
-: <session-cookie> ( id -- cookie )
-    session-id-key <cookie>
-        "$sessions" resolve-base-path >>path
-        sessions get timeout>> from-now >>expires
-        sessions get domain>> >>domain ;
-
-: put-session-cookie ( response -- response' )
-    session get id>> number>string <session-cookie> put-cookie ;
-
-: session-form-field ( -- )
-    <input
-        "hidden" =type
-        session-id-key =name
-        session get id>> number>string =value
-    input/> ;
-
-M: sessions call-responder* ( path responder -- response )
-    [ session-form-field ] add-form-hook
-    sessions set
-    request-session [ begin-session ] unless*
-    existing-session put-session-cookie ;
-
-: logout-all-sessions ( uid -- )
-    session new swap >>uid delete-tuples ;
index 8814004589529f0e308b378e608f40d2fb58e657..1d86a73cfa322c647d6d5c8ea3a4a848321c44d6 100755 (executable)
@@ -1,10 +1,15 @@
 ! Copyright (C) 2004, 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
 USING: calendar io io.files kernel math math.order\r
-math.parser http http.server namespaces parser sequences strings\r
-assocs hashtables debugger http.mime sorting html.elements\r
-html.templates.fhtml logging calendar.format accessors\r
-io.encodings.binary fry xml.entities destructors ;\r
+math.parser namespaces parser sequences strings\r
+assocs hashtables debugger mime-types sorting logging\r
+calendar.format accessors\r
+io.encodings.binary fry xml.entities destructors urls\r
+html.elements html.templates.fhtml\r
+http\r
+http.server\r
+http.server.responses\r
+http.server.redirection ;\r
 IN: http.server.static\r
 \r
 ! special maps mime types to quots with effect ( path -- )\r
@@ -17,12 +22,6 @@ TUPLE: file-responder root hook special allow-listings ;
         2drop t\r
     ] if ;\r
 \r
-: <304> ( -- response )\r
-    304 "Not modified" <trivial-response> ;\r
-\r
-: <403> ( -- response )\r
-    403 "Forbidden" <trivial-response> ;\r
-\r
 : <file-responder> ( root hook -- responder )\r
     file-responder new\r
         swap >>hook\r
@@ -71,7 +70,7 @@ TUPLE: file-responder root hook special allow-listings ;
 \r
 : list-directory ( directory -- response )\r
     file-responder get allow-listings>> [\r
-        '[ , directory. ] <html-content>\r
+        '[ , directory. ] "text/html" <content>\r
     ] [\r
         drop <403>\r
     ] if ;\r
@@ -85,7 +84,7 @@ TUPLE: file-responder root hook special allow-listings ;
         find-index [ serve-file ] [ list-directory ] ?if\r
     ] [\r
         drop\r
-        request get path>> "/" append f <standard-redirect>\r
+        request get url>> clone [ "/" append ] change-path <permanent-redirect>\r
     ] if ;\r
 \r
 : serve-object ( filename -- response )\r
@@ -101,6 +100,6 @@ M: file-responder call-responder* ( path responder -- response )
 \r
 ! file responder integration\r
 : enable-fhtml ( responder -- responder )\r
-    [ <fhtml> <html-content> ]\r
+    [ <fhtml> "text/html" <content> ]\r
     "application/x-factor-server-page"\r
     pick special>> set-at ;\r
index 7ee14e03e5d3200b26d06041c471dbc689191931..033ba3cbfb12e6465d0c56bdbdb02702683b06a5 100644 (file)
@@ -1,13 +1,22 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors kernel arrays namespaces sequences continuations
-destructors io.sockets ;
+destructors io.sockets alien alien.syntax ;
 IN: io.pools
 
-TUPLE: pool connections disposed ;
+TUPLE: pool connections disposed expired ;
+
+: check-pool ( pool -- )
+    dup check-disposed
+    dup expired>> expired? [
+        ALIEN: 31337 >>expired
+        connections>> [ delete-all ] [ dispose-each ] bi
+    ] [ drop ] if ;
 
 : <pool> ( class -- pool )
-    new V{ } clone >>connections ; inline
+    new V{ } clone
+        >>connections
+        dup check-pool ; inline
 
 M: pool dispose* connections>> dispose-each ;
 
@@ -17,15 +26,14 @@ M: pool dispose* connections>> dispose-each ;
 TUPLE: return-connection conn pool ;
 
 : return-connection ( conn pool -- )
-    dup check-disposed connections>> push ;
+    dup check-pool connections>> push ;
 
 GENERIC: make-connection ( pool -- conn )
 
 : new-connection ( pool -- )
-    [ make-connection ] keep return-connection ;
+    dup check-pool [ make-connection ] keep return-connection ;
 
 : acquire-connection ( pool -- conn )
-    dup check-disposed
     [ dup connections>> empty? ] [ dup new-connection ] [ ] while
     connections>> pop ;
 
index a8f649e2c9172c430fcfe9e0566c2bcdad3ade31..754e69a476fcd5f6ed14eb5ecf2d3a08b8753cbf 100644 (file)
@@ -38,7 +38,7 @@ M: delete diff-line
     </tr> ;
 
 : htmlize-diff ( diff -- )
-    <table "comparison" =class table>
+    <table "100%" =width "comparison" =class table>
         <tr> <th> "Old" write </th> <th> "New" write </th> </tr>
         [ diff-line ] each
     </table> ;
diff --git a/extra/mime-types/authors.txt b/extra/mime-types/authors.txt
new file mode 100755 (executable)
index 0000000..1901f27
--- /dev/null
@@ -0,0 +1 @@
+Slava Pestov
diff --git a/extra/mime-types/mime-types-tests.factor b/extra/mime-types/mime-types-tests.factor
new file mode 100644 (file)
index 0000000..925eca2
--- /dev/null
@@ -0,0 +1,6 @@
+IN: mime-types.tests
+USING: mime-types tools.test ;
+
+[ "application/postscript" ] [ "foo.ps" mime-type ] unit-test
+[ "application/octet-stream" ] [ "foo.ps.gz" mime-type ] unit-test
+[ "text/plain" ] [ "foo.factor" mime-type ] unit-test
diff --git a/extra/mime-types/mime-types.factor b/extra/mime-types/mime-types.factor
new file mode 100755 (executable)
index 0000000..a228a89
--- /dev/null
@@ -0,0 +1,23 @@
+! Copyright (C) 2004, 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: io.files io.encodings.ascii assocs sequences splitting
+kernel namespaces fry memoize ;
+IN: mime-types
+
+MEMO: mime-db ( -- seq )
+    "resource:extra/mime-types/mime.types" ascii file-lines
+    [ "#" head? not ] filter [ " \t" split harvest ] map harvest ;
+
+: nonstandard-mime-types ( -- assoc )
+    H{
+        { "factor" "text/plain"                       }
+        { "cgi"    "application/x-cgi-script"         }
+        { "fhtml"  "application/x-factor-server-page" }
+    } ;
+
+MEMO: mime-types ( -- assoc )
+    [ mime-db [ unclip '[ , _ set ] each ] each ] H{ } make-assoc
+    nonstandard-mime-types assoc-union ;
+
+: mime-type ( filename -- mime-type )
+    file-extension mime-types at "application/octet-stream" or ;
diff --git a/extra/mime-types/mime.types b/extra/mime-types/mime.types
new file mode 100644 (file)
index 0000000..b602e9d
--- /dev/null
@@ -0,0 +1,988 @@
+# This is a comment. I love comments.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s).  Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077.  The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type                                    Extensions
+application/activemessage
+application/andrew-inset                       ez
+application/applefile
+application/atom+xml                           atom
+application/atomcat+xml                                atomcat
+application/atomicmail
+application/atomsvc+xml                                atomsvc
+application/auth-policy+xml
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/ccxml+xml                          ccxml
+application/cellml+xml
+application/cnrp+xml
+application/commonground
+application/conference-info+xml
+application/cpl+xml
+application/csta+xml
+application/cstadata+xml
+application/cybercash
+application/davmount+xml                       davmount
+application/dca-rft
+application/dec-dx
+application/dialog-info+xml
+application/dicom
+application/dns
+application/dvcs
+application/ecmascript                         ecma
+application/edi-consent
+application/edi-x12
+application/edifact
+application/epp+xml
+application/eshop
+application/fastinfoset
+application/fastsoap
+application/fits
+application/font-tdpfr                         pfr
+application/h224
+application/http
+application/hyperstudio                                stk
+application/iges
+application/im-iscomposing+xml
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/javascript                         js
+application/json                               json
+application/kpml-request+xml
+application/kpml-response+xml
+application/mac-binhex40                       hqx
+application/mac-compactpro                     cpt
+application/macwriteii
+application/marc                               mrc
+application/mathematica                                ma nb mb
+application/mathml+xml                         mathml
+application/mbms-associated-procedure-description+xml
+application/mbms-deregister+xml
+application/mbms-envelope+xml
+application/mbms-msk+xml
+application/mbms-msk-response+xml
+application/mbms-protection-description+xml
+application/mbms-reception-report+xml
+application/mbms-register+xml
+application/mbms-register-response+xml
+application/mbms-user-service-description+xml
+application/mbox                               mbox
+application/mediaservercontrol+xml             mscml
+application/mikey
+application/mp4                                        mp4s
+application/mpeg4-generic
+application/mpeg4-iod
+application/mpeg4-iod-xmt
+application/msword                             doc dot
+application/mxf                                        mxf
+application/nasdata
+application/news-message-id
+application/news-transmission
+application/nss
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh class so iso dmg dist distz pkg bpk dump elc scpt
+application/oda                                        oda
+application/oebps-package+xml
+application/ogg                                        ogg
+application/parityfec
+application/pdf                                        pdf
+application/pgp-encrypted                      pgp
+application/pgp-keys
+application/pgp-signature                      asc sig
+application/pics-rules                         prf
+application/pidf+xml
+application/pkcs10                             p10
+application/pkcs7-mime                         p7m p7c
+application/pkcs7-signature                    p7s
+application/pkix-cert                          cer
+application/pkix-crl                           crl
+application/pkix-pkipath                       pkipath
+application/pkixcmp                            pki
+application/pls+xml                            pls
+application/poc-settings+xml
+application/postscript                         ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww                            cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml                            rdf
+application/reginfo+xml                                rif
+application/relax-ng-compact-syntax            rnc
+application/remote-printing
+application/resource-lists+xml                 rl
+application/riscos
+application/rlmi+xml
+application/rls-services+xml                   rs
+application/rsd+xml                            rsd
+application/rss+xml                            rss
+application/rtf                                        rtf
+application/rtx
+application/samlassertion+xml
+application/samlmetadata+xml
+application/sbml+xml                           sbml
+application/sdp                                        sdp
+application/set-payment
+application/set-payment-initiation             setpay
+application/set-registration
+application/set-registration-initiation                setreg
+application/sgml
+application/sgml-open-catalog
+application/shf+xml                            shf
+application/sieve
+application/simple-filter+xml
+application/simple-message-summary
+application/simplesymbolcontainer
+application/slate
+application/smil
+application/smil+xml                           smi smil
+application/soap+fastinfoset
+application/soap+xml
+application/spirits-event+xml
+application/srgs                               gram
+application/srgs+xml                           grxml
+application/ssml+xml                           ssml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vividence.scriptfile
+application/vnd.3gpp.bsf+xml
+application/vnd.3gpp.pic-bw-large              plb
+application/vnd.3gpp.pic-bw-small              psb
+application/vnd.3gpp.pic-bw-var                        pvb
+application/vnd.3gpp.sms
+application/vnd.3gpp2.bcmcsinfo+xml
+application/vnd.3gpp2.sms
+application/vnd.3m.post-it-notes               pwn
+application/vnd.accpac.simply.aso              aso
+application/vnd.accpac.simply.imp              imp
+application/vnd.acucobol                       acu
+application/vnd.acucorp                                atc acutc
+application/vnd.adobe.xdp+xml                  xdp
+application/vnd.adobe.xfdf                     xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami                      ami
+application/vnd.anser-web-certificate-issue-initiation cii
+application/vnd.anser-web-funds-transfer-initiation    fti
+application/vnd.antix.game-component           atx
+application/vnd.apple.installer+xml            mpkg
+application/vnd.audiograph                     aep
+application/vnd.autopackage
+application/vnd.avistar+xml
+application/vnd.blueice.multipass              mpm
+application/vnd.bmi                            bmi
+application/vnd.businessobjects                        rep
+application/vnd.cab-jscript
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cendio.thinlinc.clientconf
+application/vnd.chemdraw+xml                   cdxml
+application/vnd.chipnuts.karaoke-mmd           mmd
+application/vnd.cinderella                     cdy
+application/vnd.cirpack.isdn-ext
+application/vnd.claymore                       cla
+application/vnd.clonk.c4group                  c4g c4d c4f c4p c4u
+application/vnd.commerce-battelle
+application/vnd.commonspace                    csp cst
+application/vnd.contact.cmsg                   cdbcmsg
+application/vnd.cosmocaller                    cmc
+application/vnd.crick.clicker                  clkx
+application/vnd.crick.clicker.keyboard         clkk
+application/vnd.crick.clicker.palette          clkp
+application/vnd.crick.clicker.template         clkt
+application/vnd.crick.clicker.wordbank         clkw
+application/vnd.criticaltools.wbs+xml          wbs
+application/vnd.ctc-posml                      pml
+application/vnd.cups-pdf
+application/vnd.cups-postscript
+application/vnd.cups-ppd                       ppd
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl                           curl
+application/vnd.cybank
+application/vnd.data-vision.rdz                        rdz
+application/vnd.denovo.fcselayout-link         fe_launch
+application/vnd.dna                            dna
+application/vnd.dolby.mlp                      mlp
+application/vnd.dpgraph                                dpg
+application/vnd.dreamfactory                   dfac
+application/vnd.dvb.esgcontainer
+application/vnd.dvb.ipdcesgaccess
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart                   mag
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven                                nml
+application/vnd.epson.esf                      esf
+application/vnd.epson.msf                      msf
+application/vnd.epson.quickanime               qam
+application/vnd.epson.salt                     slt
+application/vnd.epson.ssf                      ssf
+application/vnd.ericsson.quickcall
+application/vnd.eszigno3+xml                   es3 et3
+application/vnd.eudora.data
+application/vnd.ezpix-album                    ez2
+application/vnd.ezpix-package                  ez3
+application/vnd.fdf                            fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit                     gph
+application/vnd.fluxtime.clip                  ftc
+application/vnd.framemaker                     fm frame maker
+application/vnd.frogans.fnc                    fnc
+application/vnd.frogans.ltf                    ltf
+application/vnd.fsc.weblaunch                  fsc
+application/vnd.fujitsu.oasys                  oas
+application/vnd.fujitsu.oasys2                 oa2
+application/vnd.fujitsu.oasys3                 oa3
+application/vnd.fujitsu.oasysgp                        fg5
+application/vnd.fujitsu.oasysprs               bh2
+application/vnd.fujixerox.art-ex
+application/vnd.fujixerox.art4
+application/vnd.fujixerox.hbpl
+application/vnd.fujixerox.ddd                  ddd
+application/vnd.fujixerox.docuworks            xdw
+application/vnd.fujixerox.docuworks.binder     xbd
+application/vnd.fut-misnet
+application/vnd.fuzzysheet                     fzs
+application/vnd.genomatix.tuxedo               txd
+application/vnd.google-earth.kml+xml           kml
+application/vnd.google-earth.kmz               kmz
+application/vnd.grafeq                         gqf gqs
+application/vnd.gridmp
+application/vnd.groove-account                 gac
+application/vnd.groove-help                    ghf
+application/vnd.groove-identity-message                gim
+application/vnd.groove-injector                        grv
+application/vnd.groove-tool-message            gtm
+application/vnd.groove-tool-template           tpl
+application/vnd.groove-vcard                   vcg
+application/vnd.handheld-entertainment+xml     zmm
+application/vnd.hbci                           hbci
+application/vnd.hcl-bireports
+application/vnd.hhe.lesson-player              les
+application/vnd.hp-hpgl                                hpgl
+application/vnd.hp-hpid                                hpid
+application/vnd.hp-hps                         hps
+application/vnd.hp-jlyt                                jlt
+application/vnd.hp-pcl                         pcl
+application/vnd.hp-pclxl                       pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword               x3d
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay                    mpy
+application/vnd.ibm.modcap                     afp listafp list3820
+application/vnd.ibm.rights-management          irm
+application/vnd.ibm.secure-container           sc
+application/vnd.igloader                       igl
+application/vnd.immervision-ivp                        ivp
+application/vnd.immervision-ivu                        ivu
+application/vnd.informedcontrol.rms+xml
+application/vnd.intercon.formnet               xpw xpx
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo                       qbo
+application/vnd.intu.qfx                       qfx
+application/vnd.ipunplugged.rcprofile          rcprofile
+application/vnd.irepository.package+xml                irp
+application/vnd.is-xpr                         xpr
+application/vnd.jam                            jam
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jcp.javame.midlet-rms          rms
+application/vnd.jisp                           jisp
+application/vnd.kahootz                                ktz ktr
+application/vnd.kde.karbon                     karbon
+application/vnd.kde.kchart                     chrt
+application/vnd.kde.kformula                   kfo
+application/vnd.kde.kivio                      flw
+application/vnd.kde.kontour                    kon
+application/vnd.kde.kpresenter                 kpr kpt
+application/vnd.kde.kspread                    ksp
+application/vnd.kde.kword                      kwd kwt
+application/vnd.kenameaapp                     htke
+application/vnd.kidspiration                   kia
+application/vnd.kinar                          kne knp
+application/vnd.koan                           skp skd skt skm
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop     lbd
+application/vnd.llamagraphics.life-balance.exchange+xml        lbe
+application/vnd.lotus-1-2-3                    123
+application/vnd.lotus-approach                 apr
+application/vnd.lotus-freelance                        pre
+application/vnd.lotus-notes                    nsf
+application/vnd.lotus-organizer                        org
+application/vnd.lotus-screencam                        scm
+application/vnd.lotus-wordpro                  lwp
+application/vnd.macports.portpkg               portpkg
+application/vnd.marlin.drm.actiontoken+xml
+application/vnd.marlin.drm.conftoken+xml
+application/vnd.marlin.drm.mdcf
+application/vnd.mcd                            mcd
+application/vnd.medcalcdata                    mc1
+application/vnd.mediastation.cdkey             cdkey
+application/vnd.meridian-slingshot
+application/vnd.mfer                           mwf
+application/vnd.mfmp                           mfm
+application/vnd.micrografx.flo                 flo
+application/vnd.micrografx.igx                 igx
+application/vnd.mif                            mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf                     daf
+application/vnd.mobius.dis                     dis
+application/vnd.mobius.mbk                     mbk
+application/vnd.mobius.mqy                     mqy
+application/vnd.mobius.msl                     msl
+application/vnd.mobius.plc                     plc
+application/vnd.mobius.txf                     txf
+application/vnd.mophun.application             mpn
+application/vnd.mophun.certificate             mpc
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml        xul
+application/vnd.ms-artgalry                    cil
+application/vnd.ms-asf                         asf
+application/vnd.ms-cab-compressed              cab
+application/vnd.ms-excel                       xls xlm xla xlc xlt xlw
+application/vnd.ms-fontobject                  eot
+application/vnd.ms-htmlhelp                    chm
+application/vnd.ms-ims                         ims
+application/vnd.ms-lrm                         lrm
+application/vnd.ms-playready.initiator+xml
+application/vnd.ms-powerpoint                  ppt pps pot
+application/vnd.ms-project                     mpp mpt
+application/vnd.ms-tnef
+application/vnd.ms-wmdrm.lic-chlg-req
+application/vnd.ms-wmdrm.lic-resp
+application/vnd.ms-wmdrm.meter-chlg-req
+application/vnd.ms-wmdrm.meter-resp
+application/vnd.ms-works                       wps wks wcm wdb
+application/vnd.ms-wpl                         wpl
+application/vnd.ms-xpsdocument                 xps
+application/vnd.mseq                           mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician                       mus
+application/vnd.ncd.control
+application/vnd.nervana
+application/vnd.netfpx
+application/vnd.neurolanguage.nlu              nlu
+application/vnd.noblenet-directory             nnd
+application/vnd.noblenet-sealer                        nns
+application/vnd.noblenet-web                   nnw
+application/vnd.nokia.catalogs
+application/vnd.nokia.conml+wbxml
+application/vnd.nokia.conml+xml
+application/vnd.nokia.isds-radio-presets
+application/vnd.nokia.iptv.config+xml
+application/vnd.nokia.landmark+wbxml
+application/vnd.nokia.landmark+xml
+application/vnd.nokia.landmarkcollection+xml
+application/vnd.nokia.n-gage.ac+xml
+application/vnd.nokia.n-gage.data              ngdat
+application/vnd.nokia.n-gage.symbian.install   n-gage
+application/vnd.nokia.ncd
+application/vnd.nokia.pcd+wbxml
+application/vnd.nokia.pcd+xml
+application/vnd.nokia.radio-preset             rpst
+application/vnd.nokia.radio-presets            rpss
+application/vnd.novadigm.edm                   edm
+application/vnd.novadigm.edx                   edx
+application/vnd.novadigm.ext                   ext
+application/vnd.oasis.opendocument.chart               odc
+application/vnd.oasis.opendocument.chart-template      otc
+application/vnd.oasis.opendocument.formula             odf
+application/vnd.oasis.opendocument.formula-template    otf
+application/vnd.oasis.opendocument.graphics            odg
+application/vnd.oasis.opendocument.graphics-template   otg
+application/vnd.oasis.opendocument.image               odi
+application/vnd.oasis.opendocument.image-template      oti
+application/vnd.oasis.opendocument.presentation                odp
+application/vnd.oasis.opendocument.presentation-template otp
+application/vnd.oasis.opendocument.spreadsheet         ods
+application/vnd.oasis.opendocument.spreadsheet-template        ots
+application/vnd.oasis.opendocument.text                        odt
+application/vnd.oasis.opendocument.text-master         otm
+application/vnd.oasis.opendocument.text-template       ott
+application/vnd.oasis.opendocument.text-web            oth
+application/vnd.obn
+application/vnd.olpc-sugar                     xo
+application/vnd.oma-scws-config
+application/vnd.oma-scws-http-request
+application/vnd.oma-scws-http-response
+application/vnd.oma.bcast.associated-procedure-parameter+xml
+application/vnd.oma.bcast.drm-trigger+xml
+application/vnd.oma.bcast.imd+xml
+application/vnd.oma.bcast.notification+xml
+application/vnd.oma.bcast.sgboot
+application/vnd.oma.bcast.sgdd+xml
+application/vnd.oma.bcast.sgdu
+application/vnd.oma.bcast.simple-symbol-container
+application/vnd.oma.bcast.smartcard-trigger+xml
+application/vnd.oma.bcast.sprov+xml
+application/vnd.oma.dd2+xml                    dd2
+application/vnd.oma.drm.risd+xml
+application/vnd.oma.group-usage-list+xml
+application/vnd.oma.poc.groups+xml
+application/vnd.oma.xcap-directory+xml
+application/vnd.omads-email+xml
+application/vnd.omads-file+xml
+application/vnd.omads-folder+xml
+application/vnd.omaloc-supl-init
+application/vnd.openofficeorg.extension                oxt
+application/vnd.osa.netdeploy
+application/vnd.osgi.dp                                dp
+application/vnd.otps.ct-kip+xml
+application/vnd.palm                           prc pdb pqa oprc
+application/vnd.paos.xml
+application/vnd.pg.format                      str
+application/vnd.pg.osasli                      ei6
+application/vnd.piaccess.application-licence
+application/vnd.picsel                         efif
+application/vnd.poc.group-advertisement+xml
+application/vnd.pocketlearn                    plf
+application/vnd.powerbuilder6                  pbd
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.preminet
+application/vnd.previewsystems.box             box
+application/vnd.proteus.magazine               mgz
+application/vnd.publishare-delta-tree          qps
+application/vnd.pvi.ptid1                      ptid
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.qualcomm.brew-app-res
+application/vnd.quark.quarkxpress              qxd qxt qwd qwt qxl qxb
+application/vnd.rapid
+application/vnd.recordare.musicxml             mxl
+application/vnd.recordare.musicxml+xml
+application/vnd.renlearn.rlprint
+application/vnd.rn-realmedia                   rm
+application/vnd.ruckus.download
+application/vnd.s3sms
+application/vnd.scribus
+application/vnd.sealed.3df
+application/vnd.sealed.csf
+application/vnd.sealed.doc
+application/vnd.sealed.eml
+application/vnd.sealed.mht
+application/vnd.sealed.net
+application/vnd.sealed.ppt
+application/vnd.sealed.tiff
+application/vnd.sealed.xls
+application/vnd.sealedmedia.softseal.html
+application/vnd.sealedmedia.softseal.pdf
+application/vnd.seemail                                see
+application/vnd.sema                           sema
+application/vnd.semd                           semd
+application/vnd.semf                           semf
+application/vnd.shana.informed.formdata                ifm
+application/vnd.shana.informed.formtemplate    itp
+application/vnd.shana.informed.interchange     iif
+application/vnd.shana.informed.package         ipk
+application/vnd.simtech-mindmapper             twd twds
+application/vnd.smaf                           mmf
+application/vnd.solent.sdkm+xml                        sdkm sdkd
+application/vnd.spotfire.dxp                   dxp
+application/vnd.spotfire.sfs                   sfs
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.sun.wadl+xml
+application/vnd.sus-calendar                   sus susp
+application/vnd.svd                            svd
+application/vnd.swiftview-ics
+application/vnd.syncml+xml                     xsm
+application/vnd.syncml.dm+wbxml                        bdm
+application/vnd.syncml.dm+xml                  xdm
+application/vnd.syncml.ds.notification
+application/vnd.tao.intent-module-archive      tao
+application/vnd.tmobile-livetv                 tmo
+application/vnd.trid.tpt                       tpt
+application/vnd.triscape.mxs                   mxs
+application/vnd.trueapp                                tra
+application/vnd.truedoc
+application/vnd.ufdl                           ufd ufdl
+application/vnd.uiq.theme                      utz
+application/vnd.umajin                         umj
+application/vnd.unity                          unityweb
+application/vnd.uoml+xml                       uoml
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx                            vcx
+application/vnd.vd-study
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio                          vsd vst vss vsw
+application/vnd.visionary                      vis
+application/vnd.vividence.scriptfile
+application/vnd.vsf                            vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml                      wbxml
+application/vnd.wap.wmlc                       wmlc
+application/vnd.wap.wmlscriptc                 wmlsc
+application/vnd.webturbo                       wtb
+application/vnd.wfa.wsc
+application/vnd.wordperfect                    wpd
+application/vnd.wqd                            wqd
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf                         stf
+application/vnd.wv.csp+wbxml
+application/vnd.wv.csp+xml
+application/vnd.wv.ssp+xml
+application/vnd.xara                           xar
+application/vnd.xfdl                           xfdl
+application/vnd.xmpie.cpkg
+application/vnd.xmpie.dpkg
+application/vnd.xmpie.plan
+application/vnd.xmpie.ppkg
+application/vnd.xmpie.xlim
+application/vnd.yamaha.hv-dic                  hvd
+application/vnd.yamaha.hv-script               hvs
+application/vnd.yamaha.hv-voice                        hvp
+application/vnd.yamaha.smaf-audio              saf
+application/vnd.yamaha.smaf-phrase             spf
+application/vnd.yellowriver-custom-menu                cmp
+application/vnd.zzazz.deck+xml                 zaz
+application/voicexml+xml                       vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/winhlp                             hlp
+application/wita
+application/wordperfect5.1
+application/wsdl+xml                           wsdl
+application/wspolicy+xml                       wspolicy
+application/x-ace-compressed                   ace
+application/x-bcpio                            bcpio
+application/x-bittorrent                       torrent
+application/x-bzip                             bz
+application/x-bzip2                            bz2 boz
+application/x-cdlink                           vcd
+application/x-chat                             chat
+application/x-chess-pgn                                pgn
+application/x-compress
+application/x-cpio                             cpio
+application/x-csh                              csh
+application/x-director                         dcr dir dxr fgd
+application/x-dvi                              dvi
+application/x-futuresplash                     spl
+application/x-gtar                             gtar
+application/x-gzip
+application/x-hdf                              hdf
+application/x-java-jnlp-file   jnlp
+application/x-latex                            latex
+application/x-ms-wmd                           wmd
+application/x-ms-wmz                           wmz
+application/x-msaccess                         mdb
+application/x-msbinder                         obd
+application/x-mscardfile                       crd
+application/x-msclip                           clp
+application/x-msdownload                       exe dll com bat msi
+application/x-msmediaview                      mvb m13 m14
+application/x-msmetafile                       wmf
+application/x-msmoney                          mny
+application/x-mspublisher                      pub
+application/x-msschedule                       scd
+application/x-msterminal                       trm
+application/x-mswrite                          wri
+application/x-netcdf                           nc cdf
+application/x-pkcs12                           p12 pfx
+application/x-pkcs7-certificates               p7b spc
+application/x-pkcs7-certreqresp                        p7r
+application/x-rar-compressed                   rar
+application/x-sh                               sh
+application/x-shar                             shar
+application/x-shockwave-flash                  swf
+application/x-stuffit                          sit
+application/x-stuffitx                         sitx
+application/x-sv4cpio                          sv4cpio
+application/x-sv4crc                           sv4crc
+application/x-tar                              tar
+application/x-tcl                              tcl
+application/x-tex                              tex
+application/x-texinfo                          texinfo texi
+application/x-ustar                            ustar
+application/x-wais-source                      src
+application/x-x509-ca-cert                     der crt
+application/x400-bp
+application/xcap-att+xml
+application/xcap-caps+xml
+application/xcap-el+xml
+application/xcap-error+xml
+application/xcap-ns+xml
+application/xenc+xml                           xenc
+application/xhtml+xml                          xhtml xht
+application/xml                                        xml xsl
+application/xml-dtd                            dtd
+application/xml-external-parsed-entity
+application/xmpp+xml
+application/xop+xml                            xop
+application/xslt+xml                           xslt
+application/xspf+xml                           xspf
+application/xv+xml                             mxml xhvml xvml xvm
+application/zip                                        zip
+audio/32kadpcm
+audio/3gpp
+audio/3gpp2
+audio/ac3
+audio/amr
+audio/amr-wb
+audio/amr-wb+
+audio/asc
+audio/basic                                    au snd
+audio/bv16
+audio/bv32
+audio/clearmode
+audio/cn
+audio/dat12
+audio/dls
+audio/dsr-es201108
+audio/dsr-es202050
+audio/dsr-es202211
+audio/dsr-es202212
+audio/dvi4
+audio/eac3
+audio/evrc
+audio/evrc-qcp
+audio/evrc0
+audio/evrc1
+audio/evrcb
+audio/evrcb0
+audio/evrcb1
+audio/g722
+audio/g7221
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g7291
+audio/g729d
+audio/g729e
+audio/gsm
+audio/gsm-efr
+audio/ilbc
+audio/l16
+audio/l20
+audio/l24
+audio/l8
+audio/lpc
+audio/midi                                     mid midi kar rmi
+audio/mobile-xmf
+audio/mp4                                      mp4a
+audio/mp4a-latm                        m4a m4p
+audio/mpa
+audio/mpa-robust
+audio/mpeg                                     mpga mp2 mp2a mp3 m2a m3a
+audio/mpeg4-generic
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/rtp-enc-aescm128
+audio/rtp-midi
+audio/rtx
+audio/smv
+audio/smv0
+audio/smv-qcp
+audio/sp-midi
+audio/t140c
+audio/t38
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vmr-wb
+audio/vnd.3gpp.iufp
+audio/vnd.4sb
+audio/vnd.audiokoz
+audio/vnd.celp
+audio/vnd.cisco.nse
+audio/vnd.cmles.radio-events
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds                                eol
+audio/vnd.dlna.adts
+audio/vnd.dolby.mlp
+audio/vnd.everad.plj
+audio/vnd.hns.audio
+audio/vnd.lucent.voice                         lvp
+audio/vnd.nokia.mobile-xmf
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800                      ecelp4800
+audio/vnd.nuera.ecelp7470                      ecelp7470
+audio/vnd.nuera.ecelp9600                      ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.sealedmedia.softseal.mpeg
+audio/vnd.vmx.cvsd
+audio/wav                                      wav
+audio/x-aiff                                   aif aiff aifc
+audio/x-mpegurl                                        m3u
+audio/x-ms-wax                                 wax
+audio/x-ms-wma                                 wma
+audio/x-pn-realaudio                           ram ra
+audio/x-pn-realaudio-plugin                    rmp
+audio/x-wav                                    wav
+chemical/x-cdx                                 cdx
+chemical/x-cif                                 cif
+chemical/x-cmdf                                        cmdf
+chemical/x-cml                                 cml
+chemical/x-csml                                        csml
+chemical/x-pdb                                 pdb
+chemical/x-xyz                                 xyz
+image/bmp                                      bmp
+image/cgm                                      cgm
+image/fits
+image/g3fax                                    g3
+image/gif                                      gif
+image/ief                                      ief
+image/jp2                      jp2
+image/jpeg                                     jpeg jpg jpe
+image/jpm
+image/jpx
+image/naplps
+image/pict                     pict pic pct
+image/png                                      png
+image/prs.btif                                 btif
+image/prs.pti
+image/svg+xml                                  svg svgz
+image/t38
+image/tiff                                     tiff tif
+image/tiff-fx
+image/vnd.adobe.photoshop                      psd
+image/vnd.cns.inf2
+image/vnd.djvu                                 djvu djv
+image/vnd.dwg                                  dwg
+image/vnd.dxf                                  dxf
+image/vnd.fastbidsheet                         fbs
+image/vnd.fpx                                  fpx
+image/vnd.fst                                  fst
+image/vnd.fujixerox.edmics-mmr                 mmr
+image/vnd.fujixerox.edmics-rlc                 rlc
+image/vnd.globalgraphics.pgb
+image/vnd.microsoft.icon                       ico
+image/vnd.mix
+image/vnd.ms-modi                              mdi
+image/vnd.net-fpx                              npx
+image/vnd.sealed.png
+image/vnd.sealedmedia.softseal.gif
+image/vnd.sealedmedia.softseal.jpg
+image/vnd.svf
+image/vnd.wap.wbmp                             wbmp
+image/vnd.xiff                                 xif
+image/x-cmu-raster                             ras
+image/x-cmx                                    cmx
+image/x-icon
+image/x-macpaint               pntg pnt mac
+image/x-pcx                                    pcx
+image/x-pict                                   pic pct
+image/x-portable-anymap                                pnm
+image/x-portable-bitmap                                pbm
+image/x-portable-graymap                       pgm
+image/x-portable-pixmap                                ppm
+image/x-quicktime              qtif qti
+image/x-rgb                                    rgb
+image/x-xbitmap                                        xbm
+image/x-xpixmap                                        xpm
+image/x-xwindowdump                            xwd
+message/cpim
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822                                 eml mime
+message/s-http
+message/sip
+message/sipfrag
+message/tracking-status
+model/iges                                     igs iges
+model/mesh                                     msh mesh silo
+model/vnd.dwf                                  dwf
+model/vnd.flatland.3dml
+model/vnd.gdl                                  gdl
+model/vnd.gs.gdl
+model/vnd.gtw                                  gtw
+model/vnd.moml+xml
+model/vnd.mts                                  mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu                                  vtu
+model/vrml                                     wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/calendar                                  ics ifb
+text/css                                       css
+text/csv                                       csv
+text/directory
+text/dns
+text/enriched
+text/html                                      html htm
+text/parityfec
+text/plain                                     txt text conf def list log in
+text/prs.fallenstein.rst
+text/prs.lines.tag                             dsc
+text/red
+text/rfc822-headers
+text/richtext                                  rtx
+text/rtf
+text/rtp-enc-aescm128
+text/rtx
+text/sgml                                      sgml sgm
+text/t140
+text/tab-separated-values                      tsv
+text/troff                                     t tr roff man me ms
+text/uri-list                                  uri uris urls
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.esmertec.theme-descriptor
+text/vnd.fly                                   fly
+text/vnd.fmi.flexstor                          flx
+text/vnd.in3d.3dml                             3dml
+text/vnd.in3d.spot                             spot
+text/vnd.iptc.newsml
+text/vnd.iptc.nitf
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor               jad
+text/vnd.trolltech.linguist
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml                               wml
+text/vnd.wap.wmlscript                         wmls
+text/x-asm                                     s asm
+text/x-c                                       c cc cxx cpp h hh dic
+text/x-fortran                                 f for f77 f90
+text/x-pascal                                  p pas
+text/x-java-source                             java
+text/x-setext                                  etx
+text/x-uuencode                                        uu
+text/x-vcalendar                               vcs
+text/x-vcard                                   vcf
+text/xml
+text/xml-external-parsed-entity
+video/3gpp                                     3gp
+video/3gpp-tt
+video/3gpp2                                    3g2
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261                                     h261
+video/h263                                     h263
+video/h263-1998
+video/h263-2000
+video/h264                                     h264
+video/jpeg                                     jpgv
+video/jpm                                      jpm jpgm
+video/mj2                                      mj2 mjp2
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4                                      mp4 mp4v mpg4 m4v
+video/mp4v-es
+video/mpeg                                     mpeg mpg mpe m1v m2v
+video/mpeg4-generic
+video/mpv
+video/nv
+video/parityfec
+video/pointer
+video/quicktime                                        qt mov
+video/raw
+video/rtp-enc-aescm128
+video/rtx
+video/smpte292m
+video/vc1
+video/vnd.dlna.mpeg-tts
+video/vnd.fvt                                  fvt
+video/vnd.hns.video
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl                              mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.nokia.videovoip
+video/vnd.objectvideo
+video/vnd.sealed.mpeg1
+video/vnd.sealed.mpeg4
+video/vnd.sealed.swf
+video/vnd.sealedmedia.softseal.mov
+video/vnd.vivo                                 viv
+video/x-dv                     dv dif
+video/x-fli                                    fli
+video/x-ms-asf                                 asf asx
+video/x-ms-wm                                  wm
+video/x-ms-wmv                                 wmv
+video/x-ms-wmx                                 wmx
+video/x-ms-wvx                                 wvx
+video/x-msvideo                                        avi
+video/x-sgi-movie                              movie
+x-conference/x-cooltalk                                ice
index 1a15283048fc585042254e8416975809b9778770..de37969220ca6a48a96d6d4882d2842bd4b8c9fc 100644 (file)
@@ -19,7 +19,9 @@ TUPLE: texture-gadget bytes format dim tex ;
         swap >>format
         swap >>bytes ;
 
-:: render ( gadget -- )
+GENERIC: render* ( texture-gadget -- )
+
+M:: texture-gadget render* ( gadget -- )
     GL_ENABLE_BIT [
         GL_TEXTURE_2D glEnable
         GL_TEXTURE_2D gadget tex>> glBindTexture
@@ -63,8 +65,8 @@ M: texture-gadget draw-gadget* ( gadget -- )
     ] with-translation ;
 
 M: texture-gadget graft* ( gadget -- )
-    gen-texture >>tex [ render ]
-    [ f >>bytes f >>format drop ] bi ;
+    gen-texture >>tex [ render* ]
+    [ f >>bytes drop ] bi ;
 
 M: texture-gadget ungraft* ( gadget -- )
     tex>> delete-texture ;
index 907233a335bf59ac96567c91ba29d4efce8a40ec..d1b536d9bc98aa1125688fde2a8c2f686d99d359 100644 (file)
@@ -130,5 +130,8 @@ MEMO: dummy-cairo ( -- cr )
 : layout-text ( str -- )
     layout swap -1 pango_layout_set_text ;
 
+: show-layout ( -- )
+    cr layout pango_cairo_show_layout ;
+
 : families ( -- families )
     pango_cairo_font_map_get_default list-families ;
index 9e8a99515e42167ef510844ab551a33bfebb78fa..4c46b4e5015fb36a472efa4cd67d80cc90b4bc70 100644 (file)
@@ -1,30 +1,64 @@
 ! Copyright (C) 2008 Matthew Willis.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: pango.cairo cairo cairo.ffi cairo.gadgets
+USING: pango.cairo cairo cairo.ffi
+cairo.gadgets namespaces arrays
+fry accessors ui.gadgets assocs
+sequences shuffle opengl opengl.gadgets
 alien.c-types kernel math ;
 IN: pango.cairo.gadgets
 
-: (pango-gadget) ( setup show -- gadget )
-    [ drop layout-size ]
-    [ compose [ with-pango ] curry <cairo-gadget> ] 2bi ;
-
-: <pango-gadget> ( quot -- gadget )
-    [ cr layout pango_cairo_show_layout ] (pango-gadget) ;
-
-USING: prettyprint sequences ui.gadgets.panes
-threads io.backend io.encodings.utf8 io.files ;
-: hello-pango ( -- )
-    50 [ 6 + ] map [
-        "Sans " swap unparse append
-        [ 
-            cr 0 1 0.2 0.6 cairo_set_source_rgba
-            layout-font "今日は、 Pango!" layout-text
-        ] curry
-        <pango-gadget> gadget. yield
-    ] each
-    [ 
-        "resource:extra/pango/cairo/gadgets/gadgets.factor"
-        normalize-path utf8 file-contents layout-text
-    ] <pango-gadget> gadget. ;
-
-MAIN: hello-pango
+SYMBOL: textures
+SYMBOL: dims
+SYMBOL: refcounts
+
+: init-cache ( symbol -- )
+    dup get [ drop ] [ H{ } clone swap set-global ] if ;
+
+textures init-cache
+dims init-cache
+refcounts init-cache
+
+TUPLE: pango-gadget < cairo-gadget text font ;
+
+: cache-key ( gadget -- key )
+    [ font>> ] [ text>> ] bi 2array ;
+
+: refcount-change ( gadget quot -- )
+    >r cache-key refcounts get
+    [ [ 0 ] unless* ] r> compose change-at ;
+
+: <pango-gadget> ( font text -- gadget )
+    pango-gadget construct-gadget
+        swap >>text
+        swap >>font ;
+
+: setup-layout ( {font,text} -- quot )
+    first2 '[ , layout-font , layout-text ] ;
+
+M: pango-gadget quot>> ( gadget -- quot )
+    cache-key setup-layout [ show-layout ] compose
+    [ with-pango ] curry ;
+
+M: pango-gadget dim>> ( gadget -- dim )
+    cache-key dims get [ setup-layout layout-size ] cache ;
+
+M: pango-gadget graft* ( gadget -- ) [ 1+ ] refcount-change ;
+
+: release-texture ( gadget -- )
+    cache-key textures get delete-at* [ delete-texture ] [ drop ] if ;
+
+M: pango-gadget ungraft* ( gadget -- )
+    dup [ 1- ] refcount-change
+    dup cache-key refcounts get at
+    zero? [ release-texture ] [ drop ] if ;
+
+M: pango-gadget render* ( gadget -- ) 
+    [ gen-texture ] [ cache-key textures get set-at ] bi
+    call-next-method ;
+
+M: pango-gadget tex>> ( gadget -- texture )
+    dup cache-key textures get at 
+    [ nip ] [ dup render* tex>> ] if* ;
+
+USE: ui.gadgets.panes
+: hello "Sans 50" "hello" <pango-gadget> gadget. ;
diff --git a/extra/pango/cairo/samples/samples.factor b/extra/pango/cairo/samples/samples.factor
new file mode 100644 (file)
index 0000000..644d731
--- /dev/null
@@ -0,0 +1,23 @@
+! Copyright (C) 2008 Matthew Willis.
+! See http://factorcode.org/license.txt for BSD license.
+USING: prettyprint sequences ui.gadgets.panes
+pango.cairo.gadgets math kernel cairo cairo.ffi
+pango.cairo tools.time namespaces assocs
+threads io.backend io.encodings.utf8 io.files ;
+
+IN: pango.cairo.samples
+
+: hello-pango ( -- )
+    "monospace 10" "resource:extra/pango/cairo/gadgets/gadgets.factor"
+    normalize-path utf8 file-contents
+    <pango-gadget> gadget. ;
+
+: time-pango ( -- )
+    [ hello-pango ] time ;
+
+! clear the caches, for testing.
+: clear-pango ( -- )
+    dims get clear-assoc
+    textures get clear-assoc ;
+
+MAIN: time-pango
index 364c24b91fb9f85ee0aae10704e2830d76a485ae..5183af51450da2486aa025ab3d89f9c9da8ab164 100644 (file)
@@ -4,7 +4,7 @@ USING: xml.utilities kernel assocs xml.generator math.order
     strings sequences xml.data xml.writer
     io.streams.string combinators xml xml.entities io.files io
     http.client namespaces xml.generator hashtables
-    calendar.format accessors continuations ;
+    calendar.format accessors continuations urls ;
 IN: rss
 
 : any-tag-named ( tag names -- tag-inside )
@@ -103,18 +103,15 @@ C: <entry> entry
 
 : entry, ( entry -- )
     "entry" [
-        dup entry-title "title" { { "type" "html" } } simple-tag*,
-        "link" over entry-link "href" associate contained*,
-        dup entry-pub-date timestamp>rfc3339 "published" simple-tag,
-        entry-description [ "content" { { "type" "html" } } simple-tag*, ] when*
+        dup title>> "title" { { "type" "html" } } simple-tag*,
+        "link" over link>> dup url? [ url>string ] when "href" associate contained*,
+        dup pub-date>> timestamp>rfc3339 "published" simple-tag,
+        description>> [ "content" { { "type" "html" } } simple-tag*, ] when*
     ] tag, ;
 
 : feed>xml ( feed -- xml )
     "feed" { { "xmlns" "http://www.w3.org/2005/Atom" } } [
-        dup feed-title "title" simple-tag,
-        "link" over feed-link "href" associate contained*,
-        feed-entries [ entry, ] each
+        dup title>> "title" simple-tag,
+        "link" over link>> dup url? [ url>string ] when "href" associate contained*,
+        entries>> [ entry, ] each
     ] make-xml* ;
-
-: write-feed ( feed -- )
-    feed>xml write-xml ;
index 8a4c6146deb7af57c79b3370632f3af0037de173..1f567a5f0d2c66f66221a44264b452bf49cc04e6 100644 (file)
@@ -1,6 +1,10 @@
 ! Copyright (C) 2008 Alex Chapman
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors assocs db db.sqlite db.postgresql http http.server http.server.actions http.server.static io io.files json.writer kernel math.parser namespaces semantic-db sequences strings tangle.path ;
+USING: accessors assocs db db.sqlite db.postgresql
+http http.server http.server.dispatchers http.server.responses
+http.server.static furnace.actions furnace.json
+io io.files json.writer kernel math.parser namespaces
+semantic-db sequences strings tangle.path ;
 IN: tangle
 
 GENERIC: render* ( content templater -- output )
@@ -20,7 +24,7 @@ C: <tangle> tangle
     [ [ db>> ] [ seq>> ] bi ] dip with-db ;
 
 : node-response ( id -- response )
-    load-node [ node-content <text-content> ] [ <404> ] if* ;
+    load-node [ node-content "text/plain" <content> ] [ <404> ] if* ;
 
 : display-node ( params -- response )
     [
@@ -36,7 +40,7 @@ C: <tangle> tangle
 : submit-node ( params -- response )
     [
         "node_content" swap at* [
-            create-node id>> number>string <text-content>
+            create-node id>> number>string "text/plain" <content>
         ] [
             drop <400>
         ] if
@@ -52,7 +56,7 @@ TUPLE: path-responder ;
 C: <path-responder> path-responder
 
 M: path-responder call-responder* ( path responder -- response )
-    drop path>file [ node-content <text-content> ] [ <404> ] if* ;
+    drop path>file [ node-content "text/plain" <content> ] [ <404> ] if* ;
 
 TUPLE: tangle-dispatcher < dispatcher tangle ;
 
index 23538229a446bda16144480e128d2a707d61fdbd..0e92042ddd619f78dcd0cbd44b7304f1cb2f817d 100644 (file)
@@ -1,7 +1,42 @@
-USING: help.syntax help.markup ;
+USING: help.syntax help.markup strings byte-arrays ;
 IN: unicode.collation
 
 ABOUT: "unicode.collation"
 
 ARTICLE: "unicode.collation" "Unicode collation algorithm"
-"The Unicode Collation Algorithm (UTS #10) forms a reasonable way to sort strings when accouting for all of the characters in Unicode." ;
+"The Unicode Collation Algorithm (UTS #10) forms a reasonable way to sort strings when accouting for all of the characters in Unicode. At the moment, only the default Unicode collation element table (DUCET) is used, but a more accurate collation would take locale into account. The following words are defined:"
+{ $subsection sort-strings }
+{ $subsection collation-key }
+{ $subsection string<=> }
+{ $subsection primary= }
+{ $subsection secondary= }
+{ $subsection tertiary= }
+{ $subsection quaternary= } ;
+
+HELP: sort-strings
+{ $values { "strings" "a sequence of strings" } { "sorted" "the strings in DUCET order" } }
+{ $description "This word takes a sequence of strings and sorts them according to the UCA, using code point order as a tie-breaker." } ;
+
+HELP: collation-key
+{ $values { "string" string } { "key" byte-array } }
+{ $description "This takes a string and gives a representation of the collation key, which can be compared with <=>" } ;
+
+HELP: string<=>
+{ $values { "str1" string } { "str2" string } { "<=>" "one of +lt+, +gt+ or +eq+" } }
+{ $description "This word takes two strings and compares them using the UCA with the DUCET, using code point order as a tie-breaker." } ;
+
+HELP: primary=
+{ $values { "str1" string } { "str2" string } { "?" "t or f" } }
+{ $description "This checks whether the first level of collation is identical. This is the least specific kind of equality test. In Latin script, it can be understood as ignoring case, punctuation and accent marks." } ;
+
+HELP: secondary=
+{ $values { "str1" string } { "str2" string } { "?" "t or f" } }
+{ $description "This checks whether the first two levels of collation are equal. For Latin script, this means accent marks are significant again, and it is otherwise similar to primary=." } ;
+
+HELP: tertiary=
+{ $values { "str1" string } { "str2" string } { "?" "t or f" } }
+{ $description "Along the same lines as secondary=, but case is significant." } ;
+
+HELP: quaternary=
+{ $values { "str1" string } { "str2" string } { "?" "t or f" } }
+{ $description "This is similar to tertiary= but it makes punctuation significant again, while still leaving out things like null bytes and Hebrew vowel marks, which mean absolutely nothing in collation." } ;
index b4a54bb11de61843e5835cbdb49db3fc6d07ae47..16ac50d5a960ea660104461ea5d44078bc0543b8 100755 (executable)
@@ -24,6 +24,9 @@ IN: unicode.collation.tests
 [ t t f f ] [ "hello" "HELLO" test-equality ] unit-test\r
 [ t t t f ] [ "hello" "h e l l o." test-equality ] unit-test\r
 [ t t t t ] [ "hello" "\0hello\0" test-equality ] unit-test\r
+[ { "good bye" "goodbye" "hello" "HELLO" } ]\r
+[ { "HELLO" "goodbye" "good bye" "hello" } sort-strings ]\r
+unit-test\r
 \r
 parse-test 2 <clumps>\r
 [ [ test-two ] assoc-each ] with-null-writer\r
index b12a10709ef35284f234e185f0ec2047f32d1df3..f71a58be85f2bdf65b5eb52a2788b7597598fcbc 100755 (executable)
@@ -6,6 +6,7 @@ unicode.syntax macros sequences.deep words unicode.breaks
 quotations ;\r
 IN: unicode.collation\r
 \r
+<PRIVATE\r
 VALUE: ducet\r
 \r
 TUPLE: weight primary secondary tertiary ignorable? ;\r
@@ -115,6 +116,7 @@ ducet insert-helpers
             [ [ variable-weight ] each ]\r
         } cleave\r
     ] { } make ;\r
+PRIVATE>\r
 \r
 : completely-ignorable? ( weight -- ? )\r
     [ primary>> ] [ secondary>> ] [ tertiary>> ] tri\r
@@ -131,11 +133,13 @@ ducet insert-helpers
     nfd string>graphemes graphemes>weights\r
     filter-ignorable weights>bytes ;\r
 \r
+<PRIVATE\r
 : insensitive= ( str1 str2 levels-removed -- ? )\r
     [\r
         swap collation-key swap\r
         [ [ 0 = not ] right-trim but-last ] times\r
     ] curry bi@ = ;\r
+PRIVATE>\r
 \r
 : primary= ( str1 str2 -- ? )\r
     3 insensitive= ;\r
@@ -149,17 +153,14 @@ ducet insert-helpers
 : quaternary= ( str1 str2 -- ? )\r
     0 insensitive= ;\r
 \r
-: compare-collation ( {str1,key} {str2,key} -- <=> )\r
-    2dup [ second ] bi@ <=> dup +eq+ =\r
-    [ drop <=> ] [ 2nip ] if ;\r
-\r
+<PRIVATE\r
 : w/collation-key ( str -- {str,key} )\r
-    dup collation-key 2array ;\r
+    [ collation-key ] keep 2array ;\r
+PRIVATE>\r
 \r
 : sort-strings ( strings -- sorted )\r
     [ w/collation-key ] map\r
-    [ compare-collation ] sort\r
-    keys ;\r
+    natural-sort values ;\r
 \r
 : string<=> ( str1 str2 -- <=> )\r
-    [ w/collation-key ] bi@ compare-collation ;\r
+    [ w/collation-key ] compare ;\r
index e28816fdb319a1da2dc4614467fd7f22f22c34e0..080352449b99231f1fe19c053ce6a069e96e9c20 100644 (file)
@@ -77,10 +77,36 @@ USING: urls tools.test tuple-syntax arrays kernel assocs ;
             }
             "a/relative/path"
         }
+        {
+            TUPLE{ url
+                path: "bar"
+                query: H{ { "a" "b" } }
+            }
+            "bar?a=b"
+        }
+        {
+            TUPLE{ url
+                protocol: "ftp"
+                host: "ftp.kernel.org"
+                username: "slava"
+                path: "/"
+            }
+            "ftp://slava@ftp.kernel.org/"
+        }
+        {
+            TUPLE{ url
+                protocol: "ftp"
+                host: "ftp.kernel.org"
+                username: "slava"
+                password: "secret"
+                path: "/"
+            }
+            "ftp://slava:secret@ftp.kernel.org/"
+        }
     } ;
 
 urls [
-    [ 1array ] [ [ string>url ] curry ] bi* unit-test
+    [ 1array ] [ [ >url ] curry ] bi* unit-test
 ] assoc-each
 
 urls [
@@ -192,3 +218,7 @@ urls [
 
     derive-url
 ] unit-test
+
+[ "a" ] [
+    <url> "a" "b" set-query-param "b" query-param
+] unit-test
index e20df6565640b1f649df10ee34b3d801bda1ca84..5c89205d5bfc8ed3a33a1c89f281447ea654a65c 100644 (file)
@@ -1,9 +1,10 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel unicode.categories combinators sequences splitting
-fry namespaces assocs arrays strings mirrors
-io.encodings.string io.encodings.utf8
-math math.parser accessors namespaces.lib ;
+fry namespaces assocs arrays strings io.sockets
+io.sockets.secure io.encodings.string io.encodings.utf8
+math math.parser accessors mirrors parser
+prettyprint.backend hashtables ;
 IN: urls
 
 : url-quotable? ( ch -- ? )
@@ -89,13 +90,15 @@ IN: urls
         ] assoc-each
     ] { } make "&" join ;
 
-TUPLE: url protocol host port path query anchor ;
+TUPLE: url protocol username password host port path query anchor ;
 
-: query-param ( request key -- value )
+: <url> ( -- url ) url new ;
+
+: query-param ( url key -- value )
     swap query>> at ;
 
-: set-query-param ( request value key -- request )
-    pick query>> set-at ;
+: set-query-param ( url value key -- url )
+    '[ , , _ ?set-at ] change-query ;
 
 : parse-host ( string -- host port )
     ":" split1 [ url-decode ] [
@@ -105,40 +108,56 @@ TUPLE: url protocol host port path query anchor ;
         ] when
     ] bi* ;
 
-: parse-host-part ( protocol rest -- string' )
-    [ "protocol" set ] [
+: parse-host-part ( url protocol rest -- url string' )
+    [ >>protocol ] [
         "//" ?head [ "Invalid URL" throw ] unless
+        "@" split1 [
+            [
+                ":" split1 [ >>username ] [ >>password ] bi*
+            ] dip
+        ] when*
         "/" split1 [
-            parse-host [ "host" set ] [ "port" set ] bi*
+            parse-host [ >>host ] [ >>port ] bi*
         ] [ "/" prepend ] bi*
     ] bi* ;
 
-: string>url ( string -- url )
-    [
-        ":" split1 [ parse-host-part ] when*
-        "#" split1 [
-            "?" split1 [ query>assoc "query" set ] when*
-            url-decode "path" set
-        ] [
-            url-decode "anchor" set
-        ] bi*
-    ] url make-object ;
+GENERIC: >url ( obj -- url )
 
-: unparse-host-part ( protocol -- )
+M: url >url ;
+
+M: string >url
+    <url> swap
+    ":" split1 [ parse-host-part ] when*
+    "#" split1 [
+        "?" split1
+        [ url-decode >>path ]
+        [ [ query>assoc >>query ] when* ] bi*
+    ]
+    [ url-decode >>anchor ] bi* ;
+
+: unparse-username-password ( url -- )
+    dup username>> dup [
+        % password>> [ ":" % % ] when* "@" %
+    ] [ 2drop ] if ;
+
+: unparse-host-part ( url protocol -- )
     %
     "://" %
-    "host" get url-encode %
-    "port" get [ ":" % # ] when*
-    "path" get "/" head? [ "Invalid URL" throw ] unless ;
+    {
+        [ unparse-username-password ]
+        [ host>> url-encode % ]
+        [ port>> [ ":" % # ] when* ]
+        [ path>> "/" head? [ "/" % ] unless ]
+    } cleave ;
 
 : url>string ( url -- string )
     [
-        <mirror> [
-            "protocol" get [ unparse-host-part ] when*
-            "path" get url-encode %
-            "query" get [ "?" % assoc>query % ] when*
-            "anchor" get [ "#" % url-encode % ] when*
-        ] bind
+        {
+            [ dup protocol>> dup [ unparse-host-part ] [ 2drop ] if ]
+            [ path>> url-encode % ]
+            [ query>> dup assoc-empty? [ drop ] [ "?" % assoc>query % ] if ]
+            [ anchor>> [ "#" % url-encode % ] when* ]
+        } cleave
     ] "" make ;
 
 : url-append-path ( path1 path2 -- path )
@@ -158,3 +177,26 @@ TUPLE: url protocol host port path query anchor ;
 
 : relative-url ( url -- url' )
     clone f >>protocol f >>host f >>port ;
+
+! Half-baked stuff follows
+: secure-protocol? ( protocol -- ? )
+    "https" = ;
+
+: url-addr ( url -- addr )
+    [ [ host>> ] [ port>> ] bi <inet> ] [ protocol>> ] bi
+    secure-protocol? [ <secure> ] when ;
+
+: protocol-port ( protocol -- port )
+    {
+        { "http" [ 80 ] }
+        { "https" [ 443 ] }
+        { "ftp" [ 21 ] }
+    } case ;
+
+: ensure-port ( url -- url' )
+    dup protocol>> '[ , protocol-port or ] change-port ;
+
+! Literal syntax
+: URL" lexer get skip-blank parse-string >url parsed ; parsing
+
+M: url pprint* dup url>string "URL\" " "\"" pprint-string ;
index 04194adb293a7cb81d38991b0f98c3dee7be12a6..da646fb76f2ea253f218fe3e0f4c542c1bd7e0c7 100644 (file)
@@ -1,6 +1,7 @@
-USING: math kernel accessors html.components
-http.server http.server.actions
-http.server.sessions html.templates.chloe fry ;
+USING: math kernel accessors http.server http.server.dispatchers
+furnace furnace.actions furnace.sessions
+html.components html.templates.chloe
+fry urls ;
 IN: webapps.counter
 
 SYMBOL: count
@@ -11,15 +12,15 @@ M: counter-app init-session* drop 0 count sset ;
 
 : <counter-action> ( quot -- action )
     <action>
-        swap '[ count , schange "" f <standard-redirect> ] >>submit ;
-
-: counter-template ( -- template )
-    "resource:extra/webapps/counter/counter.xml" <chloe> ;
+        swap '[
+            count , schange
+            URL" $counter-app" <redirect>
+        ] >>submit ;
 
 : <display-action> ( -- action )
     <page-action>
         [ count sget "counter" set-value ] >>init
-        counter-template >>template ;
+        { counter-app "counter" } >>template ;
 
 : <counter-app> ( -- responder )
     counter-app new-dispatcher
index 9ad4a054922c01bf14819f2e3ea487d1656168b2..853af6e84520bf2b9687545f8e47de0e53d28411 100644 (file)
@@ -4,13 +4,13 @@ USING: accessors kernel sequences assocs io.files io.sockets
 io.server
 namespaces db db.sqlite smtp
 http.server
-http.server.db
-http.server.flows
-http.server.sessions
-http.server.auth.login
-http.server.auth.providers.db
-http.server.boilerplate
-html.templates.chloe
+http.server.dispatchers
+furnace.db
+furnace.flows
+furnace.sessions
+furnace.auth.login
+furnace.auth.providers.db
+furnace.boilerplate
 webapps.pastebin
 webapps.planet
 webapps.todo
@@ -20,9 +20,6 @@ IN: webapps.factor-website
 
 : test-db "resource:test.db" sqlite-db ;
 
-: factor-template ( path -- template )
-    "resource:extra/webapps/factor-website/" swap ".xml" 3append <chloe> ;
-
 : init-factor-db ( -- )
     test-db [
         init-users-table
@@ -40,8 +37,10 @@ IN: webapps.factor-website
         init-revisions-table
     ] with-db ;
 
+TUPLE: factor-website < dispatcher ;
+
 : <factor-website> ( -- responder )
-    <dispatcher> 
+    factor-website new-dispatcher 
         <todo-list> "todo" add-responder
         <pastebin> "pastebin" add-responder
         <planet-factor> "planet" add-responder
@@ -53,7 +52,7 @@ IN: webapps.factor-website
         allow-password-recovery
         allow-edit-profile
     <boilerplate>
-        "page" factor-template >>template
+        { factor-website "page" } >>template
     <flows>
     <sessions>
     test-db <db-persistence> ;
index f7080643b448f7cf190f9264c2429f2610eb6283..32e1223c587376800290daf8a924d8001bba36b1 100644 (file)
@@ -15,6 +15,8 @@
                        <t:style t:include="resource:extra/webapps/factor-website/page.css" />
 
                        <t:write-style />
+
+                       <t:write-atom />
                </head>
 
                <body>
index 57c2fdb7c2b27418dab789eb7efc817a012e60e8..9f35d83fd8d4e18f583c87723f27d0062ab3b6ff 100644 (file)
@@ -2,7 +2,7 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:atom t:title="Paste - Atom" t:href="$pastebin/paste.atom" t:query="id" />
+       <t:atom t:title="This paste" t:href="$pastebin/paste.atom" t:query="id" />
 
        <t:title>Paste: <t:label t:name="summary" /></t:title>
 
                <tr><th class="field-label">Date: </th><td><t:label t:name="date" /></td></tr>
        </table>
 
-       <pre class="description"><t:code t:name="contents" t:mode="modes" /></pre>
+       <pre class="description"><t:code t:name="contents" t:mode="mode" /></pre>
 
        <t:button t:action="$pastebin/delete-paste" t:for="id" class="link-button link">Delete Paste</t:button>
-       |
-       <t:a t:href="$pastebin/annotate" t:query="id">Annotate</t:a>
 
-       <t:each-tuple t:values="annotations">
+       <t:bind-each t:name="annotations">
 
-               <h2>Annotation: <t:label t:name="summary" /></h2>
+               <a name="@id"><h2>Annotation: <t:label t:name="summary" /></h2></a>
 
                <table>
                        <tr><th class="field-label">Author: </th><td><t:label t:name="author" /></td></tr>
@@ -32,9 +30,9 @@
 
                <t:button t:action="$pastebin/delete-annotation" t:for="aid" class="link-button link">Delete Annotation</t:button>
 
-       </t:each-tuple>
+       </t:bind-each>
 
-       <t:bind-assoc t:name="new-annotation">
+       <t:bind t:name="new-annotation">
 
                <h2>New Annotation</h2>
 
@@ -55,6 +53,6 @@
                        <input type="SUBMIT" value="Done" />
                </t:form>
 
-       </t:bind-assoc>
+       </t:bind>
 
 </t:chloe>
index f785fceb6b99e61042be8ad14b1dc2db7a7c72d5..5ef44ad6ce2e57916aa46625c874632b66d0a230 100644 (file)
@@ -2,6 +2,8 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
+       <t:atom t:title="Pastebin" t:href="$pastebin/list.atom" />
+
        <t:style t:include="resource:extra/webapps/pastebin/pastebin.css" />
 
        <div class="navbar">
@@ -9,9 +11,9 @@
                  <t:a t:href="$pastebin/list">Pastes</t:a>
                | <t:a t:href="$pastebin/new-paste">New Paste</t:a>
 
-               <t:if t:code="http.server.sessions:uid">
+               <t:if t:code="furnace.sessions:uid">
 
-                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                       <t:if t:code="furnace.auth.login:allow-edit-profile?">
                                | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                        </t:if>
 
index 43cae74ec870876a30faea7e72ffdef573fe6198..69650b4d73f83d45962406eaf4ed85a7b6af429b 100644 (file)
@@ -2,15 +2,23 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: namespaces assocs sorting sequences kernel accessors
 hashtables sequences.lib db.types db.tuples db combinators
-calendar calendar.format math.parser rss xml.writer
-xmode.catalog validators html.components html.templates.chloe
+calendar calendar.format math.parser rss urls xml.writer
+xmode.catalog validators
+html.components
+html.templates.chloe
 http.server
-http.server.actions
-http.server.auth
-http.server.auth.login
-http.server.boilerplate ;
+http.server.dispatchers
+http.server.redirection
+furnace
+furnace.actions
+furnace.auth
+furnace.auth.login
+furnace.boilerplate
+furnace.rss ;
 IN: webapps.pastebin
 
+TUPLE: pastebin < dispatcher ;
+
 ! ! !
 ! DOMAIN MODEL
 ! ! !
@@ -58,28 +66,31 @@ annotation "ANNOTATIONS"
 : paste ( id -- paste )
     <paste> select-tuple fetch-annotations ;
 
-: <id-redirect> ( id next -- response )
-    swap "id" associate <standard-redirect> ;
-
 ! ! !
 ! LINKS, ETC
 ! ! !
 
 : pastebin-link ( -- url )
-    "$pastebin/list" f link>string ;
+    URL" $pastebin/list" ;
 
 GENERIC: entity-link ( entity -- url )
 
+: paste-link ( id -- url )
+    <url>
+        "$pastebin/paste" >>path
+        swap "id" set-query-param ;
+
 M: paste entity-link
-    id>> "id" associate "$pastebin/paste" swap link>string ;
+    id>> paste-link ;
 
-M: annotation entity-link
-    [ parent>> "parent" associate "$pastebin/paste" swap link>string ]
-    [ id>> number>string "#" prepend ] bi
-    append ;
+: annotation-link ( parent id -- url )
+    <url>
+        "$pastebin/paste" >>path
+        swap number>string >>anchor
+        swap "id" set-query-param ;
 
-: pastebin-template ( name -- template )
-    "resource:extra/webapps/pastebin/" swap ".xml" 3append <chloe> ;
+M: annotation entity-link
+    [ parent>> ] [ id>> ] bi annotation-link ;
 
 ! ! !
 ! PASTE LIST
@@ -88,7 +99,7 @@ M: annotation entity-link
 : <pastebin-action> ( -- action )
     <page-action>
         [ pastes "pastes" set-value ] >>init
-        "pastebin" pastebin-template >>template ;
+        { pastebin "pastebin" } >>template ;
 
 : pastebin-feed-entries ( seq -- entries )
     <reversed> 20 short head [
@@ -96,7 +107,7 @@ M: annotation entity-link
             swap
             [ summary>> >>title ]
             [ date>> >>pub-date ]
-            [ entity-link >>link ]
+            [ entity-link adjust-url relative-to-request >>link ]
             tri
     ] map ;
 
@@ -117,7 +128,7 @@ M: annotation entity-link
     <page-action>
         [
             validate-integer-id
-            "id" value paste from-tuple
+            "id" value paste from-object
 
             "id" value
             "new-annotation" [
@@ -127,7 +138,7 @@ M: annotation entity-link
             ] nest-values
         ] >>init
 
-        "paste" pastebin-template >>template ;
+        { pastebin "paste" } >>template ;
 
 : paste-feed-entries ( paste -- entries )
     fetch-annotations annotations>> pastebin-feed-entries ;
@@ -135,15 +146,15 @@ M: annotation entity-link
 : paste-feed ( paste -- feed )
     feed new
         swap
-        [ "Paste #" swap id>> number>string append >>title ]
-        [ entity-link >>link ]
+        [ "Paste " swap id>> number>string append >>title ]
+        [ entity-link adjust-url relative-to-request >>link ]
         [ paste-feed-entries >>entries ]
         tri ;
 
 : <paste-feed-action> ( -- action )
     <feed-action>
         [ validate-integer-id ] >>init
-        [ "id" value paste annotations>> paste-feed ] >>feed ;
+        [ "id" value paste paste-feed ] >>feed ;
 
 : validate-entity ( -- )
     {
@@ -165,7 +176,9 @@ M: annotation entity-link
             mode-names "modes" set-value
         ] >>init
 
-        "new-paste" pastebin-template >>template
+        { pastebin "new-paste" } >>template
+
+        [ mode-names "modes" set-value ] >>validate
 
         [
             validate-entity
@@ -173,7 +186,7 @@ M: annotation entity-link
             f <paste>
             [ deposit-entity-slots ]
             [ insert-tuple ]
-            [ id>> "$pastebin/paste" <id-redirect> ]
+            [ id>> paste-link <redirect> ]
             tri
         ] >>submit ;
 
@@ -184,7 +197,7 @@ M: annotation entity-link
         [
             "id" value <paste> delete-tuples
             "id" value f <annotation> delete-tuples
-            "$pastebin/list" f <permanent-redirect>
+            URL" $pastebin/list" <redirect>
         ] >>submit ;
 
 ! ! !
@@ -192,10 +205,10 @@ M: annotation entity-link
 ! ! !
 
 : <new-annotation-action> ( -- action )
-    <page-action>
+    <action>
         [
             { { "id" [ v-integer ] } } validate-params
-            "id" value "$pastebin/paste" <id-redirect>
+            "id" value paste-link <redirect>
         ] >>display
 
         [
@@ -207,10 +220,7 @@ M: annotation entity-link
             "id" value f <annotation>
             [ deposit-entity-slots ]
             [ insert-tuple ]
-            [
-                ! Add anchor here
-                parent>> "$pastebin/paste" <id-redirect>
-            ]
+            [ entity-link <redirect> ]
             tri
         ] >>submit ;
 
@@ -221,12 +231,10 @@ M: annotation entity-link
         [
             f "id" value <annotation> select-tuple
             [ delete-tuples ]
-            [ parent>> "$pastebin/paste" <id-redirect> ]
+            [ parent>> paste-link <redirect> ]
             bi
         ] >>submit ;
 
-TUPLE: pastebin < dispatcher ;
-
 SYMBOL: can-delete-pastes?
 
 can-delete-pastes? define-capability
@@ -242,7 +250,7 @@ can-delete-pastes? define-capability
         <new-annotation-action> "new-annotation" add-responder
         <delete-annotation-action> { can-delete-pastes? } <protected> "delete-annotation" add-responder
     <boilerplate>
-        "pastebin-common" pastebin-template >>template ;
+        { pastebin "pastebin-common" } >>template ;
 
 : init-pastes-table \ paste ensure-table ;
 
index 9ec2cb7976dca830ec746ab3a059a5e25a5bc8f4..a6b3078aa793fb5f88432aa3422d3a2b91d96979 100644 (file)
@@ -2,8 +2,6 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:atom t:title="Pastebin - Atom" t:href="$pastebin/list.atom" />
-
        <t:title>Pastebin</t:title>
 
        <table width="100%">
                <th align="left" width="100">Paste by:</th>
                <th align="left" width="200">Date:</th>
 
-               <t:each-tuple t:values="pastes">
+               <t:bind-each t:name="pastes">
                        <tr>
                                <td><t:a t:href="$pastebin/paste" t:query="id"><t:label t:name="summary" /></t:a></td>
                                <td><t:label t:name="author" /></td>
                                <td><t:label t:name="date" /></td>
                        </tr>
-               </t:each-tuple>
+               </t:bind-each>
        </table>
 
 </t:chloe>
index 4711ca4716d5ea1db834e313a31897edc9066f6f..26a3e6f2066824330fb4638c0bdb7607421027c0 100644 (file)
@@ -5,13 +5,13 @@
        <t:title>Planet Factor Administration</t:title>
 
        <ul>
-               <t:each-tuple t:values="blogroll">
+               <t:bind-each t:name="blogroll">
                        <li>
                                <t:a t:href="$planet-factor/admin/edit-blog" t:query="id">
                                        <t:label t:name="name" />
                                </t:a>
                        </li>
-               </t:each-tuple>
+               </t:bind-each>
        </ul>
 
        <p>
index 741b12345679e59bf8c8c9136be8c72201193f1c..70274d67d9f5c0fc39e8e5a575ca253b1b65602d 100644 (file)
@@ -4,7 +4,7 @@
 
        <p class="news">
                <strong><t:view t:component="title" /></strong> <br/>
-               <t:a value="link" t:session="none" class="more">Read More...</t:a>
+               <t:a value="link" class="more">Read More...</t:a>
        </p>
 
 </t:chloe>
index 5e437173849549e961fcb3b3fbde05a385a7d61f..01fda67316c8e8c634783f088399d323e9ea1340 100644 (file)
@@ -3,7 +3,7 @@
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
        <h2 class="posting-title">
-               <t:a t:value="link" t:session="none"><t:view t:component="title" /></t:a>
+               <t:a t:value="link"><t:view t:component="title" /></t:a>
        </h2>
 
        <p class="posting-body">
@@ -11,7 +11,7 @@
        </p>
 
        <p class="posting-date">
-               <t:a t:value="link" t:session="none"><t:view t:component="pub-date" /></t:a>
+               <t:a t:value="link"><t:view t:component="pub-date" /></t:a>
        </p>
 
 </t:chloe>
index 1338463bcf090479f5ba0974d73f4c564341ff36..8de7216b0e98d8c6ab78cf5c2c27e71d652e2933 100644 (file)
@@ -2,13 +2,13 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:each-tuple t:values="postings">
+       <t:bind-each t:name="postings">
 
                <p class="news">
                        <strong><t:view t:component="title" /></strong> <br/>
-                       <t:a value="link" t:session="none" class="more">Read More...</t:a>
+                       <t:a value="link" class="more">Read More...</t:a>
                </p>
 
-       </t:each-tuple>
+       </t:bind-each>
 
 </t:chloe>
index 29609e12ba6873829d1c980fe7c07399f2495bee..e92f88c2c22b55ae93b5200cd7863f393f45a527 100644 (file)
@@ -9,8 +9,8 @@
                | <t:a t:href="$planet-factor/feed.xml">Atom Feed</t:a>
                | <t:a t:href="$planet-factor/admin">Admin</t:a>
 
-               <t:if t:code="http.server.sessions:uid">
-                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+               <t:if t:code="furnace.sessions:uid">
+                       <t:if t:code="furnace.auth.login:allow-edit-profile?">
                                | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                        </t:if>
        
index 414a59f3b2a1aa97b0c78a04f0f69cdd20b3edf3..c5fa5e25d44bcd3ca22a2e861fa35c29919aa121 100755 (executable)
@@ -3,18 +3,22 @@
 USING: kernel accessors sequences sorting math math.order
 calendar alarms logging concurrency.combinators namespaces
 sequences.lib db.types db.tuples db fry locals hashtables
-html.components html.templates.chloe
-rss xml.writer
+html.components
+rss urls xml.writer
 validators
 http.server
-http.server.actions
-http.server.boilerplate
-http.server.auth.login
-http.server.auth ;
+http.server.dispatchers
+furnace
+furnace.actions
+furnace.boilerplate
+furnace.auth.login
+furnace.auth
+furnace.rss ;
 IN: webapps.planet
 
-: planet-template ( name -- template )
-    "resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
+TUPLE: planet-factor < dispatcher ;
+
+TUPLE: planet-factor-admin < dispatcher ;
 
 TUPLE: blog id name www-url feed-url ;
 
@@ -61,7 +65,7 @@ posting "POSTINGS"
 : <edit-blogroll-action> ( -- action )
     <page-action>
         [ blogroll "blogroll" set-value ] >>init
-        "admin" planet-template >>template ;
+        { planet-factor "admin" } >>template ;
 
 : <planet-action> ( -- action )
     <page-action>
@@ -70,7 +74,7 @@ posting "POSTINGS"
             postings "postings" set-value
         ] >>init
 
-        "planet" planet-template >>template ;
+        { planet-factor "planet" } >>template ;
 
 : planet-feed ( -- feed )
     feed new
@@ -110,7 +114,7 @@ posting "POSTINGS"
     <action>
         [
             update-cached-postings
-            "" f <permanent-redirect>
+            URL" $planet-factor/admin" <redirect>
         ] >>submit ;
 
 : <delete-blog-action> ( -- action )
@@ -119,7 +123,7 @@ posting "POSTINGS"
 
         [
             "id" value <blog> delete-tuples
-            "$planet-factor/admin" f <standard-redirect>
+            URL" $planet-factor/admin" <redirect>
         ] >>submit ;
 
 : validate-blog ( -- )
@@ -129,15 +133,12 @@ posting "POSTINGS"
         { "feed-url" [ v-url ] }
     } validate-params ;
 
-: <id-redirect> ( id next -- response )
-    swap "id" associate <standard-redirect> ;
-
 : deposit-blog-slots ( blog -- )
     { "name" "www-url" "feed-url" } deposit-slots ;
 
 : <new-blog-action> ( -- action )
     <page-action>
-        "new-blog" planet-template >>template
+        { planet-factor "new-blog" } >>template
 
         [ validate-blog ] >>validate
 
@@ -145,7 +146,12 @@ posting "POSTINGS"
             f <blog>
             [ deposit-blog-slots ]
             [ insert-tuple ]
-            [ id>> "$planet-factor/admin/edit-blog" <id-redirect> ]
+            [
+                <url>
+                    "$planet-factor/admin/edit-blog" >>path
+                    swap id>> "id" set-query-param
+                <redirect>
+            ]
             tri
         ] >>submit ;
     
@@ -153,10 +159,10 @@ posting "POSTINGS"
     <page-action>
         [
             validate-integer-id
-            "id" value <blog> select-tuple from-tuple
+            "id" value <blog> select-tuple from-object
         ] >>init
 
-        "edit-blog" planet-template >>template
+        { planet-factor "edit-blog" } >>template
 
         [
             validate-integer-id
@@ -167,12 +173,15 @@ posting "POSTINGS"
             f <blog>
             [ deposit-blog-slots ]
             [ update-tuple ]
-            [ id>> "$planet-factor/admin" <id-redirect> ]
+            [
+                <url>
+                    "$planet-factor/admin" >>path
+                    swap id>> "id" set-query-param
+                <redirect>
+            ]
             tri
         ] >>submit ;
 
-TUPLE: planet-factor-admin < dispatcher ;
-
 : <planet-factor-admin> ( -- responder )
     planet-factor-admin new-dispatcher
         <edit-blogroll-action> "blogroll" add-main-responder
@@ -185,15 +194,13 @@ SYMBOL: can-administer-planet-factor?
 
 can-administer-planet-factor? define-capability
 
-TUPLE: planet-factor < dispatcher ;
-
 : <planet-factor> ( -- responder )
     planet-factor new-dispatcher
         <planet-action> "list" add-main-responder
         <feed-action> "feed.xml" add-responder
         <planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
     <boilerplate>
-        "planet-common" planet-template >>template ;
+        { planet-factor "planet-common" } >>template ;
 
 : start-update-task ( db params -- )
     '[ , , [ update-cached-postings ] with-db ] 10 minutes every drop ;
index 526a9b306b6af7d3e98ade9fb3957203c7f48b46..213c314d7a756bb95e167a9b6e4024593775061e 100644 (file)
@@ -8,10 +8,10 @@
                <tr>
                        <td>
 
-                               <t:each-tuple t:values="postings">
+                               <t:bind-each t:name="postings">
 
                                        <h2 class="posting-title">
-                                               <t:a t:value="link" t:session="none"><t:label t:name="title" /></t:a>
+                                               <t:a t:value="link"><t:label t:name="title" /></t:a>
                                        </h2>
 
                                        <p class="posting-body">
                                        </p>
 
                                        <p class="posting-date">
-                                               <t:a t:value="link" t:session="none"><t:label t:name="pub-date" /></t:a>
+                                               <t:a t:value="link"><t:label t:name="pub-date" /></t:a>
                                        </p>
 
-                               </t:each-tuple>
+                               </t:bind-each>
 
                        </td>
 
@@ -31,7 +31,7 @@
                                <h2>Blogroll</h2>
 
                                <ul>
-                                       <t:each t:values="blogroll">
+                                       <t:each t:name="blogroll">
                                                <li>
                                                        <t:link t:name="value"/>
                                                </li>
index 0974c8ce1bb7bddeb97fa39b3d5eef14ed1bc75d..6bae6e705e8d6aef682e38c51b7fe21c2e6f9517 100644 (file)
                <input type="SUBMIT" value="Done" />
        </t:form>
 
-       <t:if t:value="id">
-       
-               <t:a t:href="$todo-list/view" t:query="id">View</t:a>
-               |
-               <t:button t:action="$todo-list/delete" t:for="id" class="link-button link">Delete</t:button>
-               
-       </t:if>
+       <t:a t:href="$todo-list/view" t:query="id">View</t:a>
+       |
+       <t:button t:action="$todo-list/delete" t:for="id" class="link-button link">Delete</t:button>
 
 </t:chloe>
diff --git a/extra/webapps/todo/new-todo.xml b/extra/webapps/todo/new-todo.xml
new file mode 100644 (file)
index 0000000..f557d53
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:title>New Item</t:title>
+
+       <t:form t:action="$todo-list/new">
+               <table>
+                       <tr><th class="field-label">Summary: </th><td><t:field t:name="summary" /></td></tr>
+                       <tr><th class="field-label">Priority: </th><td><t:field t:name="priority" /></td></tr>
+                       <tr><th class="field-label big-field-label">Description:</th><td><t:textarea t:name="description" t:rows="20" t:cols="60" /></td></tr>
+               </table>
+
+               <input type="SUBMIT" value="Done" />
+       </t:form>
+
+</t:chloe>
index 845c38dbf7c9f6d354ca88f598c477f6d78a886b..036c59030646320f878605de7ace2632f438408f 100644 (file)
@@ -13,7 +13,7 @@
                        <th>Edit</th>
                </tr>
 
-               <t:each-tuple t:values="items">
+               <t:bind-each t:name="items">
 
                        <tr>
                                <td>
@@ -30,7 +30,7 @@
                                </td>
                        </tr>
 
-               </t:each-tuple>
+               </t:bind-each>
 
        </table>
 
index e3b174eaea76afd66047288c43ca86b0109ca10e..3600e2f874b58fce996735bf7fe0d310d3a5bd29 100755 (executable)
@@ -1,18 +1,22 @@
 ! Copyright (c) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors kernel sequences namespaces
-db db.types db.tuples validators hashtables
+db db.types db.tuples validators hashtables urls
 html.components
 html.templates.chloe
-http.server.sessions
-http.server.boilerplate
-http.server.auth
-http.server.actions
-http.server.db
-http.server.auth.login
-http.server ;
+http.server
+http.server.dispatchers
+furnace
+furnace.sessions
+furnace.boilerplate
+furnace.auth
+furnace.actions
+furnace.db
+furnace.auth.login ;
 IN: webapps.todo
 
+TUPLE: todo-list < dispatcher ;
+
 TUPLE: todo uid id priority summary description ;
 
 todo "TODO"
@@ -31,20 +35,14 @@ todo "TODO"
         swap >>id
         uid >>uid ;
 
-: todo-template ( name -- template )
-    "resource:extra/webapps/todo/" swap ".xml" 3append <chloe> ;
-
 : <view-action> ( -- action )
     <page-action>
         [
             validate-integer-id
-            "id" value <todo> select-tuple from-tuple
+            "id" value <todo> select-tuple from-object
         ] >>init
         
-        "view-todo" todo-template >>template ;
-
-: <id-redirect> ( id next -- response )
-    swap "id" associate <standard-redirect> ;
+        { todo-list "view-todo" } >>template ;
 
 : validate-todo ( -- )
     {
@@ -57,15 +55,20 @@ todo "TODO"
     <page-action>
         [ 0 "priority" set-value ] >>init
 
-        "edit-todo" todo-template >>template
+        { todo-list "new-todo" } >>template
 
         [ validate-todo ] >>validate
 
         [
             f <todo>
-                dup { "summary" "description" } deposit-slots
+                dup { "summary" "priority" "description" } deposit-slots
             [ insert-tuple ]
-            [ id>> "$todo-list/view" <id-redirect> ]
+            [
+                <url>
+                    "$todo-list/view" >>path
+                    swap id>> "id" set-query-param
+                <redirect>
+            ]
             bi
         ] >>submit ;
 
@@ -73,10 +76,10 @@ todo "TODO"
     <page-action>
         [
             validate-integer-id
-            "id" value <todo> select-tuple from-tuple
+            "id" value <todo> select-tuple from-object
         ] >>init
 
-        "edit-todo" todo-template >>template
+        { todo-list "edit-todo" } >>template
 
         [
             validate-integer-id
@@ -87,7 +90,12 @@ todo "TODO"
             f <todo>
                 dup { "id" "summary" "priority" "description" } deposit-slots
             [ update-tuple ]
-            [ id>> "$todo-list/view" <id-redirect> ]
+            [
+                <url>
+                    "$todo-list/view" >>path
+                    swap id>> "id" set-query-param
+                <redirect>
+            ]
             bi
         ] >>submit ;
 
@@ -97,15 +105,13 @@ todo "TODO"
 
         [
             "id" get <todo> delete-tuples
-            "$todo-list/list" f <standard-redirect>
+            URL" $todo-list/list" <redirect>
         ] >>submit ;
 
 : <list-action> ( -- action )
     <page-action>
         [ f <todo> select-tuples "items" set-value ] >>init
-        "todo-list" todo-template >>template ;
-
-TUPLE: todo-list < dispatcher ;
+        { todo-list "todo-list" } >>template ;
 
 : <todo-list> ( -- responder )
     todo-list new-dispatcher
@@ -115,5 +121,5 @@ TUPLE: todo-list < dispatcher ;
         <edit-action>   "edit"   add-responder
         <delete-action> "delete" add-responder
     <boilerplate>
-        "todo" todo-template >>template
+        { todo-list "todo" } >>template
     f <protected> ;
index 39ab5cda8b9597e06099c73bf8bcf8b065a7420b..3dd0b9a7d13b279b1a0938f50219d8017ddb2508 100644 (file)
@@ -6,9 +6,9 @@
 
        <div class="navbar">
                  <t:a t:href="$todo-list/list">List Items</t:a>
-               | <t:a t:href="$todo-list/edit">Add Item</t:a>
+               | <t:a t:href="$todo-list/new">Add Item</t:a>
 
-               <t:if t:code="http.server.auth.login:allow-edit-profile?">
+               <t:if t:code="furnace.auth.login:allow-edit-profile?">
                        | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                </t:if>
 
index 3f9ac8d69082732bd6f021992815e0cd890e606c..0c55f8ca76dbe8bceb1b0f297063cd85e662161e 100644 (file)
        
        <tr>
                <th class="field-label big-field-label">Capabilities:</th>
-               <td><t:choice t:name="capabilities" t:choices="all-capabilities" t:multiple="true" /></td>
+               <td>
+                       <t:each t:name="capabilities">
+                               <t:checkbox t:name="@value" t:label="@value" /><br/>
+                       </t:each>
+               </td>
        </tr>
        
        <tr>
index 881dca9c168a5a492446d3052922e6b0272542ae..b1f35c979b4954feee18bfbd3e49084e656f09e5 100644 (file)
        
        <tr>
                <th class="field-label big-field-label">Capabilities:</th>
-               <td><t:choice t:name="capabilities" t:choices="all-capabilities" t:multiple="true" /></td>
+               <td>
+                       <t:each t:name="capabilities">
+                               <li><t:checkbox t:name="@value" t:label="@value" /><br/>
+                       </t:each>
+               </td>
        </tr>
 
        </table>
index cdaf3f5ea9964c21b3b5c75e3c19805f47362d23..b8687274f095a744f149adac11f12915714b58be 100644 (file)
@@ -1,45 +1,59 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel sequences accessors namespaces combinators words
-assocs db.tuples arrays splitting strings validators
+assocs db.tuples arrays splitting strings validators urls
 html.elements
 html.components
-html.templates.chloe
-http.server.boilerplate
-http.server.auth.providers
-http.server.auth.providers.db
-http.server.auth.login
-http.server.auth
-http.server.sessions
-http.server.actions
-http.server ;
+furnace
+furnace.boilerplate
+furnace.auth.providers
+furnace.auth.providers.db
+furnace.auth.login
+furnace.auth
+furnace.sessions
+furnace.actions
+http.server
+http.server.dispatchers ;
 IN: webapps.user-admin
 
-: admin-template ( name -- template )
-    "resource:extra/webapps/user-admin/" swap ".xml" 3append <chloe> ;
+TUPLE: user-admin < dispatcher ;
+
+: word>string ( word -- string )
+    [ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ;
 
 : words>strings ( seq -- seq' )
-    [ [ word-vocabulary ] [ drop ":" ] [ word-name ] tri 3append ] map ;
+    [ word>string ] map ;
+
+: string>word ( string -- word )
+    ":" split1 swap lookup ;
 
 : strings>words ( seq -- seq' )
-    [ ":" split1 swap lookup ] map ;
+    [ string>word ] map ;
 
 : <user-list-action> ( -- action )
     <page-action>
         [ f <user> select-tuples "users" set-value ] >>init
-        "user-list" admin-template >>template ;
+        { user-admin "user-list" } >>template ;
+
+: init-capabilities ( -- )
+    capabilities get words>strings "capabilities" set-value ;
+
+: selected-capabilities ( -- seq )
+    "capabilities" value
+    [ param empty? not ] filter
+    [ string>word ] map ;
 
 : <new-user-action> ( -- action )
     <page-action>
         [
-            "username" param <user> from-tuple
-            capabilities get words>strings "all-capabilities" set-value
+            "username" param <user> from-object
+            init-capabilities
         ] >>init
 
-        "new-user" admin-template >>template
+        { user-admin "new-user" } >>template
 
         [
-            capabilities get words>strings "all-capabilities" set-value
+            init-capabilities
 
             {
                 { "username" [ v-username ] }
@@ -62,10 +76,11 @@ IN: webapps.user-admin
                 "email" value >>email
                 "new-password" value >>encoded-password
                 H{ } clone >>profile
+                selected-capabilities >>capabilities
 
             insert-tuple
 
-            "$user-admin" f <standard-redirect>
+            URL" $user-admin" <redirect>
         ] >>submit ;
 
 : validate-username ( -- )
@@ -77,15 +92,16 @@ IN: webapps.user-admin
             validate-username
 
             "username" value <user> select-tuple
-            [ from-tuple ] [ capabilities>> words>strings "capabilities" set-value ] bi
+            [ from-object ]
+            [ capabilities>> [ "true" swap word>string set-value ] each ] bi
 
-            capabilities get words>strings "all-capabilities" set-value
+            capabilities get words>strings "capabilities" set-value
         ] >>init
 
-        "edit-user" admin-template >>template
+        { user-admin "edit-user" } >>template
 
         [
-            capabilities get words>strings "all-capabilities" set-value
+            init-capabilities
 
             {
                 { "username" [ v-username ] }
@@ -93,7 +109,6 @@ IN: webapps.user-admin
                 { "new-password" [ [ v-password ] v-optional ] }
                 { "verify-password" [ [ v-password ] v-optional ] }
                 { "email" [ [ v-email ] v-optional ] }
-                { "capabilities" [ ] }
             } validate-params
 
             "new-password" "verify-password"
@@ -106,19 +121,15 @@ IN: webapps.user-admin
             "username" value <user> select-tuple
                 "realname" value >>realname
                 "email" value >>email
+                selected-capabilities >>capabilities
 
             "new-password" value empty? [
                 "new-password" value >>encoded-password
             ] unless
 
-            "capabilities" value {
-                { [ dup string? ] [ 1array ] }
-                { [ dup array? ] [ ] }
-            } cond strings>words >>capabilities
-
             update-tuple
 
-            "$user-admin" f <standard-redirect>
+            URL" $user-admin" <redirect>
         ] >>submit ;
 
 : <delete-user-action> ( -- action )
@@ -130,11 +141,9 @@ IN: webapps.user-admin
             [ logout-all-sessions ]
             bi
 
-            "$user-admin" f <standard-redirect>
+            URL" $user-admin" <redirect>
         ] >>submit ;
 
-TUPLE: user-admin < dispatcher ;
-
 SYMBOL: can-administer-users?
 
 can-administer-users? define-capability
@@ -146,7 +155,7 @@ can-administer-users? define-capability
         <edit-user-action> "edit" add-responder
         <delete-user-action> "delete" add-responder
     <boilerplate>
-        "user-admin" admin-template >>template
+        { user-admin "user-admin" } >>template
     { can-administer-users? } <protected> ;
 
 : make-admin ( username -- )
index 05817565ed6e6c3c63f409471ecdce68eb1c02c9..93a701a6963734cb60eb26166f333a7959597bb0 100644 (file)
@@ -6,7 +6,7 @@
                  <t:a t:href="$user-admin">List Users</t:a>
                | <t:a t:href="$user-admin/new">Add User</t:a>
 
-               <t:if t:code="http.server.auth.login:allow-edit-profile?">
+               <t:if t:code="furnace.auth.login:allow-edit-profile?">
                        | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                </t:if>
 
index 020d053e039853407b51579dbae7384b4b95bbc3..83b3f97cf9958281ead989686a8785d920769d62 100644 (file)
@@ -6,13 +6,13 @@
 
        <ul>
 
-               <t:each-tuple t:values="users">
+               <t:bind-each t:name="users">
                        <li>
                                <t:a t:href="$user-admin/edit" t:query="username">
                                        <t:label t:name="username" />
                                </t:a>
                        </li>
-               </t:each-tuple>
+               </t:bind-each>
 
        </ul>
 
index a552c2618f6a7d9cbdb988ca4bd2d7aed8ce7b3e..e19c531d3d383ecf052af6bfa9e6895ac2142bf1 100644 (file)
@@ -5,11 +5,11 @@
        <t:title>All Articles</t:title>
 
        <ul>
-               <t:each-tuple t:values="articles">
+               <t:bind-each t:name="articles">
                        <li>
                                <t:a t:href="view" t:query="title"><t:label t:name="title"/></t:a>
                        </li>
-               </t:each-tuple>
+               </t:bind-each>
        </ul>
 
 </t:chloe>
index 5efa0c045aea60e30bdba5e3723f30444c403959..95fb0de2feb89392132965d2db5234d0097036ea 100644 (file)
@@ -5,7 +5,7 @@
        <t:title>Recent Changes</t:title>
 
        <ul>
-               <t:each-tuple t:values="changes">
+               <t:bind-each t:name="changes">
                        <li>
                                <t:a t:href="title" t:query="title"><t:label t:name="title" /></t:a>
                                on
@@ -13,7 +13,7 @@
                                by
                                <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>
                        </li>
-               </t:each-tuple>
+               </t:bind-each>
        </ul>
 
 </t:chloe>
index 0fb0d6bae678c5a47942df66003dc45834749938..35afe51b66dd66bf4974970e81fd25411f6eabf0 100644 (file)
@@ -2,34 +2,23 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-       <t:bind-tuple t:name="old">
-               <t:title>Diff: <t:label t:name="title" /></t:title>
-       </t:bind-tuple>
+       <t:title>Diff: <t:label t:name="title" /></t:title>
 
        <table>
                <tr>
                        <th class="field-label">Old revision:</th>
-                       <t:bind-tuple t:name="old">
+                       <t:bind t:name="old">
                                <td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.</td>
-                       </t:bind-tuple>
+                       </t:bind>
                </tr>
                <tr>
                        <th class="field-label">New revision:</th>
-                       <t:bind-tuple t:name="old">
+                       <t:bind t:name="old">
                                <td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.</td>
-                       </t:bind-tuple>
+                       </t:bind>
                </tr>
        </table>
 
        <t:comparison t:name="diff" />
 
-       <t:bind-tuple t:name="old">
-               <div class="navbar">
-                       <t:a t:href="$wiki/view" t:query="title">Latest</t:a>
-                       | <t:a t:href="$wiki/revisions" t:query="title">Revisions</t:a>
-                       | <t:a t:href="$wiki/edit" t:query="title">Edit</t:a>
-                       | <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
-               </div>
-       </t:bind-tuple>
-
 </t:chloe>
index 85c8490c5dec6e0b89d45c1510b821cbd5b2c389..057b7f8f7129d8e0886e075bea0ea58675c7e7e4 100644 (file)
@@ -16,5 +16,4 @@
 
        </t:form>
 
-       <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
 </t:chloe>
diff --git a/extra/webapps/wiki/page-common.xml b/extra/webapps/wiki/page-common.xml
new file mode 100644 (file)
index 0000000..1d4b507
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+       <t:call-next-template />
+
+       <div class="navbar">
+               <t:a t:href="$wiki/view" t:query="title">Latest</t:a>
+               | <t:a t:href="$wiki/revisions" t:query="title">Revisions</t:a>
+               | <t:a t:href="$wiki/edit" t:query="title">Edit</t:a>
+               | <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
+       </div>
+
+</t:chloe>
index 4b7bdadf50251f11aa184486f9f0b0ca4eb80f76..2a909e6ab3a017680bd2eb26a2f757f12456c7f2 100644 (file)
@@ -4,15 +4,23 @@
 
        <t:title>Revisions of <t:label t:name="title" /></t:title>
 
-       <ul>
-               <t:each-tuple t:values="revisions">
-                       <li>
-                               <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a>
-                               by
-                               <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>
-                       </li>
-               </t:each-tuple>
-       </ul>
+       <div class="revisions">
+               <table>
+                       <tr>
+                               <th>Revision</th>
+                               <th>Author</th>
+                               <th>Rollback</th>
+                       </tr>
+
+                       <t:bind-each t:name="revisions">
+                               <tr>
+                                       <td> <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a> </td>
+                                       <td> <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a> </td>
+                                       <td> <t:button t:action="rollback" t:for="id" class="link link-button">Rollback</t:button> </td>
+                               </tr>
+                       </t:bind-each>
+               </table>
+       </div>
 
        <h2>View Differences</h2>
 
@@ -23,9 +31,9 @@
                                
                                <td>
                                        <select name="old-id">
-                                               <t:each-tuple t:values="revisions">
+                                               <t:bind-each t:name="revisions">
                                                        <option> <t:label t:name="id" /> </option>
-                                               </t:each-tuple>
+                                               </t:bind-each>
                                        </select>
                                </td>
                        </tr>
@@ -34,9 +42,9 @@
                                
                                <td>
                                        <select name="new-id">
-                                               <t:each-tuple t:values="revisions">
+                                               <t:bind-each t:name="revisions">
                                                        <option> <t:label t:name="id" /> </option>
-                                               </t:each-tuple>
+                                               </t:bind-each>
                                        </select>
                                </td>
                        </tr>
index cf19a3837054227a6f78eda2d47913f27b686757..61809802d99bfa1af980e4a0648d688390360d67 100644 (file)
@@ -5,13 +5,13 @@
        <t:title>Edits by <t:label t:name="author" /></t:title>
 
        <ul>
-               <t:each-tuple t:values="user-edits">
+               <t:bind-each t:name="user-edits">
                        <li>
                                <t:a t:href="view" t:query="title"><t:label t:name="title" /></t:a>
                                on
                                <t:a t:href="revision" t:query="id"><t:label t:name="date" /></t:a>
                        </li>
-               </t:each-tuple>
+               </t:bind-each>
        </ul>
 
 </t:chloe>
index 56c8b37a1ded53378206ffce0e5148affecfc687..30dfb71270eca5578e5badae38c79b9e874d88cb 100644 (file)
@@ -8,12 +8,6 @@
                <t:farkup t:name="content" />
        </div>
 
-       <div class="navbar">
-               <t:a t:href="$wiki/view" t:query="title">Latest</t:a>
-               | <t:a t:href="$wiki/revisions" t:query="title">Revisions</t:a>
-               | <t:a t:href="$wiki/edit" t:query="title">Edit</t:a>
-               | <t:button t:action="$wiki/delete" t:for="title" class="link-button link">Delete</t:button>
-               | This revision created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.
-       </div>
+       <p><em>This revision created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:query="author"><t:label t:name="author" /></t:a>.</em></p>
 
 </t:chloe>
index 23e61e55fe51d7f91dbf858b36d17019b6349807..67a5b91c934d3c873130d6d050abbf3cde7f815c 100644 (file)
@@ -10,9 +10,9 @@
                | <t:a t:href="$wiki/articles">All Articles</t:a>
                | <t:a t:href="$wiki/changes">Recent Changes</t:a>
 
-               <t:if t:code="http.server.sessions:uid">
+               <t:if t:code="furnace.sessions:uid">
 
-                       <t:if t:code="http.server.auth.login:allow-edit-profile?">
+                       <t:if t:code="furnace.auth.login:allow-edit-profile?">
                                | <t:a t:href="$login/edit-profile" t:flow="begin">Edit Profile</t:a>
                        </t:if>
 
index e737cdd898f395229a52bf08bd0ad91f3857b044..83ec918e3baac71c6313c075d97901563c88115c 100644 (file)
@@ -1,21 +1,18 @@
-.comparison table, {
-    border-color: #666;
-    border-style: solid;
-}
-
 .comparison th {
     border-width: 1px;
     border-color: #666;
     border-style: solid;
+    width: 50%;
 }
 
 .comparison table {
+    border-color: #666;
+    border-style: solid;
     border-width: 1px;
     border-spacing: 0;
     border-collapse: collapse;
 }
 
-
 .insert {
     background-color: #9f9;
 }
 .delete {
     background-color: #f99;
 }
+
+.revisions table, .revisions td, .revisions th {
+    border-color: #666;
+    border-style: solid;
+}
+
+.revisions table {
+    border-width: 0 0 1px 1px;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+.revisions td, .revisions th {
+    margin: 0;
+    padding: 4px;
+    border-width: 1px 1px 0 0;
+}
+
index 344a3d40bd9cd40d3ef7619513ee3632742c6e26..6dcf89e208514eb547d7f1bf10842e248eaad77f 100644 (file)
@@ -3,16 +3,19 @@
 USING: accessors kernel hashtables calendar
 namespaces splitting sequences sorting math.order
 html.components
-html.templates.chloe
 http.server
-http.server.actions
-http.server.auth
-http.server.auth.login
-http.server.boilerplate
+http.server.dispatchers
+furnace
+furnace.actions
+furnace.auth
+furnace.auth.login
+furnace.boilerplate
 validators
-db.types db.tuples lcs farkup ;
+db.types db.tuples lcs farkup urls ;
 IN: webapps.wiki
 
+TUPLE: wiki < dispatcher ;
+
 TUPLE: article title revision ;
 
 article "ARTICLES" {
@@ -41,18 +44,17 @@ revision "REVISIONS" {
 
 : init-revisions-table revision ensure-table ;
 
-: wiki-template ( name -- template )
-    "resource:extra/webapps/wiki/" swap ".xml" 3append <chloe> ;
-
-: <title-redirect> ( title next -- response )
-    swap "title" associate <standard-redirect> ;
-
 : validate-title ( -- )
     { { "title" [ v-one-line ] } } validate-params ;
 
 : <main-article-action> ( -- action )
     <action>
-        [ "Front Page" "$wiki/view" <title-redirect> ] >>display ;
+        [
+            <url>
+                "$wiki/view" >>path
+                "Front Page" "title" set-query-param
+            <redirect>
+        ] >>display ;
 
 : <view-article-action> ( -- action )
     <action>
@@ -65,10 +67,13 @@ revision "REVISIONS" {
 
         [
             "title" value dup <article> select-tuple [
-                revision>> <revision> select-tuple from-tuple
-                "view" wiki-template <html-content>
+                revision>> <revision> select-tuple from-object
+                { wiki "view" } <chloe-content>
             ] [
-                "$wiki/edit" <title-redirect>
+                <url>
+                    "$wiki/edit" >>path
+                    swap "title" set-query-param
+                <redirect>
             ] ?if
         ] >>display ;
 
@@ -77,10 +82,10 @@ revision "REVISIONS" {
         [
             { { "id" [ v-integer ] } } validate-params
             "id" value <revision>
-            select-tuple from-tuple
+            select-tuple from-object
         ] >>init
 
-        "view" wiki-template >>template ;
+        { wiki "view" } >>template ;
 
 : add-revision ( revision -- )
     [ insert-tuple ]
@@ -97,11 +102,11 @@ revision "REVISIONS" {
         [
             validate-title
             "title" value <article> select-tuple [
-                revision>> <revision> select-tuple from-tuple
+                revision>> <revision> select-tuple from-object
             ] when*
         ] >>init
 
-        "edit" wiki-template >>template
+        { wiki "edit" } >>template
         
         [
             validate-title
@@ -113,7 +118,12 @@ revision "REVISIONS" {
                 logged-in-user get username>> >>author
                 "content" value >>content
             [ add-revision ]
-            [ title>> "$wiki/view" <title-redirect> ] bi
+            [
+                <url>
+                    "$wiki/view" >>path
+                    swap title>> "title" set-query-param
+                <redirect>
+            ] bi
         ] >>submit ;
 
 : <list-revisions-action> ( -- action )
@@ -125,7 +135,24 @@ revision "REVISIONS" {
             "revisions" set-value
         ] >>init
 
-        "revisions" wiki-template >>template ;
+        { wiki "revisions" } >>template ;
+
+: <rollback-action> ( -- action )
+    <action>
+        [
+            { { "id" [ v-integer ] } } validate-params
+        ] >>validate
+        
+        [
+            "id" value <revision> select-tuple clone f >>id
+            [ add-revision ]
+            [
+                <url>
+                    "$wiki/view" >>path
+                    swap title>> "title" set-query-param
+                <redirect>
+            ] bi
+        ] >>submit ;
 
 : <list-changes-action> ( -- action )
     <page-action>
@@ -135,7 +162,7 @@ revision "REVISIONS" {
             "changes" set-value
         ] >>init
 
-        "changes" wiki-template >>template ;
+        { wiki "changes" } >>template ;
 
 : <delete-action> ( -- action )
     <action>
@@ -144,7 +171,7 @@ revision "REVISIONS" {
         [
             "title" value <article> delete-tuples
             f <revision> "title" value >>title delete-tuples
-            "" f <standard-redirect>
+            URL" $wiki" <redirect>
         ] >>submit ;
 
 : <diff-action> ( -- action )
@@ -157,12 +184,15 @@ revision "REVISIONS" {
 
             "old-id" "new-id"
             [ value <revision> select-tuple ] bi@
-            [ [ "old" set-value ] [ "new" set-value ] bi* ]
+            [
+                [ [ title>> "title" set-value ] [ "old" set-value ] bi ]
+                [ "new" set-value ] bi*
+            ]
             [ [ content>> string-lines ] bi@ diff "diff" set-value ]
             2bi
         ] >>init
 
-        "diff" wiki-template >>template ;
+        { wiki "diff" } >>template ;
 
 : <list-articles-action> ( -- action )
     <page-action>
@@ -172,7 +202,7 @@ revision "REVISIONS" {
             "articles" set-value
         ] >>init
 
-        "articles" wiki-template >>template ;
+        { wiki "articles" } >>template ;
 
 : <user-edits-action> ( -- action )
     <page-action>
@@ -182,21 +212,24 @@ revision "REVISIONS" {
             select-tuples "user-edits" set-value
         ] >>init
 
-        "user-edits" wiki-template >>template ;
-
-TUPLE: wiki < dispatcher ;
+        { wiki "user-edits" } >>template ;
 
 : <wiki> ( -- dispatcher )
     wiki new-dispatcher
-        <main-article-action> "" add-responder
-        <view-article-action> "view" add-responder
-        <view-revision-action> "revision" add-responder
-        <list-revisions-action> "revisions" add-responder
+        <dispatcher>
+            <main-article-action> "" add-responder
+            <view-article-action> "view" add-responder
+            <view-revision-action> "revision" add-responder
+            <list-revisions-action> "revisions" add-responder
+            <diff-action> "diff" add-responder
+            <edit-article-action> { } <protected> "edit" add-responder
+        <boilerplate>
+            { wiki "page-common" } >>template
+        >>default
+        <rollback-action> "rollback" add-responder
         <user-edits-action> "user-edits" add-responder
-        <diff-action> "diff" add-responder
         <list-articles-action> "articles" add-responder
         <list-changes-action> "changes" add-responder
-        <edit-article-action> { } <protected> "edit" add-responder
         <delete-action> { } <protected> "delete" add-responder
     <boilerplate>
-        "wiki-common" wiki-template >>template ;
+        { wiki "wiki-common" } >>template ;
index 6eccddc94af049d2d5304f5e4d187cd83fec3851..9167517bb2ed35a3a1c75dfccf3c855f187b7351 100755 (executable)
@@ -1,14 +1,14 @@
 USING: xmode.tokens xmode.marker xmode.catalog kernel
 html.elements io io.files sequences words io.encodings.utf8
-namespaces xml.entities ;
+namespaces xml.entities accessors ;
 IN: xmode.code2html
 
 : htmlize-tokens ( tokens -- )
     [
-        dup token-str swap token-id [
+        [ str>> ] [ id>> ] bi [
             <span word-name =class span> escape-string write </span>
         ] [
-            write
+            escape-string write
         ] if*
     ] each ;
 
index 2f56a5b8194a13d7a4f50213dea39fee08a18a6e..2bc766dbc6507b12503fec26e5bcfbfd26ff0a41 100755 (executable)
@@ -1,8 +1,8 @@
 ! Copyright (C) 2007, 2008 Slava Pestov.\r
 ! See http://factorcode.org/license.txt for BSD license.\r
 USING: io io.files io.encodings.utf8 namespaces http.server\r
-http.server.static http xmode.code2html kernel sequences\r
-accessors fry ;\r
+http.server.responses http.server.static http xmode.code2html\r
+kernel sequences accessors fry ;\r
 IN: xmode.code2html.responder\r
 \r
 : <sources> ( root -- responder )\r
@@ -12,5 +12,5 @@ IN: xmode.code2html.responder
             , utf8 [\r
                 , file-name input-stream get htmlize-stream\r
             ] with-file-reader\r
-        ] <html-content>\r
+        ] "text/html" <content>\r
     ] <file-responder> ;\r
index f990dd0ed29ff1ada6887e18c53cbca2d40a2481..382fc3fc0970e6c2c62345a8470a1000771d662f 100644 (file)
@@ -1 +1,2 @@
 Daniel Ehrenberg
+Walton Chan
index 662369d96e92dfa6d724799018c1f9f95239bc31..98287365af5eebd5839b756f82e9e6913e766277 100644 (file)
@@ -1 +1 @@
-Yahoo! search example using XML-RPC
+Yahoo! search example using XML
index dc684af726c83d374c9c87aac46c8f964149eca3..827d6ecfd0d3312ec94dfb90d07fc4517f939655 100644 (file)
@@ -1,4 +1,4 @@
-USING: tools.test yahoo kernel io.files xml sequences ;
+USING: tools.test yahoo kernel io.files xml sequences accessors urls ;
 
 [ T{
     result
@@ -8,4 +8,4 @@ USING: tools.test yahoo kernel io.files xml sequences ;
     "Official site with news, tour dates, discography, store, community, and more."
 } ] [ "resource:extra/yahoo/test-results.xml" file>xml parse-yahoo first ] unit-test
 
-[ "http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=Factor-search&query=hi&results=1" ] [ "hi" 1 "Factor-search" query ] unit-test
+[ URL" http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=Factor-search&query=hi&results=2&similar_ok=1" ] [ "hi" <search> "Factor-search" >>appid 2 >>results t >>similar-ok query ] unit-test
index 214ad04979e0c667a58824870a2cbb043b217bea..c47b8be15c92340cef3b53be27ab99c050fe11bb 100755 (executable)
@@ -1,13 +1,16 @@
-! Copyright (C) 2006 Daniel Ehrenberg
+! Copyright (C) 2006 Daniel Ehrenberg, Walton Chan
 ! See http://factorcode.org/license.txt for BSD license.
 USING: http.client xml xml.utilities kernel sequences
-namespaces http math.parser help math.order locals ;
+math.parser urls accessors locals ;
 IN: yahoo
 
 TUPLE: result title url summary ;
 
 C: <result> result
 
+TUPLE: search query results adult-ok start appid region type
+format similar-ok language country site subscription license ;
+
 : parse-yahoo ( xml -- seq )
     "Result" deep-tags-named [
         { "Title" "Url" "Summary" }
@@ -16,21 +19,44 @@ C: <result> result
     ] map ;
 
 : yahoo-url ( -- str )
-    "http://search.yahooapis.com/WebSearchService/V1/webSearch" ;
-
-:: query ( search num appid -- url )
-    [
-        yahoo-url %
-        "?appid=" % appid %
-        "&query=" % search url-encode %
-        "&results=" % num #
-    ] "" make ;
+    URL" http://search.yahooapis.com/WebSearchService/V1/webSearch" ;
+
+:: param ( search url name quot -- search url )
+    search url search quot call
+    [ name set-query-param ] when* ; inline
+
+: num-param ( search str quot -- search )
+    [ dup [ number>string ] when ] compose param ; inline
+
+: bool-param ( search str quot -- search )
+    [ "1" and ] compose param ; inline
+
+: query ( search -- url )
+    yahoo-url clone
+    "appid" [ appid>> ] param
+    "query" [ query>> ] param
+    "region" [ region>> ] param
+    "type" [ type>> ] param
+    "format" [ format>> ] param
+    "language" [ language>> ] param
+    "country" [ country>> ] param
+    "site" [ site>> ] param
+    "subscription" [ subscription>> ] param
+    "license" [ license>> ] param
+    "results" [ results>> ] num-param
+    "start" [ start>> ] num-param
+    "adult_ok" [ adult-ok>> ] bool-param
+    "similar_ok" [ similar-ok>> ] bool-param
+    nip ;
 
 : factor-id
     "fRrVAKzV34GDyeRw6bUHDhEWHRedwfOC7e61wwXZLgGF80E67spxdQXuugBe2pgIevMmKwA-" ;
 
-: search-yahoo/id ( search num id -- seq )
-    query http-get string>xml parse-yahoo ;
+: <search> ( query -- search )
+    search new
+        factor-id >>appid
+        10 >>results
+        swap >>query ;
 
-: search-yahoo ( search num -- seq )
-    factor-id search-yahoo/id ;
+: search-yahoo ( search -- seq )
+    query http-get string>xml parse-yahoo ;
diff --git a/unmaintained/cont-responder/callbacks-tests.factor b/unmaintained/cont-responder/callbacks-tests.factor
new file mode 100755 (executable)
index 0000000..db6f43c
--- /dev/null
@@ -0,0 +1,67 @@
+USING: furnace furnace.actions furnace.callbacks accessors\r
+http http.server http.server.responses tools.test\r
+namespaces io fry sequences\r
+splitting kernel hashtables continuations ;\r
+IN: furnace.callbacks.tests\r
+\r
+[ 123 ] [\r
+    [\r
+        <request> "GET" >>method init-request\r
+        [\r
+            exit-continuation set\r
+            { }\r
+            <action> [ [ "hello" print 123 ] show-final ] >>display\r
+            <callback-responder>\r
+            call-responder\r
+        ] callcc1\r
+    ] with-scope\r
+] unit-test\r
+\r
+[\r
+    <action> [\r
+        [\r
+            "hello" print\r
+            "text/html" <content>\r
+        ] show-page\r
+        "byebye" print\r
+        [ 123 ] show-final\r
+    ] >>display\r
+    <callback-responder> "r" set\r
+\r
+    [ 123 ] [\r
+        <request> init-request\r
+\r
+        [\r
+            exit-continuation set\r
+            <request> "GET" >>method init-request\r
+            { } "r" get call-responder\r
+        ] callcc1\r
+\r
+        body>> first\r
+\r
+        <request>\r
+            "GET" >>method\r
+            dup url>> rot cont-id associate >>query drop\r
+            dup url>> "/" >>path drop\r
+        init-request\r
+\r
+        [\r
+            exit-continuation set\r
+            { }\r
+            "r" get call-responder\r
+        ] callcc1\r
+\r
+        ! get-post-get\r
+        <request>\r
+            "GET" >>method\r
+            dup url>> rot "location" header query>> >>query drop\r
+            dup url>> "/" >>path drop\r
+        init-request\r
+\r
+        [\r
+            exit-continuation set\r
+            { }\r
+            "r" get call-responder\r
+        ] callcc1\r
+    ] unit-test\r
+] with-scope\r
diff --git a/unmaintained/cont-responder/callbacks.factor b/unmaintained/cont-responder/callbacks.factor
new file mode 100755 (executable)
index 0000000..1931be2
--- /dev/null
@@ -0,0 +1,122 @@
+! Copyright (C) 2004 Chris Double.\r
+! Copyright (C) 2006, 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: http http.server io kernel math namespaces\r
+continuations calendar sequences assocs hashtables\r
+accessors arrays alarms quotations combinators fry\r
+http.server.redirection furnace assocs.lib urls ;\r
+IN: furnace.callbacks\r
+\r
+SYMBOL: responder\r
+\r
+TUPLE: callback-responder responder callbacks ;\r
+\r
+: <callback-responder> ( responder -- responder' )\r
+    H{ } clone callback-responder boa ;\r
+\r
+TUPLE: callback cont quot expires alarm responder ;\r
+\r
+: timeout 20 minutes ;\r
+\r
+: timeout-callback ( callback -- )\r
+    [ alarm>> cancel-alarm ]\r
+    [ dup responder>> callbacks>> delete-at ]\r
+    bi ;\r
+\r
+: touch-callback ( callback -- )\r
+    dup expires>> [\r
+        dup alarm>> [ cancel-alarm ] when*\r
+        dup '[ , timeout-callback ] timeout later >>alarm\r
+    ] when drop ;\r
+\r
+: <callback> ( cont quot expires? -- callback )\r
+    f callback-responder get callback boa\r
+    dup touch-callback ;\r
+\r
+: invoke-callback ( callback -- response )\r
+    [ touch-callback ]\r
+    [ quot>> request get exit-continuation get 3array ]\r
+    [ cont>> continue-with ]\r
+    tri ;\r
+\r
+: register-callback ( cont quot expires? -- id )\r
+    <callback> callback-responder get callbacks>> set-at-unique ;\r
+\r
+: forward-to-url ( url -- * )\r
+    #! When executed inside a 'show' call, this will force a\r
+    #! HTTP 302 to occur to instruct the browser to forward to\r
+    #! the request URL.\r
+    <temporary-redirect> exit-with ;\r
+\r
+: cont-id "factorcontid" ;\r
+\r
+: forward-to-id ( id -- * )\r
+    #! When executed inside a 'show' call, this will force a\r
+    #! HTTP 302 to occur to instruct the browser to forward to\r
+    #! the request URL.\r
+    <url>\r
+        swap cont-id set-query-param forward-to-url ;\r
+\r
+: restore-request ( pair -- )\r
+    first3 exit-continuation set request set call ;\r
+\r
+SYMBOL: post-refresh-get?\r
+\r
+: redirect-to-here ( -- )\r
+    #! Force a redirect to the client browser so that the browser\r
+    #! goes to the current point in the code. This forces an URL\r
+    #! change on the browser so that refreshing that URL will\r
+    #! immediately run from this code point. This prevents the\r
+    #! "this request will issue a POST" warning from the browser\r
+    #! and prevents re-running the previous POST logic. This is\r
+    #! known as the 'post-refresh-get' pattern.\r
+    post-refresh-get? get [\r
+        [\r
+            [ ] t register-callback forward-to-id\r
+        ] callcc1 restore-request\r
+    ] [\r
+        post-refresh-get? on\r
+    ] if ;\r
+\r
+SYMBOL: current-show\r
+\r
+: store-current-show ( -- )\r
+    #! Store the current continuation in the variable 'current-show'\r
+    #! so it can be returned to later by 'quot-id'. Note that it\r
+    #! recalls itself when the continuation is called to ensure that\r
+    #! it resets its value back to the most recent show call.\r
+    [ current-show set f ] callcc1\r
+    [ restore-request store-current-show ] when* ;\r
+\r
+: show-final ( quot -- * )\r
+    [ redirect-to-here store-current-show ] dip\r
+    call exit-with ; inline\r
+\r
+: resuming-callback ( responder request -- id )\r
+    url>> cont-id query-param swap callbacks>> at ;\r
+\r
+M: callback-responder call-responder* ( path responder -- response )\r
+    '[\r
+        , ,\r
+\r
+        [ callback-responder set ]\r
+        [ request get resuming-callback ] bi\r
+\r
+        [\r
+            invoke-callback\r
+        ] [\r
+            callback-responder get responder>> call-responder\r
+        ] ?if\r
+    ] with-exit-continuation ;\r
+\r
+: show-page ( quot -- )\r
+    [ redirect-to-here store-current-show ] dip\r
+    [\r
+        [ ] t register-callback swap call exit-with\r
+    ] callcc1 restore-request ; inline\r
+\r
+: quot-id ( quot -- id )\r
+    current-show get swap t register-callback ;\r
+\r
+: quot-url ( quot -- url )\r
+    quot-id f swap cont-id associate derive-url ;\r