diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,42 @@
+Version 0.8 released 13 May 2011
+
+* Uses happstack 6.
+
+* Added textile and org export formats, textile page format.
+
+* Added support for RPXNow authentication, based on a patch from
+  Pasqualino Titto Assini.
+
+* Added `authentication-required` field in config.
+
+    * If set to 'modify', authentication is required to modify the wiki.
+    * If set to 'read', atuhentication is required to view the wiki.
+    * If set to 'none', authentication is never required, and pages can
+      be edited anonymously.
+
+    API changes:
+
+    * currentUser moved to Authentication module
+    * requireAuthentication added to Config
+    * Added AuthenticationLevel type
+    * requireUser renamed authenticate, parameter for AuthenticationLevel
+      added; requireUserThat renamed authenticateUserThat
+
+* MathML and jsMath now work in the preview pane (Sean Seefried).
+
+* Use footnotes.js for fancy footnote styling (gwern).
+
+* Added a `--listen` parameter to specify the listen device
+  (Timo B. Hübel).
+
+* Removed withInput.
+
+* Replaced fileContents with filePath in Params.
+
+* Fixed bug in uploadForm.js which caused a prefix to be added in the
+  default wikiname.
+
+
 Version 0.7.3.12 released 01 Feb 2011
 
 * Use pandoc 1.8.
diff --git a/Network/Gitit.hs b/Network/Gitit.hs
--- a/Network/Gitit.hs
+++ b/Network/Gitit.hs
@@ -22,7 +22,7 @@
 
 > import Network.Gitit
 > import Happstack.Server.SimpleHTTP
-> 
+>
 > main = do
 >   conf <- getDefaultConfig
 >   createStaticIfMissing conf
@@ -38,12 +38,12 @@
 > import Control.Monad
 > import Text.XHtml hiding (dir)
 > import Happstack.Server.SimpleHTTP
-> 
+>
 > type WikiSpec = (String, FileStoreType, PageType)
-> 
+>
 > wikis = [ ("markdownWiki", Git, Markdown)
 >         , ("latexWiki", Darcs, LaTeX) ]
-> 
+>
 > -- custom authentication
 > myWithUser :: Handler -> Handler
 > myWithUser handler = do
@@ -66,7 +66,7 @@
 > indexPage = ok $ toResponse $
 >   (p << "Wiki index") +++
 >   ulist << map (\(path', _, _) -> li << hotlink (path' ++ "/") << path') wikis
-> 
+>
 > main = do
 >   conf <- getDefaultConfig
 >   let conf' = conf{authHandler = myAuthHandler, withUser = myWithUser}
@@ -125,31 +125,37 @@
 import Prelude hiding (readFile)
 import qualified Data.ByteString.Char8 as B
 import System.FilePath ((</>))
+import System.Directory (getTemporaryDirectory)
 import Safe
 
 -- | Happstack handler for a gitit wiki.
 wiki :: Config -> ServerPart Response
 wiki conf = do
+  tempDir <- liftIO getTemporaryDirectory
+  let maxSize = fromIntegral $ maxUploadSize conf
+  decodeBody $ defaultBodyPolicy tempDir maxSize maxSize maxSize
   let static = staticDir conf
   defaultStatic <- liftIO $ getDataFileName $ "data" </> "static"
   -- if file not found in staticDir, we check also in the data/static
   -- directory, which contains defaults
   let staticHandler = withExpiresHeaders $
-        fileServeStrict' [] static `mplus` fileServeStrict' [] defaultStatic
-  let handlers = [debugHandler | debugMode conf] ++ (authHandler conf : wikiHandlers)
+        serveDirectory' static `mplus` serveDirectory' defaultStatic
+  let debugHandler' = msum [debugHandler | debugMode conf]
+  let handlers = debugHandler' `mplus` authHandler conf `mplus`
+                 authenticate ForRead (msum wikiHandlers)
   let fs = filestoreFromConfig conf
   let ws = WikiState { wikiConfig = conf, wikiFileStore = fs }
   if compressResponses conf
      then compressedResponseFilter
      else return ""
-  staticHandler `mplus` runHandler ws (withUser conf $ msum handlers)
+  staticHandler `mplus` runHandler ws (withUser conf handlers)
 
--- | Like 'fileServeStrict', but if file is not found, fail instead of
+-- | Like 'serveDirectory', but if file is not found, fail instead of
 -- returning a 404 error.
-fileServeStrict' :: [FilePath] -> FilePath -> ServerPart Response
-fileServeStrict' ps p = do
+serveDirectory' :: FilePath -> ServerPart Response
+serveDirectory' p = do
   rq <- askRq
-  resp' <- fileServeStrict ps p
+  resp' <- serveDirectory EnableBrowsing [] p
   if rsCode resp' == 404 || lastNote "fileServeStrict'" (rqUri rq) == '/'
      then mzero  -- pass through if not found or directory index
      else do
@@ -163,13 +169,12 @@
   [ -- redirect /wiki -> /wiki/ when gitit is being served at /wiki
     -- so that relative wikilinks on the page will work properly:
     guardBareBase >> getWikiBase >>= \b -> movedPermanently (b ++ "/") (toResponse ())
-  , dir "_user"     currentUser
   , dir "_activity" showActivity
   , dir "_go"       goToPage
   , dir "_search"   searchResults
   , dir "_upload"   $  do guard =<< return . uploadsAllowed =<< getConfig
-                          msum [ methodOnly GET  >> requireUser uploadForm 
-                                 , methodOnly POST >> requireUser uploadFile ]
+                          msum [ methodOnly GET  >> authenticate ForModify uploadForm
+                                 , methodOnly POST >> authenticate ForModify uploadFile ]
   , dir "_random"   $ methodOnly GET  >> randomPage
   , dir "_index"    indexPage
   , dir "_feed"     feedHandler
@@ -182,22 +187,22 @@
   , dir "_history"  $ msum
       [ showPageHistory
       , guardPath isSourceCode >> showFileHistory ]
-  , dir "_edit" $ requireUser (unlessNoEdit editPage showPage)
+  , dir "_edit" $ authenticate ForModify (unlessNoEdit editPage showPage)
   , dir "_diff" $ msum
       [ showPageDiff
       , guardPath isSourceCode >> showFileDiff ]
   , dir "_discuss" discussPage
   , dir "_delete" $ msum
       [ methodOnly GET  >>
-          requireUser (unlessNoDelete confirmDelete showPage)
+          authenticate ForModify (unlessNoDelete confirmDelete showPage)
       , methodOnly POST >>
-          requireUser (unlessNoDelete deletePage showPage) ]
+          authenticate ForModify (unlessNoDelete deletePage showPage) ]
   , dir "_preview" preview
   , guardIndex >> indexPage
   , guardCommand "export" >> exportPage
   , methodOnly POST >> guardCommand "cancel" >> showPage
   , methodOnly POST >> guardCommand "update" >>
-      requireUser (unlessNoEdit updatePage showPage)
+      authenticate ForModify (unlessNoEdit updatePage showPage)
   , showPage
   , guardPath isSourceCode >> methodOnly GET >> showHighlightedSource
   , handleAny
@@ -214,8 +219,6 @@
 runHandler :: WikiState -> Handler -> ServerPart Response
 runHandler = mapServerPartT . unpackReaderT
 
-unpackReaderT:: (Monad m)
-    => c 
-    -> (ReaderT c m) (Maybe ((Either b a), FilterFun b))
-    -> m (Maybe ((Either b a), FilterFun b))
-unpackReaderT st handler = runReaderT handler st
+unpackReaderT :: s -> UnWebT (ReaderT s IO) a -> UnWebT IO a
+unpackReaderT st uw = runReaderT uw st
+
diff --git a/Network/Gitit/Authentication.hs b/Network/Gitit/Authentication.hs
--- a/Network/Gitit/Authentication.hs
+++ b/Network/Gitit/Authentication.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE ScopedTypeVariables, StandaloneDeriving #-}
 {-
 Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>,
                    Henry Laxen <nadine.and.henry@pobox.com>
@@ -20,7 +21,10 @@
 {- Handlers for registering and authenticating users.
 -}
 
-module Network.Gitit.Authentication (formAuthHandlers, httpAuthHandlers, loginUserForm) where
+module Network.Gitit.Authentication ( loginUserForm
+                                    , formAuthHandlers
+                                    , httpAuthHandlers
+                                    , rpxAuthHandlers) where
 
 import Network.Gitit.State
 import Network.Gitit.Types
@@ -32,18 +36,21 @@
 import Text.XHtml hiding ( (</>), dir, method, password, rev )
 import qualified Text.XHtml as X ( password )
 import System.Process (readProcessWithExitCode)
-import Control.Monad (unless, liftM)
+import Control.Monad (unless, liftM, mplus)
 import Control.Monad.Trans (MonadIO(), liftIO)
 import System.Exit
 import System.Log.Logger (logM, Priority(..))
 import Data.Char (isAlphaNum, isAlpha, isAscii)
+import qualified Data.Map as M
 import Text.Pandoc.Shared (substitute)
-import Data.Maybe (isJust, fromJust)
+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe)
 import Network.URL (encString, exportURL, add_param, importURL)
 import Network.BSD (getHostName)
 import qualified Text.StringTemplate as T
-import Network.HTTP (urlEncodeVars)
-import Codec.Binary.UTF8.String (encodeString) 
+import Network.HTTP (urlEncodeVars, urlDecode, urlEncode)
+import Codec.Binary.UTF8.String (encodeString)
+import Data.ByteString.UTF8 (toString)
+import Network.Gitit.Rpxnow as R
 
 data ValidationType = Register
                     | ResetPassword
@@ -111,7 +118,7 @@
                     response
     else registerForm >>=
          formattedPage defaultPageLayout{
-                         pgMessages = errors, 
+                         pgMessages = errors,
                          pgShowPageTools = False,
                          pgTabs = [],
                          pgTitle = "Register for an account"
@@ -155,7 +162,7 @@
   let errors = case (knownUser, resetCodeMatches) of
                      (True, True)   -> []
                      (True, False)  -> ["Your reset code is invalid"]
-                     (False, _)     -> ["User " ++ uname ++ " is not known"] 
+                     (False, _)     -> ["User " ++ uname ++ " is not known"]
   if null errors
      then postValidate (fromJust user)
      else registerForm >>=
@@ -366,7 +373,7 @@
   if allowed
     then do
       key <- newSession (SessionData uname)
-      addCookie (sessionTimeout cfg) (mkCookie "sid" (show key))
+      addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show key))
       seeOther (encUrl destination) $ toResponse $ p << ("Welcome, " ++ uname)
     else
       withMessages ["Invalid username or password."] loginUserForm
@@ -383,8 +390,7 @@
   case key of
        Just k  -> do
          delSession k
-         -- make cookie expire immediately, effectively deleting it
-         addCookie 0 (mkCookie "sid" "-1")
+         expireCookie "sid"
        Nothing -> return ()
   seeOther (encUrl dest) $ toResponse "You have been logged out."
 
@@ -407,6 +413,7 @@
   , dir "_resetPassword"   $ methodSP POST $ withData resetPasswordRequest
   , dir "_doResetPassword" $ methodSP GET  $ withData resetPassword
   , dir "_doResetPassword" $ methodSP POST $ withData doResetPassword
+  , dir "_user" currentUser
   ]
 
 loginUserHTTP :: Params -> Handler
@@ -421,4 +428,72 @@
 httpAuthHandlers :: [Handler]
 httpAuthHandlers =
   [ dir "_logout" $ logoutUserHTTP
-  , dir "_login"  $ withData loginUserHTTP ]
+  , dir "_login"  $ withData loginUserHTTP
+  , dir "_user" currentUser ]
+
+-- Login using RPX (see RPX development docs at https://rpxnow.com/docs)
+loginRPXUser :: RPars  -- ^ The parameters passed by the RPX callback call (after authentication has taken place
+             -> Handler
+loginRPXUser params = do
+  cfg <- getConfig
+  ref <- getReferer
+  let mtoken = rToken params
+  if isNothing mtoken
+     then do
+       let url = baseUrl cfg ++ "/_login?destination=" ++
+                  (fromMaybe ref $ rDestination params)
+       if null (rpxDomain cfg)
+          then error "rpx-domain is not set."
+          else do
+             let rpx = "https://" ++ rpxDomain cfg ++
+                       ".rpxnow.com/openid/v2/signin?token_url=" ++
+                       urlEncode url
+             see rpx
+     else do -- We got an answer from RPX, this might also return an exception.
+       uid' :: Either String R.Identifier <- liftIO $
+                      R.authenticate (rpxKey cfg) $ fromJust mtoken
+       uid <- case uid' of
+                   Right u -> return u
+                   Left err -> error err
+       liftIO $ logM "gitit.loginRPXUser" DEBUG $ "uid:" ++ show uid
+       -- We need to get an unique identifier for the user
+       -- The 'identifier' is always present but can be rather cryptic
+       -- The 'verifiedEmail' is also unique and is a more readable choice
+       -- so we use it if present.
+       let userId = R.userIdentifier uid
+       let email  = prop "verifiedEmail" uid
+       user <- liftIO $ mkUser (fromMaybe userId email) (fromMaybe "" email) "none"
+       updateGititState $ \s -> s { users = M.insert userId user (users s) }
+       key <- newSession (SessionData userId)
+       addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show key))
+       see $ fromJust $ rDestination params
+      where
+        prop pname info = lookup pname $ R.userData info
+        see url = seeOther (encUrl url) $ toResponse noHtml
+
+-- The parameters passed by the RPX callback call.
+data RPars = RPars { rToken       :: Maybe String
+                   , rDestination :: Maybe String }
+                   deriving Show
+
+instance FromData RPars where
+     fromData = do
+         vtoken <- liftM Just (look "token") `mplus` return Nothing
+         vDestination <- liftM (Just . urlDecode) (look "destination") `mplus`
+                           return Nothing
+         return RPars { rToken = vtoken
+                      , rDestination = vDestination }
+
+rpxAuthHandlers :: [Handler]
+rpxAuthHandlers =
+  [ dir "_logout" $ methodSP GET $ withData logoutUser
+  , dir "_login"  $ withData loginRPXUser
+  , dir "_user" currentUser ]
+
+-- | Returns username of logged in user or null string if nobody logged in.
+currentUser :: Handler
+currentUser = do
+  req <- askRq
+  ok $ toResponse $ maybe "" toString (getHeader "REMOTE_USER" req)
+
+
diff --git a/Network/Gitit/Config.hs b/Network/Gitit/Config.hs
--- a/Network/Gitit/Config.hs
+++ b/Network/Gitit/Config.hs
@@ -27,7 +27,7 @@
 import Network.Gitit.Types
 import Network.Gitit.Server (mimeTypes)
 import Network.Gitit.Framework
-import Network.Gitit.Authentication (formAuthHandlers, httpAuthHandlers)
+import Network.Gitit.Authentication (formAuthHandlers, rpxAuthHandlers, httpAuthHandlers)
 import Network.Gitit.Util (parsePageType, readFileUTF8)
 import System.Log.Logger (logM, Priority(..))
 import qualified Data.Map as M
@@ -66,6 +66,7 @@
       cfDefaultPageType <- get cp "DEFAULT" "default-page-type"
       cfMathMethod <- get cp "DEFAULT" "math"
       cfShowLHSBirdTracks <- get cp "DEFAULT" "show-lhs-bird-tracks"
+      cfRequireAuthentication <- get cp "DEFAULT" "require-authentication"
       cfAuthenticationMethod <- get cp "DEFAULT" "authentication-method"
       cfUserFile <- get cp "DEFAULT" "user-file"
       cfSessionTimeout <- get cp "DEFAULT" "session-timeout"
@@ -88,6 +89,8 @@
       cfUseRecaptcha <- get cp "DEFAULT" "use-recaptcha"
       cfRecaptchaPublicKey <- get cp "DEFAULT" "recaptcha-public-key"
       cfRecaptchaPrivateKey <- get cp "DEFAULT" "recaptcha-private-key"
+      cfRPXDomain <- get cp "DEFAULT" "rpx-domain"
+      cfRPXKey <- get cp "DEFAULT" "rpx-key"
       cfCompressResponses <- get cp "DEFAULT" "compress-responses"
       cfUseCache <- get cp "DEFAULT" "use-cache"
       cfCacheDir <- get cp "DEFAULT" "cache-dir"
@@ -115,6 +118,8 @@
                         "darcs"     -> Darcs
                         "mercurial" -> Mercurial
                         x           -> error $ "Unknown repository type: " ++ x
+      when (authMethod == "rpx" && cfRPXDomain == "") $
+         liftIO $ logM "gitit" WARNING $ "rpx-domain is not set"
 
       return $! Config{
           repositoryPath       = cfRepositoryPath
@@ -130,10 +135,18 @@
         , withUser             = case authMethod of
                                       "form"     -> withUserFromSession
                                       "http"     -> withUserFromHTTPAuth
+                                      "rpx"      -> withUserFromSession
                                       _          -> id
+        , requireAuthentication = case map toLower cfRequireAuthentication of
+                                       "none"    -> Never
+                                       "modify"  -> ForModify
+                                       "read"    -> ForRead
+                                       _         -> ForModify
+
         , authHandler          = case authMethod of
                                       "form"     -> msum formAuthHandlers
                                       "http"     -> msum httpAuthHandlers
+                                      "rpx"      -> msum rpxAuthHandlers
                                       _          -> mzero
         , userFile             = cfUserFile
         , sessionTimeout       = readNumber "session-timeout" cfSessionTimeout * 60  -- convert minutes -> seconds
@@ -162,6 +175,8 @@
         , useRecaptcha         = cfUseRecaptcha
         , recaptchaPublicKey   = cfRecaptchaPublicKey
         , recaptchaPrivateKey  = cfRecaptchaPrivateKey
+        , rpxDomain            = cfRPXDomain
+        , rpxKey               = cfRPXKey
         , compressResponses    = cfCompressResponses
         , useCache             = cfUseCache
         , cacheDir             = cfCacheDir
diff --git a/Network/Gitit/ContentTransformer.hs b/Network/Gitit/ContentTransformer.hs
--- a/Network/Gitit/ContentTransformer.hs
+++ b/Network/Gitit/ContentTransformer.hs
@@ -68,31 +68,30 @@
   )
 where
 
-import Prelude hiding (catch)
-import Network.Gitit.Server
+import Control.Exception (throwIO, catch)
+import Control.Monad.State
+import Data.Maybe (isNothing, mapMaybe)
+import Network.Gitit.Cache (lookupCache, cacheContents)
+import Network.Gitit.Export (exportFormats)
 import Network.Gitit.Framework
-import Network.Gitit.State
-import Network.Gitit.Types
 import Network.Gitit.Layout
-import Network.Gitit.Export (exportFormats)
 import Network.Gitit.Page (stringToPage)
-import Network.Gitit.Cache (lookupCache, cacheContents)
-import qualified Data.FileStore as FS
-import Data.Maybe (mapMaybe)
+import Network.Gitit.Server
+import Network.Gitit.State
+import Network.Gitit.Types
+import Network.URI (isUnescapedInURI)
+import Network.URL (encString)
+import Prelude hiding (catch)
+import System.FilePath
+import Text.HTML.SanitizeXSS (sanitizeBalance)
+import Text.Highlighting.Kate
 import Text.Pandoc hiding (MathML, WebTeX)
-import qualified Text.Pandoc as Pandoc
 import Text.Pandoc.Shared (ObfuscationMethod(..))
 import Text.XHtml hiding ( (</>), dir, method, password, rev )
-import Text.Highlighting.Kate
-import Data.Maybe (isNothing)
-import System.FilePath
-import Control.Monad.State
-import Control.Exception (throwIO, catch)
-import qualified Data.ByteString as S (concat) 
+import qualified Data.ByteString as S (concat)
 import qualified Data.ByteString.Lazy as L (toChunks, fromChunks)
-import Network.URL (encString)
-import Network.URI (isUnescapedInURI)
-import Text.HTML.SanitizeXSS (sanitizeBalance)
+import qualified Data.FileStore as FS
+import qualified Text.Pandoc as Pandoc
 --
 -- ContentTransformer runners
 --
@@ -100,7 +99,7 @@
 runTransformer :: ToMessage a
                => (String -> String)
                -> ContentTransformer a
-               -> GititServerPart a 
+               -> GititServerPart a
 runTransformer pathFor xform = withData $ \params -> do
   page <- getPage
   cfg <- getConfig
@@ -122,7 +121,7 @@
 -- specialized to wiki pages.
 runPageTransformer :: ToMessage a
                    => ContentTransformer a
-                   -> GititServerPart a 
+                   -> GititServerPart a
 runPageTransformer = runTransformer pathForPage
 
 -- | Converts a @ContentTransformer@ into a @GititServerPart@;
@@ -150,7 +149,7 @@
 showPage = runPageTransformer htmlViaPandoc
 
 -- | Responds with page exported into selected format.
-exportPage :: Handler 
+exportPage :: Handler
 exportPage = runPageTransformer exportViaPandoc
 
 -- | Responds with highlighted source code.
@@ -184,7 +183,7 @@
 rawTextResponse = rawContents >>= textResponse
 
 -- | Responds with a wiki page in the format specified
--- by the @format@ parameter. 
+-- by the @format@ parameter.
 exportViaPandoc :: ContentTransformer Response
 exportViaPandoc = rawContents >>=
                   maybe mzero return >>=
@@ -212,7 +211,7 @@
 highlightRawSource :: ContentTransformer Response
 highlightRawSource =
   cachedHtml `mplus`
-    (updateLayout (\l -> l { pgTabs = [ViewTab,HistoryTab] }) >> 
+    (updateLayout (\l -> l { pgTabs = [ViewTab,HistoryTab] }) >>
      rawContents >>=
      highlightSource >>=
      applyWikiTemplate >>=
@@ -223,8 +222,8 @@
 --
 
 -- | Caches a response (actually just the response body) on disk,
--- unless the context indicates that the page is not cacheable. 
-cacheHtml :: Response -> ContentTransformer Response 
+-- unless the context indicates that the page is not cacheable.
+cacheHtml :: Response -> ContentTransformer Response
 cacheHtml resp' = do
   params <- getParams
   file <- getFileName
@@ -283,7 +282,7 @@
 mimeResponse c mimeType =
   return . setContentType mimeType . toResponse $ c
 
--- | Converts Pandoc to response using format specified in parameters. 
+-- | Converts Pandoc to response using format specified in parameters.
 exportPandoc :: Pandoc -> ContentTransformer Response
 exportPandoc doc = do
   params <- getParams
@@ -403,7 +402,7 @@
   return result'
 
 -- | Applies all the page transform plugins to a Pandoc document.
-applyPageTransforms :: Pandoc -> ContentTransformer Pandoc 
+applyPageTransforms :: Pandoc -> ContentTransformer Pandoc
 applyPageTransforms c = do
   xforms <- getPageTransforms
   foldM applyTransform c (wikiLinksTransform : xforms)
@@ -488,7 +487,7 @@
 -- Pandoc and wiki content conversion support
 --
 
-readerFor :: PageType -> Bool -> (String -> Pandoc)
+readerFor :: PageType -> Bool -> String -> Pandoc
 readerFor pt lhs =
   let defPS = defaultParserState{ stateSmart = True
                                 , stateLiterateHaskell = lhs }
diff --git a/Network/Gitit/Export.hs b/Network/Gitit/Export.hs
--- a/Network/Gitit/Export.hs
+++ b/Network/Gitit/Export.hs
@@ -57,7 +57,7 @@
 respond mimetype ext fn page doc = liftIO (fn doc) >>=
   ok . setContentType mimetype .
   (if null ext then id else setFilename (page ++ "." ++ ext)) .
-  toResponseBS (B.empty)
+  toResponseBS B.empty
 
 respondX :: String -> String -> String
           -> (WriterOptions -> Pandoc -> IO L.ByteString)
@@ -225,7 +225,7 @@
               -- run pdflatex twice to get the references and toc right
               let cmd = "pdflatex"
               oldEnv <- getEnvironment
-              let env = Just $ ("TEXINPUTS",".:" ++ 
+              let env = Just $ ("TEXINPUTS",".:" ++
                                escapeStringUsing [(' ',"\\ "),('"',"\\\"")]
                                (curdir </> repositoryPath cfg) ++ ":") : oldEnv
               let opts = ["-interaction=batchmode", "-no-shell-escape", tempfile]
@@ -257,13 +257,13 @@
 fixURLs pndc = do
     curdir <- liftIO getCurrentDirectory
     cfg <- getConfig
-    
+
     let go (Image ils (url, title)) = Image ils (fixURL url, title)
         go x                        = x
-        
+
         fixURL ('/':url) = curdir </> staticDir cfg </> url
         fixURL url       = url
-    
+
     return $ bottomUp go pndc
 
 exportFormats :: Config -> [(String, String -> Pandoc -> Handler)]
diff --git a/Network/Gitit/Framework.hs b/Network/Gitit/Framework.hs
--- a/Network/Gitit/Framework.hs
+++ b/Network/Gitit/Framework.hs
@@ -18,11 +18,11 @@
 -}
 
 module Network.Gitit.Framework (
-                               -- * Combinators for dealing with users 
+                               -- * Combinators for dealing with users
                                  withUserFromSession
                                , withUserFromHTTPAuth
-                               , requireUserThat
-                               , requireUser
+                               , authenticateUserThat
+                               , authenticate
                                , getLoggedInUser
                                -- * Combinators to exclude certain actions
                                , unlessNoEdit
@@ -46,7 +46,6 @@
                                , isSourceCode
                                -- * Combinators that change the request locally
                                , withMessages
-                               , withInput
                                -- * Miscellaneous
                                , urlForPage
                                , pathForPage
@@ -61,11 +60,11 @@
 import Network.Gitit.Types
 import Data.FileStore
 import Data.Char (toLower)
-import Control.Monad (mzero, liftM, MonadPlus)
+import Control.Monad (mzero, liftM, unless, MonadPlus)
 import qualified Data.Map as M
 import Data.ByteString.UTF8 (toString)
 import Data.ByteString.Lazy.UTF8 (fromString)
-import Data.Maybe (fromJust)
+import Data.Maybe (fromJust, fromMaybe)
 import Data.List (intercalate, isPrefixOf, isInfixOf)
 import System.FilePath ((<.>), takeExtension, takeFileName)
 import Text.Highlighting.Kate
@@ -75,24 +74,27 @@
 import Happstack.Crypto.Base64 (decode)
 import Network.HTTP (urlEncodeVars)
 
--- | Run the handler if a user is logged in, otherwise redirect
+-- | Require a logged in user if the authentication level demands it.
+-- Run the handler if a user is logged in, otherwise redirect
 -- to login page.
-requireUser :: Handler -> Handler 
-requireUser = requireUserThat (const True)
+authenticate :: AuthenticationLevel -> Handler -> Handler
+authenticate = authenticateUserThat (const True)
 
--- | Run the handler if a user satisfying the predicate is logged in.
--- Redirect to login if nobody logged in; raise error if someone is
--- logged in but doesn't satisfy the predicate.
-requireUserThat :: (User -> Bool) -> Handler -> Handler
-requireUserThat predicate handler = do
-  mbUser <- getLoggedInUser
-  rq <- askRq
-  let url = rqUri rq ++ rqQuery rq
-  case mbUser of
-       Nothing   -> tempRedirect ("/_login?" ++ urlEncodeVars [("destination", url)]) $ toResponse ()
-       Just u    -> if predicate u
-                       then handler
-                       else error "Not authorized."
+-- | Like 'authenticate', but with a predicate that the user must satisfy.
+authenticateUserThat :: (User -> Bool) -> AuthenticationLevel -> Handler -> Handler
+authenticateUserThat predicate level handler = do
+  cfg <- getConfig
+  if level <= requireAuthentication cfg
+     then do
+       mbUser <- getLoggedInUser
+       rq <- askRq
+       let url = rqUri rq ++ rqQuery rq
+       case mbUser of
+            Nothing   -> tempRedirect ("/_login?" ++ urlEncodeVars [("destination", url)]) $ toResponse ()
+            Just u    -> if predicate u
+                            then handler
+                            else error "Not authorized."
+     else handler
 
 -- | Run the handler after setting @REMOTE_USER@ with the user from
 -- the session.
@@ -103,7 +105,7 @@
   mbUser <- case mbSd of
             Nothing    -> return Nothing
             Just sd    -> do
-              addCookie (sessionTimeout cfg) (mkCookie "sid" (show $ fromJust sk))  -- refresh timeout
+              addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show $ fromJust sk))  -- refresh timeout
               getUser $! sessionUser sd
   let user = maybe "" uUsername mbUser
   localRq (setHeader "REMOTE_USER" user) handler
@@ -113,7 +115,7 @@
 withUserFromHTTPAuth :: Handler -> Handler
 withUserFromHTTPAuth handler = do
   req <- askRq
-  let user = case (getHeader "authorization" req) of
+  let user = case getHeader "authorization" req of
               Nothing         -> ""
               Just authHeader -> case parse pAuthorizationHeader "" (toString authHeader) of
                                   Left _  -> ""
@@ -137,14 +139,14 @@
 
 pDigestHeader :: GenParser Char st String
 pDigestHeader = do
-  string "Digest username=\""
+  _ <- string "Digest username=\""
   result' <- many (noneOf "\"")
-  char '"'
+  _ <- char '"'
   return result'
 
 pBasicHeader :: GenParser Char st String
 pBasicHeader = do
-  string "Basic "
+  _ <- string "Basic "
   result' <- many (noneOf " \t\n")
   return $ takeWhile (/=':') $ decode result'
 
@@ -237,7 +239,7 @@
   let (next, rest) = break (==c) cs
   in case rest of
          []     -> [next]
-         (_:rs) -> next : splitOn c rs 
+         (_:rs) -> next : splitOn c rs
 
 -- | Returns path portion of URI, without initial @\/@.
 -- Consecutive spaces are collapsed.  We don't want to distinguish
@@ -271,8 +273,7 @@
 -- | Returns encoded URL path for the page with the given name, relative to
 -- the wiki base.
 urlForPage :: String -> String
-urlForPage page = "/" ++
-  encString False isUnescapedInURI page
+urlForPage page = '/' : encString False isUnescapedInURI page
 
 -- | Returns the filestore path of the file containing the page's source.
 pathForPage :: String -> FilePath
@@ -282,9 +283,8 @@
 getMimeTypeForExtension :: String -> GititServerPart String
 getMimeTypeForExtension ext = do
   mimes <- liftM mimeMap getConfig
-  return $ case M.lookup (dropWhile (=='.') $ map toLower ext) mimes of
-                Nothing -> "application/octet-stream"
-                Just t  -> t
+  return $ fromMaybe "application/octet-stream"
+    (M.lookup (dropWhile (== '.') $ map toLower ext) mimes)
 
 -- | Simple helper for validation of forms.
 validate :: [(Bool, String)]   -- ^ list of conditions and error messages
@@ -307,9 +307,8 @@
   base <- getWikiBase
   uri' <- liftM rqUri askRq
   let localpath = drop (length base) uri'
-  if length localpath > 1 && lastNote "guardIndex" uri' == '/'
-     then return ()
-     else mzero
+  unless (length localpath > 1 && lastNote "guardIndex" uri' == '/')
+    mzero
 
 -- Guard against a path like @\/wiki@ when the wiki is being
 -- served at @\/wiki@.
@@ -317,29 +316,24 @@
 guardBareBase = do
   base' <- getWikiBase
   uri' <- liftM rqUri askRq
-  if not (null base') && base' == uri'
-     then return ()
-     else mzero
+  unless (not (null base') && base' == uri')
+    mzero
 
 -- | Runs a server monad in a local context after setting
 -- the "messages" request header.
 withMessages :: ServerMonad m => [String] -> m a -> m a
-withMessages = withInput "messages" . show
-
--- | Runs a server monad in a local context after setting
--- request header.
-withInput :: ServerMonad m => String -> String -> m a -> m a
-withInput name val handler = do
+withMessages messages handler = do
   req <- askRq
-  let inps = filter (\(n,_) -> n /= name) $ rqInputs req
-  let newInp = (name, Input { inputValue = fromString val
+  let inps = filter (\(n,_) -> n /= "messages") $ rqInputsQuery req
+  let newInp = ("messages", Input {
+                              inputValue = Right $ fromString $ show messages
                             , inputFilename = Nothing
                             , inputContentType = ContentType {
                                     ctType = "text"
                                   , ctSubtype = "plain"
                                   , ctParameters = [] }
                             })
-  localRq (\rq -> rq{ rqInputs = newInp : inps }) handler
+  localRq (\rq -> rq{ rqInputsQuery = newInp : inps }) handler
 
 -- | Returns a filestore object derived from the
 -- repository path and filestore type specified in configuration.
diff --git a/Network/Gitit/Handlers.hs b/Network/Gitit/Handlers.hs
--- a/Network/Gitit/Handlers.hs
+++ b/Network/Gitit/Handlers.hs
@@ -48,7 +48,6 @@
                       , deletePage
                       , confirmDelete
                       , showHighlightedSource
-                      , currentUser
                       , expireCache
                       , feedHandler
                       )
@@ -65,7 +64,6 @@
         exportPage, showHighlightedSource, preview, applyPreCommitPlugins)
 import Network.Gitit.Page (extractCategories)
 import Control.Exception (throwIO, catch, try)
-import Data.ByteString.UTF8 (toString)
 import System.Time
 import System.FilePath
 import Prelude hiding (catch)
@@ -102,7 +100,7 @@
                                   (ok $ setContentType mimetype $
                                     (toResponse noHtml) {rsBody = contents})
                                     -- ugly hack
-                Left NotFound  -> anyRequest mzero
+                Left NotFound  -> mzero
                 Left e         -> error (show e)
 
 debugHandler :: Handler
@@ -186,13 +184,13 @@
 uploadFile :: Handler
 uploadFile = withData $ \(params :: Params) -> do
   let origPath = pFilename params
-  let fileContents = pFileContents params
+  let filePath = pFilePath params
   let wikiname = pWikiname params `orIfNull` takeFileName origPath
   let logMsg = pLogMsg params
   cfg <- getConfig
   mbUser <- getLoggedInUser
   (user, email) <- case mbUser of
-                        Nothing -> fail "User must be logged in to delete page."
+                        Nothing -> return ("Anonymous", "")
                         Just u  -> return (uUsername u, uEmail u)
   let overwrite = pOverwrite params
   fs <- getFileStore
@@ -214,17 +212,17 @@
                  , (not overwrite && exists, "A file named '" ++ wikiname ++
                     "' already exists in the repository: choose a new name " ++
                     "or check the box to overwrite the existing file.")
-                 , (B.length fileContents > fromIntegral (maxUploadSize cfg),
-                    "File exceeds maximum upload size.")
                  , (isPageFile wikiname,
                     "This file extension is reserved for wiki pages.")
                  ]
   if null errors
      then do
        expireCachedFile wikiname `mplus` return ()
+       fileContents <- liftIO $ B.readFile filePath
+       let len = B.length fileContents
        liftIO $ save fs wikiname (Author user email) logMsg fileContents
        let contents = thediv <<
-             [ h2 << ("Uploaded " ++ show (B.length fileContents) ++ " bytes")
+             [ h2 << ("Uploaded " ++ show len ++ " bytes")
              , if takeExtension wikiname `elem` imageExtensions
                   then p << "To add this image to a page, use:" +++
                        pre << ("![alt text](/" ++ wikiname ++ ")")
@@ -259,11 +257,11 @@
                                        Just m  -> seeOther (base' ++ urlForPage m) $
                                                   toResponse $ "Redirecting" ++
                                                     " to partial match"
-                                       Nothing -> withInput "patterns" gotopage searchResults
+                                       Nothing -> searchResults
 
 searchResults :: Handler
 searchResults = withData $ \(params :: Params) -> do
-  let patterns = pPatterns params
+  let patterns = pPatterns params `orIfNull` [pGotoPage params]
   fs <- getFileStore
   matchLines <- if null patterns
                    then return []
@@ -292,7 +290,7 @@
   let relevance (f, ms) = length ms + if f `elem` pageNameMatches
                                          then 100
                                          else 0
-  let preamble = if null patterns 
+  let preamble = if null patterns
                     then h3 << ["Please enter a search term."]
                     else h3 << [ stringToHtml (show (length matches) ++ " matches found for ")
                                , thespan ! [identifier "pattern"] << unwords patterns]
@@ -448,7 +446,7 @@
   from' <- case (from, to) of
               (Just _, _)        -> return from
               (Nothing, Nothing) -> return from
-              (Nothing, Just t)  -> do 
+              (Nothing, Just t)  -> do
                 pageHist <- liftIO $ history fs [file]
                                      (TimeRange Nothing Nothing)
                 let (_, upto) = break (\r -> idsMatch fs (revId r) t)
@@ -484,7 +482,10 @@
            pre ! [theclass "diff"] << map diffLineToHtml rawDiff
 
 editPage :: Handler
-editPage = withData $ \(params :: Params) -> do
+editPage = withData editPage'
+
+editPage' :: Params -> Handler
+editPage' params = do
   let rev = pRevision params  -- if this is set, we're doing a revert
   fs <- getFileStore
   page <- getPage
@@ -537,6 +538,11 @@
                               value "Preview" ]
                    , thediv ! [ identifier "previewpane" ] << noHtml
                    ]
+  let pgScripts' = ["preview.js"]
+  let pgScripts'' = case mathMethod cfg of
+       JsMathScript -> "jsMath/easy/load.js" : pgScripts'
+       MathML       -> "MathMLinHTML.js" : pgScripts'
+       _            -> pgScripts'
   formattedPage defaultPageLayout{
                   pgPageName = page,
                   pgMessages = messages,
@@ -545,7 +551,7 @@
                   pgShowSiteNav = False,
                   pgMarkupHelp = Just $ markupHelp cfg,
                   pgSelectedTab = EditTab,
-                  pgScripts = ["preview.js"],
+                  pgScripts = pgScripts'',
                   pgTitle = ("Editing " ++ page)
                   } editForm
 
@@ -585,12 +591,12 @@
   let file = pFileToDelete params
   mbUser <- getLoggedInUser
   (user, email) <- case mbUser of
-                        Nothing -> fail "User must be logged in to delete."
+                        Nothing -> return ("Anonymous", "")
                         Just u  -> return (uUsername u, uEmail u)
   let author = Author user email
   let descrip = "Deleted using web interface."
   base' <- getWikiBase
-  if pConfirm params && (file == page || file == page <.> "page") 
+  if pConfirm params && (file == page || file == page <.> "page")
      then do
        fs <- getFileStore
        liftIO $ delete fs file author descrip
@@ -603,7 +609,7 @@
   cfg <- getConfig
   mbUser <- getLoggedInUser
   (user, email) <- case mbUser of
-                        Nothing -> fail "User must be logged in to delete page."
+                        Nothing -> return ("Anonymous", "")
                         Just u  -> return (uUsername u, uEmail u)
   editedText <- case pEditedText params of
                      Nothing -> error "No body text in POST request"
@@ -639,9 +645,10 @@
                       if conflicts
                          then "Please resolve conflicts and Save."
                          else "Please review and Save."
-               withMessages [mergeMsg] $
-                 withInput "editedText" mergedText $
-                   withInput "sha1" (revId mergedWithRev) editPage
+               editPage' $
+                 params{ pEditedText = Just mergedText,
+                         pSHA1       = revId mergedWithRev,
+                         pMessages   = [mergeMsg] }
 
 indexPage :: Handler
 indexPage = do
@@ -733,12 +740,6 @@
                   pgTabs = [],
                   pgScripts = ["search.js"],
                   pgTitle = "Categories" } htmlMatches
-
--- | Returns username of logged in user or null string if nobody logged in.
-currentUser :: Handler
-currentUser = do
-  req <- askRq
-  ok $ toResponse $ maybe "" toString (getHeader "REMOTE_USER" req)
 
 expireCache :: Handler
 expireCache = do
diff --git a/Network/Gitit/Initialize.hs b/Network/Gitit/Initialize.hs
--- a/Network/Gitit/Initialize.hs
+++ b/Network/Gitit/Initialize.hs
@@ -172,7 +172,7 @@
       logM "gitit" WARNING $ "Created " ++ (cssdir </> f)
 
     {-
-    let icondir = staticdir </> "img" </> "icons" 
+    let icondir = staticdir </> "img" </> "icons"
     createDirectoryIfMissing True icondir
     iconDataDir <- getDataFileName $ "data" </> "static" </> "img" </> "icons"
     iconFiles <- liftM (filter (\f -> takeExtension f == ".png")) $ getDirectoryContents iconDataDir
diff --git a/Network/Gitit/Layout.hs b/Network/Gitit/Layout.hs
--- a/Network/Gitit/Layout.hs
+++ b/Network/Gitit/Layout.hs
@@ -71,11 +71,11 @@
 
 -- | Returns a page template with gitit variables filled in.
 filledPageTemplate :: String -> Config -> PageLayout -> Html ->
-                      T.StringTemplate String -> T.StringTemplate String 
-filledPageTemplate base' cfg layout htmlContents templ = 
+                      T.StringTemplate String -> T.StringTemplate String
+filledPageTemplate base' cfg layout htmlContents templ =
   let rev  = pgRevision layout
       page = pgPageName layout
-      scripts  = ["jquery.min.js", "jquery-ui.packed.js"] ++ pgScripts layout
+      scripts  = ["jquery.min.js", "jquery-ui.packed.js", "footnotes.js"] ++ pgScripts layout
       scriptLink x = script ! [src (base' ++ "/js/" ++ x),
         thetype "text/javascript"] << noHtml
       javascriptlinks = renderHtmlFragment $ concatHtml $ map scriptLink scripts
@@ -105,7 +105,7 @@
                    T.setAttribute "tabs" (renderHtmlFragment tabs) .
                    T.setAttribute "messages" (pgMessages layout) .
                    T.setAttribute "usecache" (useCache cfg) .
-                   T.setAttribute "content" (renderHtmlFragment htmlContents) . 
+                   T.setAttribute "content" (renderHtmlFragment htmlContents) .
                    setBoolAttr "wikiupload" ( uploadsAllowed cfg) $
                    templ
 
@@ -118,7 +118,7 @@
          value (fromJust rev)] | isJust rev ] ++
      [ select ! [name "format"] <<
          map ((\f -> option ! [value f] << f) . fst) (exportFormats cfg)
-     , primHtmlChar "nbsp" 
+     , primHtmlChar "nbsp"
      , submit "export" "Export" ])
 exportBox _ _ _ _ = noHtml
 
diff --git a/Network/Gitit/Page.hs b/Network/Gitit/Page.hs
--- a/Network/Gitit/Page.hs
+++ b/Network/Gitit/Page.hs
@@ -21,7 +21,7 @@
 -  which looks like this (it is valid YAML):
 -
 -  > ---
--  > title: Custom Title 
+-  > title: Custom Title
 -  > format: markdown+lhs
 -  > toc: yes
 -  > categories: foo bar baz
@@ -32,7 +32,7 @@
 -  text as markdown with literate haskell, to include a table of
 -  contents, and to include the page in the categories foo, bar,
 -  and baz.
-- 
+-
 -  The metadata block may be omitted entirely, and any particular line
 -  may be omitted. The categories in the @categories@ field should be
 -  separated by spaces. Commas will be treated as spaces.
@@ -52,7 +52,7 @@
 import Network.Gitit.Util (trim, splitCategories, parsePageType)
 import Text.ParserCombinators.Parsec
 import Data.Char (toLower)
-import Data.List (intercalate)
+import Data.List (isPrefixOf)
 import Data.Maybe (fromMaybe)
 
 parseMetadata :: String -> ([(String, String)], String)
@@ -63,11 +63,11 @@
 
 pMetadataBlock :: GenParser Char st ([(String, String)], String)
 pMetadataBlock = try $ do
-  string "---"
-  pBlankline
+  _ <- string "---"
+  _ <- pBlankline
   ls <- many pMetadataLine
-  string "..."
-  pBlankline
+  _ <- string "..."
+  _ <- pBlankline
   skipMany pBlankline
   rest <- getInput
   return (ls, rest)
@@ -79,11 +79,11 @@
 pMetadataLine = try $ do
   ident <- many1 letter
   skipMany (oneOf " \t")
-  char ':'
+  _ <- char ':'
   rawval <- many $ noneOf "\n\r"
                  <|> (try $ newline >> notFollowedBy pBlankline >>
                             skipMany1 (oneOf " \t") >> return ' ')
-  newline
+  _ <- newline
   return (ident, trim rawval)
 
 -- | Read a string (the contents of a page file) and produce a Page
@@ -91,7 +91,7 @@
 stringToPage :: Config -> String -> String -> Page
 stringToPage conf pagename raw =
   let (ls, rest) = parseMetadata raw
-      page' = Page { pageName        = pagename 
+      page' = Page { pageName        = pagename
                    , pageFormat      = defaultPageType conf
                    , pageLHS         = defaultLHS conf
                    , pageTOC         = tableOfContents conf
@@ -106,7 +106,7 @@
 adjustPage ("format", val) page' = page' { pageFormat = pt, pageLHS = lhs }
     where (pt, lhs) = parsePageType val
 adjustPage ("toc", val) page' = page' {
-  pageTOC = (map toLower val) `elem` ["yes","true"] }
+  pageTOC = map toLower val `elem` ["yes","true"] }
 adjustPage ("categories", val) page' =
    page' { pageCategories = splitCategories val ++ pageCategories page' }
 adjustPage (_, _) page' = page'
@@ -126,7 +126,7 @@
                        else "") ++
                    (if pageformat /= defaultPageType conf ||
                           pagelhs /= defaultLHS conf
-                       then "!format: " ++ 
+                       then "!format: " ++
                             map toLower (show pageformat) ++
                             if pagelhs then "+lhs\n" else "\n"
                        else "") ++
@@ -135,12 +135,12 @@
                             (if pagetoc then "yes" else "no") ++ "\n"
                        else "") ++
                    (if not (null pagecats)
-                       then "!categories: " ++ intercalate " " pagecats ++ "\n"
+                       then "!categories: " ++ unwords pagecats ++ "\n"
                        else "")
   in  metadata' ++ (if null metadata' then "" else "\n") ++ pageText page'
 
 extractCategories :: String -> [String]
-extractCategories s | take 3 s == "---" = 
+extractCategories s | "---" `isPrefixOf` s =
   let (md,_) = parseMetadata s
   in  splitCategories $ fromMaybe "" $ lookup "categories" md
 extractCategories _ = []
diff --git a/Network/Gitit/Rpxnow.hs b/Network/Gitit/Rpxnow.hs
new file mode 100644
--- /dev/null
+++ b/Network/Gitit/Rpxnow.hs
@@ -0,0 +1,81 @@
+-- Modified from Michael Snoyman's BSD3 authenticate-0.0.1
+-- and http-wget-0.0.1.
+-- Facilitates authentication with "http://rpxnow.com/".
+
+module Network.Gitit.Rpxnow
+    ( Identifier (..)
+    , authenticate
+    ) where
+
+import Text.JSON
+import Data.Maybe (isJust, fromJust)
+import System.Process
+import System.Exit
+import System.IO
+import Network.HTTP (urlEncodeVars)
+
+-- | Make a post request with parameters to the URL and return a response.
+curl :: Monad m
+     => String             -- ^ URL
+     -> [(String, String)] -- ^ Post parameters
+     -> IO (m String)      -- ^ Response body
+curl url params = do
+    (Nothing, Just hout, Just herr, phandle) <- createProcess $ (proc "curl"
+        [url, "-d", urlEncodeVars params]
+        ) { std_out = CreatePipe, std_err = CreatePipe }
+    exitCode <- waitForProcess phandle
+    case exitCode of
+        ExitSuccess -> hGetContents hout >>= return . return
+        _           -> hGetContents herr >>= return . fail
+
+
+
+-- | Information received from Rpxnow after a valid login.
+data Identifier = Identifier
+    { userIdentifier  :: String
+    , userData        :: [(String, String)]
+    }
+    deriving Show
+
+-- | Attempt to log a user in.
+authenticate :: Monad m
+             => String -- ^ API key given by RPXNOW.
+             -> String -- ^ Token passed by client.
+             -> IO (m Identifier)
+authenticate apiKey token = do
+    body <- curl
+                "https://rpxnow.com/api/v2/auth_info"
+                [ ("apiKey", apiKey)
+                , ("token", token)
+                ]
+    case body of
+        Left s -> return $ fail $ "Unable to connect to rpxnow: " ++ s
+        Right b ->
+          case decode b >>= getObject of
+            Error s -> return $ fail $ "Not a valid JSON response: " ++ s
+            Ok o ->
+              case valFromObj "stat" o of
+                Error _ -> return $ fail "Missing 'stat' field"
+                Ok "ok" -> return $ parseProfile o
+                Ok stat -> return $ fail $ "Login not accepted: " ++ stat
+
+parseProfile :: Monad m => JSObject JSValue -> m Identifier
+parseProfile v = do
+    profile <- resultToMonad $ valFromObj "profile" v >>= getObject
+    ident <- resultToMonad $ valFromObj "identifier" profile
+    let pairs = fromJSObject profile
+        pairs' = filter (\(k, _) -> k /= "identifier") pairs
+        pairs'' = map fromJust . filter isJust . map takeString $ pairs'
+    return $ Identifier ident pairs''
+
+takeString :: (String, JSValue) -> Maybe (String, String)
+takeString (k, JSString v) = Just (k, fromJSString v)
+takeString _ = Nothing
+
+getObject :: Monad m => JSValue -> m (JSObject JSValue)
+getObject (JSObject o) = return o
+getObject _ = fail "Not an object"
+
+resultToMonad :: Monad m => Result a -> m a
+resultToMonad (Ok x) = return x
+resultToMonad (Error s) = fail s
diff --git a/Network/Gitit/Server.hs b/Network/Gitit/Server.hs
--- a/Network/Gitit/Server.hs
+++ b/Network/Gitit/Server.hs
@@ -35,10 +35,10 @@
           )
 where
 import Happstack.Server
-import Happstack.Server.Parts (compressedResponseFilter)
+import Happstack.Server.Compression (compressedResponseFilter)
 import Network.Socket (getAddrInfo, defaultHints, addrAddress)
 import Control.Monad.Reader
-import Data.ByteString.UTF8 as U
+import Data.ByteString.UTF8 as U hiding (lines)
 
 withExpiresHeaders :: ServerMonad m => m Response -> m Response
 withExpiresHeaders = liftM (setHeader "Cache-Control" "max-age=21600")
@@ -57,7 +57,7 @@
   if null addrs
      then return Nothing
      else return $ Just $ takeWhile (/=':') $ show $ addrAddress $ case addrs of -- head addrs
-                                                                     [] -> error $ "lookupIPAddr, no addrs"
+                                                                     [] -> error "lookupIPAddr, no addrs"
                                                                      (x:_) -> x
 getHost :: ServerMonad m => m (Maybe String)
 getHost = liftM (maybe Nothing (Just . U.toString)) $ getHeaderM "Host"
diff --git a/Network/Gitit/Types.hs b/Network/Gitit/Types.hs
--- a/Network/Gitit/Types.hs
+++ b/Network/Gitit/Types.hs
@@ -29,13 +29,10 @@
 import System.Log.Logger (Priority(..))
 import Text.Pandoc.Definition (Pandoc)
 import Text.XHtml (Html)
-import qualified Data.ByteString.Lazy.UTF8 as L (ByteString)
-import qualified Data.ByteString.Lazy as L (empty)
 import qualified Data.Map as M
 import Data.List (intersect)
 import Data.Time (parseTime)
 import System.Locale (defaultTimeLocale)
-import Data.Maybe (fromMaybe)
 import Data.FileStore.Types
 import Network.Gitit.Server
 import Text.Pandoc.CharacterReferences (decodeCharacterReferences)
@@ -48,6 +45,9 @@
 data MathMethod = MathML | JsMathScript | WebTeX String | RawTeX
                   deriving (Read, Show, Eq)
 
+data AuthenticationLevel = Never | ForModify | ForRead
+                  deriving (Read, Show, Eq, Ord)
+
 -- | Data structure for information read from config file.
 data Config = Config {
   -- | Path of repository containing filestore
@@ -65,6 +65,8 @@
   -- | Combinator to set @REMOTE_USER@ request header
   withUser             :: Handler -> Handler,
   -- | Handler for login, logout, register, etc.
+  requireAuthentication :: AuthenticationLevel,
+  -- | Specifies which actions require authentication.
   authHandler          :: Handler,
   -- | Path of users database
   userFile             :: FilePath,
@@ -108,6 +110,9 @@
   useRecaptcha         :: Bool,
   recaptchaPublicKey   :: String,
   recaptchaPrivateKey  :: String,
+  -- | RPX domain and key
+  rpxDomain            :: String,
+  rpxKey               :: String,
   -- | Should responses be compressed?
   compressResponses    :: Bool,
   -- | Should responses be cached?
@@ -221,7 +226,7 @@
   { pgPageName       :: String
   , pgRevision       :: Maybe String
   , pgPrintable      :: Bool
-  , pgMessages       :: [String] 
+  , pgMessages       :: [String]
   , pgTitle          :: String
   , pgScripts        :: [String]
   , pgShowPageTools  :: Bool
@@ -273,8 +278,8 @@
                      , pPrintable    :: Bool
                      , pOverwrite    :: Bool
                      , pFilename     :: String
-                     , pFileContents :: L.ByteString
-                     , pConfirm      :: Bool 
+                     , pFilePath     :: FilePath
+                     , pConfirm      :: Bool
                      , pSessionKey   :: Maybe SessionKey
                      , pRecaptcha    :: Recaptcha
                      , pResetCode    :: String
@@ -298,7 +303,7 @@
          pa <- look' "patterns"       `mplus` return ""
          gt <- look' "gotopage"       `mplus` return ""
          ft <- look' "filetodelete"   `mplus` return ""
-         me <- lookRead "messages"   `mplus` return [] 
+         me <- lookRead "messages"   `mplus` return []
          fm <- liftM Just (look' "from") `mplus` return Nothing
          to <- liftM Just (look' "to")   `mplus` return Nothing
          et <- liftM (Just . filter (/='\r')) (look' "editedText")
@@ -311,9 +316,10 @@
          wn <- look' "wikiname"       `mplus` return ""
          pr <- (look' "printable" >> return True) `mplus` return False
          ow <- liftM (=="yes") (look' "overwrite") `mplus` return False
-         fn <- liftM (fromMaybe "" . inputFilename) (lookInput "file")
-                 `mplus` return ""
-         fc <- liftM inputValue (lookInput "file") `mplus` return L.empty
+         fileparams <- liftM Just (lookFile "file") `mplus` return Nothing
+         let (fp, fn) = case fileparams of
+                             Just (x,y,_) -> (x,y)
+                             Nothing      -> ("","")
          ac <- look' "accessCode"     `mplus` return ""
          cn <- (look' "confirm" >> return True) `mplus` return False
          sk <- liftM Just (readCookieValue "sid") `mplus` return Nothing
@@ -336,16 +342,16 @@
                          , pFrom         = fm
                          , pTo           = to
                          , pEditedText   = et
-                         , pFormat       = fo 
+                         , pFormat       = fo
                          , pSHA1         = sh
                          , pLogMsg       = lm
                          , pEmail        = em
-                         , pFullName     = na 
+                         , pFullName     = na
                          , pWikiname     = wn
-                         , pPrintable    = pr 
+                         , pPrintable    = pr
                          , pOverwrite    = ow
                          , pFilename     = fn
-                         , pFileContents = fc
+                         , pFilePath     = fp
                          , pAccessCode   = ac
                          , pConfirm      = cn
                          , pSessionKey   = sk
@@ -366,7 +372,7 @@
                where commandList = ["update", "cancel", "export"]
 
 -- | State for a single wiki.
-data WikiState = WikiState { 
+data WikiState = WikiState {
                      wikiConfig    :: Config
                    , wikiFileStore :: FileStore
                    }
diff --git a/Network/Gitit/Util.hs b/Network/Gitit/Util.hs
--- a/Network/Gitit/Util.hs
+++ b/Network/Gitit/Util.hs
@@ -49,11 +49,11 @@
   setCurrentDirectory d
   result <- action
   setCurrentDirectory w
-  return result 
+  return result
 
 -- | Perform a function in a temporary directory and clean up.
 withTempDir :: FilePath -> (FilePath -> IO a) -> IO a
-withTempDir baseName = bracket (createTempDir 0 baseName) (removeDirectoryRecursive)
+withTempDir baseName = bracket (createTempDir 0 baseName) removeDirectoryRecursive
 
 -- | Create a temporary directory with a unique name.
 createTempDir :: Integer -> FilePath -> IO FilePath
diff --git a/data/default.conf b/data/default.conf
--- a/data/default.conf
+++ b/data/default.conf
@@ -14,6 +14,12 @@
 # specifies the path of the repository directory.  If it does not
 # exist, gitit will create it on startup.
 
+require-authentication: modify
+# if 'none', login is never required, and pages can be edited anonymously.
+# if 'modify', login is required to modify the wiki (edit, add, delete
+# pages, upload files).
+# if 'read', login is required to see any wiki pages.
+
 authentication-method: form
 # 'form' means that users will be logged in and registered
 # using forms in the gitit web interface.  'http' means
@@ -24,7 +30,10 @@
 # suppressed).  'generic' means that gitit will assume that
 # some form of authentication is in place that directly
 # sets REMOTE_USER to the name of the authenticated user
-# (e.g. mod_auth_cas on apache).
+# (e.g. mod_auth_cas on apache).  'rpx' means that gitit
+# will attempt to log in through https://rpxnow.com.
+# This requires that 'rpx-domain', 'rpx-key', and 'base-url'
+# be set below, and that 'curl' be in the system path.
 
 user-file: gitit-users
 # specifies the path of the file containing user login information.
@@ -170,6 +179,12 @@
 # access-question:  What is the code given to you by Ms. X?
 # access-question-answers:  RED DOG, red dog
 
+rpx-domain:
+rpx-key:
+# Specifies the domain and key of your RPX account.  The domain is
+# just the prefix of the complete RPX domain, so if your full domain
+# is 'https://foo.rpxnow.com/', use 'foo' as the value of rpx-domain.
+
 mail-command: sendmail %s
 # specifies the command to use to send notification emails.
 # '%s' will be replaced by the destination email address.
@@ -209,11 +224,9 @@
 # individual pages)
 
 base-url:
-# the base URL of the wiki, to be used in constructing feed IDs.
-# If this field is left blank, gitit will get the base URL from the
-# request header 'Host'.  For most users, this should be fine, but
-# if you are proxying a gitit instance to a subdirectory URL, you will
-# want to set this manually.
+# the base URL of the wiki, to be used in constructing feed IDs
+# and RPX token_urls.  Set this if use-feed is 'yes' or
+# authentication-method is 'rpx'.
 
 feed-days: 14
 # number of days to be included in feeds.
diff --git a/data/static/css/screen.css b/data/static/css/screen.css
--- a/data/static/css/screen.css
+++ b/data/static/css/screen.css
@@ -1,6 +1,6 @@
 @import url("reset-fonts-grids.css");
 
-html { background: #f9f9f9; color: black; } 
+html { background: #f9f9f9; color: black; }
 body { margin: 10px; }
 
 fieldset { border: 1px solid #ccc; padding: 1em; }
@@ -44,6 +44,7 @@
 caption { margin-bottom: .5em; text-align: center; }
 sup { vertical-align: super; }
 sub { vertical-align: sub; }
+sub, sup { line-height: 0.3em; }
 p, fieldset, table, pre { margin-bottom: 1em; }
 button, input[type="checkbox"], input[type="radio"], input[type="reset"], input[type="submit"] { padding: 1px; }
 
@@ -138,3 +139,17 @@
 div.markupHelp pre { font-size: 77%; overflow: auto; }
 
 .login { display: none; }
+
+/* Style the footnotes; from gwern.net */
+#footnotediv div {
+background-color: white;
+padding: 3px;
+padding: 12px;
+max-width: 800px;
+border: 1px solid #CDBBB5;
+box-shadow: #555 0 0 10px;
+-webkit-box-shadow: #555 0 0 10px;
+-moz-box-shadow: 0 0 10px #555;
+}
+/* Deal with multiple footnotes one after another; Charuru */
+sup + sup { margin-left: 2px; }
diff --git a/data/static/js/preview.js b/data/static/js/preview.js
--- a/data/static/js/preview.js
+++ b/data/static/js/preview.js
@@ -6,10 +6,15 @@
       {"raw" : $("#editedText").attr("value")},
       function(data) {
         $('#previewpane').html(data);
+        // Process any mathematics if we're using MathML
         if (typeof(convert) == 'function') { convert(); }
+        // Process any mathematics if we're using jsMath
+        if (typeof(jsMath) == 'object')    { jsMath.ProcessBeforeShowing(); }
       },
       "html");
+
     $('#previewpane').fadeIn(1000);
+
 };
 $(document).ready(function(){
     $("#previewButton").show();
diff --git a/data/static/js/uploadForm.js b/data/static/js/uploadForm.js
--- a/data/static/js/uploadForm.js
+++ b/data/static/js/uploadForm.js
@@ -1,3 +1,6 @@
 $(document).ready(function(){
-    $("#file").change(function () { $("#wikiname").val($(this).val()); });
+    $("#file").change(function () {
+      var fn = $(this).val().replace(/.*\\/,"");
+      $("#wikiname").val(fn);
+    });
   });
diff --git a/expireGititCache.hs b/expireGititCache.hs
--- a/expireGititCache.hs
+++ b/expireGititCache.hs
@@ -43,7 +43,7 @@
              Nothing -> do
                hPutStrLn stderr ("Could not parse URI " ++ uriString)
                exitWith (ExitFailure 5)
-  forM_ files (expireFile uri)  
+  forM_ files (expireFile uri)
 
 usageMessage :: IO ()
 usageMessage = do
diff --git a/gitit.cabal b/gitit.cabal
--- a/gitit.cabal
+++ b/gitit.cabal
@@ -1,5 +1,5 @@
 name:                gitit
-version:             0.7.3.12
+version:             0.8
 Cabal-version:       >= 1.2
 build-type:          Simple
 synopsis:            Wiki using happstack, git or darcs, and pandoc.
@@ -41,7 +41,7 @@
 maintainer:          jgm@berkeley.edu
 bug-reports:         http://code.google.com/p/gitit/issues/list
 homepage:            http://github.com/jgm/gitit/tree/master
-stability:           experimental 
+stability:           experimental
 data-files:          data/static/css/screen.css, data/static/css/print.css,
                      data/static/css/ie.css, data/static/css/hk-pyg.css,
                      data/static/css/reset-fonts-grids.css,
@@ -91,11 +91,12 @@
   exposed-modules:   Network.Gitit, Network.Gitit.ContentTransformer,
                      Network.Gitit.Types, Network.Gitit.Framework,
                      Network.Gitit.Initialize, Network.Gitit.Config,
-                     Network.Gitit.Layout
+                     Network.Gitit.Layout, Network.Gitit.Authentication
   other-modules:     Network.Gitit.Cache, Network.Gitit.State,
                      Paths_gitit, Network.Gitit.Server, Network.Gitit.Export,
-                     Network.Gitit.Util, Network.Gitit.Handlers, Network.Gitit.Plugins,
-                     Network.Gitit.Authentication, Network.Gitit.Page, Network.Gitit.Feed
+                     Network.Gitit.Util, Network.Gitit.Handlers,
+                     Network.Gitit.Plugins, Network.Gitit.Rpxnow,
+                     Network.Gitit.Page, Network.Gitit.Feed
   if flag(plugins)
     exposed-modules: Network.Gitit.Interface
     build-depends:   ghc, ghc-paths
@@ -138,13 +139,14 @@
                      filestore >= 0.4.0.2 && < 0.5,
                      zlib >= 0.5 && < 0.6,
                      url >= 2.1 && < 2.2,
-                     happstack-server >= 0.5 && < 0.6,
-                     happstack-util >= 0.5 && < 0.6,
+                     happstack-server >= 6.0 && < 6.1,
+                     happstack-util >= 6.0 && < 6.1,
                      xml >= 1.3.5,
                      hslogger >= 1 && < 1.2,
                      ConfigFile >= 1 && < 1.1,
                      feed >= 0.3.6 && < 0.4,
-                     xss-sanitize >= 0.2 && < 0.3
+                     xss-sanitize >= 0.2 && < 0.3,
+                     json >= 0.4 && < 0.5
   if impl(ghc >= 6.10)
     build-depends:   base >= 4, syb
   if flag(plugins)
diff --git a/gitit.hs b/gitit.hs
--- a/gitit.hs
+++ b/gitit.hs
@@ -32,6 +32,8 @@
 import System.Exit
 import System.IO (stderr)
 import System.Console.GetOpt
+import Network.Socket hiding (Debug)
+import Network.URI
 import Data.Version (showVersion)
 import qualified Data.ByteString as B
 import Data.ByteString.UTF8 (fromString)
@@ -74,10 +76,18 @@
   -- initialize state
   initializeGititState conf'
 
-  let serverConf = Conf { validator = Nothing, port = portNumber conf' }
+  let serverConf = Conf { validator = Nothing, port = portNumber conf',
+                          timeout = 20, logAccess = Nothing }
 
+  -- open the requested interface
+  sock <- socket AF_INET Stream defaultProtocol
+  setSocketOption sock ReuseAddr 1
+  device <- inet_addr (getListenOrDefault opts)
+  bindSocket sock (SockAddrInet (toEnum (portNumber conf')) device)
+  listen sock 10
+
   -- start the server
-  simpleHTTP serverConf $ msum [ wiki conf'
+  simpleHTTPWithSocket sock serverConf $ msum [ wiki conf'
                                , dir "_reloadTemplates" reloadTemplates
                                ]
 
@@ -85,6 +95,7 @@
     = Help
     | ConfigFile FilePath
     | Port Int
+	| Listen String
     | Debug
     | Version
     | PrintDefaultConfig
@@ -98,6 +109,8 @@
         "Print version information"
    , Option ['p'] ["port"] (ReqArg (Port . read) "PORT")
         "Specify port"
+   , Option ['l'] ["listen"] (ReqArg (Listen . checkListen) "INTERFACE")
+        "Specify interface to listen on"
    , Option [] ["print-default-config"] (NoArg PrintDefaultConfig)
         "Print default configuration"
    , Option [] ["debug"] (NoArg Debug)
@@ -106,6 +119,16 @@
         "Specify configuration file"
    ]
 
+checkListen :: String -> String
+checkListen l | isIPv6address l = l
+              | isIPv4address l = l
+			  | otherwise         = error "Gitit.checkListen: Not a valid interface name"
+
+getListenOrDefault :: [Opt] -> String
+getListenOrDefault [] = "127.0.0.1"
+getListenOrDefault ((Listen l):_) = l
+getListenOrDefault (_:os) = getListenOrDefault os
+
 parseArgs :: [String] -> IO [Opt]
 parseArgs argv = do
   progname <- getProgName
@@ -139,6 +162,7 @@
     Debug              -> return conf{ debugMode = True }
     Port p             -> return conf{ portNumber = p }
     ConfigFile fname   -> getConfigFromFile fname
+    Listen _           -> return conf
 
 putErr :: ExitCode -> String -> IO a
 putErr c s = B.hPutStrLn stderr (fromString s) >> exitWith c
diff --git a/plugins/Interwiki.hs b/plugins/Interwiki.hs
--- a/plugins/Interwiki.hs
+++ b/plugins/Interwiki.hs
@@ -61,106 +61,84 @@
                       ("Hoogle", "http://www.haskell.org/hoogle/?hoogle=")]
 
 -- This mapping is derived from <https://secure.wikimedia.org/wikipedia/meta/wiki/Interwiki_map>
--- as of 3:16 PM, 7 February 2010.
+-- as of 11:30 AM, 6 February 2011.
 wpInterwikiMap = [ ("AbbeNormal", "http://ourpla.net/cgi/pikie?"),
+                 ("AIWiki", "http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?"),
                  ("Acronym", "http://www.acronymfinder.com/af-query.asp?String=exact&Acronym="),
                  ("Advisory", "http://advisory.wikimedia.org/wiki/"),
                  ("Advogato", "http://www.advogato.org/"),
                  ("Aew", "http://wiki.arabeyes.org/"),
                  ("Airwarfare", "http://airwarfare.com/mediawiki-1.4.5/index.php?"),
-                 ("AIWiki", "http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?"),
                  ("AllWiki", "http://allwiki.com/index.php/"),
                  ("Appropedia", "http://www.appropedia.org/"),
                  ("AquariumWiki", "http://www.theaquariumwiki.com/"),
-                 ("arXiv", "http://arxiv.org/abs/"),
                  ("AspieNetWiki", "http://aspie.mela.de/index.php/"),
                  ("AtmWiki", "http://www.otterstedt.de/wiki/index.php/"),
-                 ("BattlestarWiki", "http://en.battlestarwiki.org/wiki/"),
                  ("BEMI", "http://bemi.free.fr/vikio/index.php?"),
+                 ("BLW", "http://britainloveswikipedia.org/wiki/"),
+                 ("BattlestarWiki", "http://en.battlestarwiki.org/wiki/"),
                  ("BenefitsWiki", "http://www.benefitslink.com/cgi-bin/wiki.cgi?"),
-                 ("betawiki", "http://translatewiki.net/wiki/"),
                  ("BibleWiki", "http://bible.tmtm.com/wiki/"),
                  ("BluWiki", "http://www.bluwiki.org/go/"),
-                 ("BLW", "http://britainloveswikipedia.org/wiki/"),
                  ("Botwiki", "http://botwiki.sno.cc/wiki/"),
                  ("Boxrec", "http://www.boxrec.com/media/index.php?"),
-                 ("BrickWiki", "http://brickwiki.org/index.php?title="),
-                 ("BridgesWiki", "http://c2.com:8000/"),
-                 ("bugzilla", "https://bugzilla.wikimedia.org/show_bug.cgi?id="),
-                 ("bulba", "http://bulbapedia.bulbagarden.net/wiki/"),
-                 ("buzztard", "http://buzztard.org/index.php/"),
+                 ("BrickWiki", "http://lego.wikia.com/index.php?title="),
                  ("Bytesmiths", "http://www.Bytesmiths.com/wiki/"),
                  ("C2", "http://c2.com/cgi/wiki?"),
                  ("C2find", "http://c2.com/cgi/wiki?FindPage&value="),
+                 ("CANWiki", "http://www.can-wiki.info/"),
+                 ("CKWiss", "http://ck-wissen.de/ckwiki/index.php?title="),
+                 ("CNDbName", "http://cndb.com/actor.html?name="),
+                 ("CNDbTitle", "http://cndb.com/movie.html?title="),
                  ("Cache", "http://www.google.com/search?q=cache:"),
                  ("CanyonWiki", "http://www.canyonwiki.com/wiki/index.php/"),
-                 ("CANWiki", "http://www.can-wiki.info/"),
-                 ("ĈEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),
                  ("CellWiki", "http://cell.wikia.com/wiki/"),
-                 ("CentralWikia", "http://www.wikia.com/wiki/"),
+                 ("CentralWikia", "http://community.wikia.com/wiki/"),
                  ("ChEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),
                  ("ChoralWiki", "http://www.cpdl.org/wiki/index.php/"),
-                 ("Ciscavate", "http://ciscavate.org/index.php/"),
                  ("Citizendium", "http://en.citizendium.org/wiki/"),
-                 ("CKWiss", "http://ck-wissen.de/ckwiki/index.php?title="),
-                 ("CNDbName", "http://cndb.com/actor.html?name="),
-                 ("CNDbTitle", "http://cndb.com/movie.html?title="),
-                 ("CoLab", "http://colab.info"),
                  ("Comixpedia", "http://www.comixpedia.org/index.php/"),
-                 ("comcom", "http://comcom.wikimedia.org/wiki/"),
                  ("Commons", "http://commons.wikimedia.org/wiki/"),
                  ("CommunityScheme", "http://community.schemewiki.org/?c=s&key="),
-                 ("comune", "http://rete.comuni-italiani.it/wiki/"),
-                 ("Consciousness", "http://teadvus.inspiral.org/index.php/"),
                  ("CorpKnowPedia", "http://corpknowpedia.org/wiki/index.php/"),
                  ("CrazyHacks", "http://www.crazy-hacks.org/wiki/index.php?title="),
-                 ("Creative Commons", "http://www.creativecommons.org/licenses/"),
+                 ("CreativeCommons", "http://www.creativecommons.org/licenses/"),
+                 ("CreativeCommonsWiki", "http://wiki.creativecommons.org/"),
                  ("CreaturesWiki", "http://creatures.wikia.com/wiki/"),
                  ("CxEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),
-                 ("DAwiki", "http://www.dienstag-abend.de/wiki/index.php/"),
-                 ("Dcc", "http://www.dccwiki.com/"),
-                 ("DCDatabase", "http://www.dcdatabaseproject.com/wiki/"),
+                 ("DCDatabase", "http://dc.wikia.com/"),
                  ("DCMA", "http://www.christian-morgenstern.de/dcma/"),
+                 ("DOI", "http://dx.doi.org/"),
+                 ("DRAE", "http://buscon.rae.es/draeI/SrvltGUIBusUsual?LEMA="),
+                 ("DWJWiki", "http://www.suberic.net/cgi-bin/dwj/wiki.cgi?"),
+                 ("Dcc", "http://www.dccwiki.com/"),
                  ("DejaNews", "http://www.deja.com/=dnc/getdoc.xp?AN="),
-                 ("Delicious", "http://del.icio.us/tag/"),
+                 ("Delicious", "http://www.delicious.com/tag/"),
                  ("Demokraatia", "http://wiki.demokraatia.ee/index.php/"),
                  ("Devmo", "http://developer.mozilla.org/en/docs/"),
-                 ("Dictionary", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),
                  ("Dict", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),
+                 ("Dictionary", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),
                  ("Disinfopedia", "http://www.sourcewatch.org/wiki.phtml?title="),
-                 ("distributedproofreaders", "http://www.pgdp.net/wiki/"),
-                 ("distributedproofreadersca", "http://www.pgdpcanada.net/wiki/index.php/"),
-                 ("dmoz", "http://www.dmoz.org/"),
-                 ("dmozs", "http://www.dmoz.org/cgi-bin/search?search="),
                  ("DocBook", "http://wiki.docbook.org/topic/"),
-                 ("DOI", "http://dx.doi.org/"),
-                 ("doom_wiki", "http://doom.wikia.com/wiki/"),
-                 ("download", "http://download.wikimedia.org/"),
-                 ("dbdump", "http://download.wikimedia.org//latest/"),
-                 ("DRAE", "http://buscon.rae.es/draeI/SrvltGUIBusUsual?LEMA="),
                  ("Dreamhost", "http://wiki.dreamhost.com/index.php/"),
                  ("DrumCorpsWiki", "http://www.drumcorpswiki.com/index.php/"),
-                 ("DWJWiki", "http://www.suberic.net/cgi-bin/dwj/wiki.cgi?"),
-                 ("EĉeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),
+                 ("ELibre", "http://enciclopedia.us.es/index.php/"),
                  ("EcheI", "http://www.ikso.net/cgi-bin/wiki.pl?"),
                  ("EcoReality", "http://www.EcoReality.org/wiki/"),
                  ("EcxeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),
-                 ("EfnetCeeWiki", "http://purl.net/wiki/c/"),
-                 ("EfnetCppWiki", "http://purl.net/wiki/cpp/"),
-                 ("EfnetPythonWiki", "http://purl.net/wiki/python/"),
-                 ("EfnetXmlWiki", "http://purl.net/wiki/xml/"),
-                 ("ELibre", "http://enciclopedia.us.es/index.php/"),
                  ("EmacsWiki", "http://www.emacswiki.org/cgi-bin/wiki.pl?"),
-                 ("EnergieWiki", "http://www.netzwerk-energieberater.de/wiki/index.php/"),
                  ("Encyc", "http://encyc.org/wiki/"),
+                 ("EnergieWiki", "http://www.netzwerk-energieberater.de/wiki/index.php/"),
                  ("EoKulturCentro", "http://esperanto.toulouse.free.fr/nova/wikini/wakka.php?wiki="),
                  ("Ethnologue", "http://www.ethnologue.com/show_language.asp?code="),
                  ("EvoWiki", "http://wiki.cotch.net/index.php/"),
                  ("Exotica", "http://www.exotica.org.uk/wiki/"),
+                 ("EĉeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),
                  ("FanimutationWiki", "http://wiki.animutationportal.com/index.php/"),
                  ("FinalEmpire", "http://final-empire.sourceforge.net/cgi-bin/wiki.pl?"),
                  ("FinalFantasy", "http://finalfantasy.wikia.com/wiki/"),
                  ("Finnix", "http://www.finnix.org/"),
+                 ("FlickrPhoto", "http://www.flickr.com/photo.gne?id="),
                  ("FlickrUser", "http://www.flickr.com/people/"),
                  ("FloralWIKI", "http://www.floralwiki.co.uk/wiki/"),
                  ("FlyerWiki-de", "http://de.flyerwiki.net/index.php/"),
@@ -168,16 +146,17 @@
                  ("ForthFreak", "http://wiki.forthfreak.net/index.cgi?"),
                  ("Foundation", "http://wikimediafoundation.org/wiki/"),
                  ("FoxWiki", "http://fox.wikis.com/wc.dll?Wiki~"),
-                 ("FreeBio", "http://freebiology.org/wiki/"),
                  ("FreeBSDman", "http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query="),
+                 ("FreeBio", "http://freebiology.org/wiki/"),
                  ("FreeCultureWiki", "http://wiki.freeculture.org/index.php/"),
-                 ("Freedomdefined", "http://freedomdefined.org/"),
                  ("FreeFeel", "http://freefeel.org/wiki/"),
+                 ("Freedomdefined", "http://freedomdefined.org/"),
                  ("FreekiWiki", "http://wiki.freegeek.org/index.php/"),
                  ("Freenode", "http://ganfyd.org/index.php?title="),
+                 ("Gardenology", "http://www.gardenology.org/wiki/"),
                  ("GaussWiki", "http://gauss.ffii.org/"),
-                 ("Gentoo-Wiki", "http://gentoo-wiki.com/"),
                  ("GenWiki", "http://wiki.genealogy.net/index.php/"),
+                 ("Gentoo-Wiki", "http://gentoo-wiki.com/"),
                  ("GlobalVoices", "http://cyber.law.harvard.edu/dyn/globalvoices/wiki/"),
                  ("GlossarWiki", "http://glossar.hs-augsburg.de/"),
                  ("GlossaryWiki", "http://glossary.hs-augsburg.de/"),
@@ -189,35 +168,29 @@
                  ("GreatLakesWiki", "http://greatlakeswiki.org/index.php/"),
                  ("GuildWarsWiki", "http://www.wiki.guildwars.com/wiki/"),
                  ("Guildwiki", "http://guildwars.wikia.com/wiki/"),
-                 ("gutenberg", "http://www.gutenberg.org/etext/"),
-                 ("gutenbergwiki", "http://www.gutenberg.org/wiki/"),
                  ("H2Wiki", "http://halowiki.net/p/"),
+                 ("HRFWiki", "http://fanstuff.hrwiki.org/index.php/"),
+                 ("HRWiki", "http://www.hrwiki.org/index.php/"),
                  ("HammondWiki", "http://www.dairiki.org/HammondWiki/index.php3?"),
-                 ("heroeswiki", "http://heroeswiki.com/"),
                  ("HerzKinderWiki", "http://www.herzkinderinfo.de/Mediawiki/index.php/"),
-                 ("HKMule", "http://www.hkmule.com/wiki/"),
-                 ("HolshamTraders", "http://www.holsham-traders.de/wiki/index.php/"),
-                 ("HRWiki", "http://www.hrwiki.org/index.php/"),
-                 ("HRFWiki", "http://fanstuff.hrwiki.org/index.php/"),
-                 ("HumanCell", "http://www.humancell.org/index.php/"),
                  ("HupWiki", "http://wiki.hup.hu/index.php/"),
+                 ("IMDbCharacter", "http://www.imdb.com/character/ch/"),
+                 ("IMDbCompany", "http://www.imdb.com/company/co/"),
                  ("IMDbName", "http://www.imdb.com/name/nm/"),
                  ("IMDbTitle", "http://www.imdb.com/title/tt/"),
-                 ("IMDbCompany", "http://www.imdb.com/company/co/"),
-                 ("IMDbCharacter", "http://www.imdb.com/character/ch/"),
+                 ("IRC", "http://www.sil.org/iso639-3/documentation.asp?id="),
                  ("Incubator", "http://incubator.wikimedia.org/wiki/"),
-                 ("infoAnarchy", "http://www.infoanarchy.org/en/"),
-                 ("Infosecpedia", "http://www.infosecpedia.org/pedia/index.php/"),
+                 ("Infosecpedia", "http://infosecpedia.org/wiki/"),
                  ("Infosphere", "http://theinfosphere.org/"),
-                 ("IRC", "http://www.sil.org/iso639-3/documentation.asp?id="),
                  ("Iuridictum", "http://iuridictum.pecina.cz/w/"),
+                 ("JEFO", "http://esperanto-jeunes.org/wiki/"),
+                 ("JSTOR", "http://www.jstor.org/journals/"),
                  ("JamesHoward", "http://jameshoward.us/"),
                  ("JavaNet", "http://wiki.java.net/bin/view/Main/"),
                  ("Javapedia", "http://wiki.java.net/bin/view/Javapedia/"),
-                 ("JEFO", "http://esperanto-jeunes.org/wiki/"),
                  ("JiniWiki", "http://www.cdegroot.com/cgi-bin/jini?"),
+                 ("Jira", "https://jira.toolserver.org/browse/"),
                  ("JspWiki", "http://www.ecyrd.com/JSPWiki/Wiki.jsp?page="),
-                 ("JSTOR", "http://www.jstor.org/journals/"),
                  ("Kamelo", "http://kamelopedia.mormo.org/index.php/"),
                  ("Karlsruhe", "http://ka.stadtwiki.net/"),
                  ("KerimWiki", "http://wiki.oxus.net/"),
@@ -226,25 +199,26 @@
                  ("KontuWiki", "http://kontu.merri.net/wiki/"),
                  ("KoslarWiki", "http://wiki.koslar.de/index.php/"),
                  ("Kpopwiki", "http://www.kpopwiki.com/"),
+                 ("LISWiki", "http://liswiki.org/wiki/"),
+                 ("LQWiki", "http://wiki.linuxquestions.org/wiki/"),
                  ("LinguistList", "http://linguistlist.org/forms/langs/LLDescription.cfm?code="),
                  ("LinuxWiki", "http://www.linuxwiki.de/"),
                  ("LinuxWikiDe", "http://www.linuxwiki.de/"),
-                 ("LISWiki", "http://liswiki.org/wiki/"),
                  ("LiteratePrograms", "http://en.literateprograms.org/"),
                  ("Livepedia", "http://www.livepedia.gr/index.php?title="),
                  ("Lojban", "http://www.lojban.org/tiki/tiki-index.php?page="),
                  ("Lostpedia", "http://lostpedia.wikia.com/wiki/"),
-                 ("LQWiki", "http://wiki.linuxquestions.org/wiki/"),
                  ("LugKR", "http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?"),
                  ("Luxo", "http://toolserver.org/~luxo/contributions/contributions.php?user="),
-                 ("lyricwiki", "http://lyrics.wikia.com/"),
+                 ("MW", "http://www.mediawiki.org/wiki/"),
+                 ("MWOD", "http://www.merriam-webster.com/cgi-bin/dictionary?book=Dictionary&va="),
+                 ("MWOT", "http://www.merriam-webster.com/cgi-bin/thesaurus?book=Thesaurus&va="),
                  ("Mail", "https://lists.wikimedia.org/mailman/listinfo/"),
-                 ("mailarchive", "http://lists.wikimedia.org/pipermail/"),
                  ("Mariowiki", "http://www.mariowiki.com/"),
                  ("MarvelDatabase", "http://www.marveldatabase.com/wiki/index.php/"),
-                 ("MeatBall", "http://www.usemod.com/cgi-bin/mb.pl?"),
+                 ("MeatBall", "http://meatballwiki.org/wiki/"),
                  ("MediaZilla", "https://bugzilla.wikimedia.org/"),
-                 ("MemoryAlpha", "http://memory-alpha.org/en/wiki/"),
+                 ("MemoryAlpha", "http://memory-alpha.org/wiki/"),
                  ("MetaWiki", "http://sunir.org/apps/meta.pl?"),
                  ("MetaWikiPedia", "http://meta.wikimedia.org/wiki/"),
                  ("Mineralienatlas", "http://www.mineralienatlas.de/lexikon/index.php/"),
@@ -255,62 +229,48 @@
                  ("MozillaWiki", "http://wiki.mozilla.org/"),
                  ("MozillaZineKB", "http://kb.mozillazine.org/"),
                  ("MusicBrainz", "http://musicbrainz.org/doc/"),
-                 ("MW", "http://www.mediawiki.org/wiki/"),
-                 ("MWOD", "http://www.merriam-webster.com/cgi-bin/dictionary?book=Dictionary&va="),
-                 ("MWOT", "http://www.merriam-webster.com/cgi-bin/thesaurus?book=Thesaurus&va="),
-                 ("NetVillage", "http://www.netbros.com/?"),
                  ("NKcells", "http://www.nkcells.info/wiki/index.php/"),
                  ("NoSmoke", "http://no-smok.net/nsmk/"),
                  ("Nost", "http://nostalgia.wikipedia.org/wiki/"),
-                 ("OEIS", "http://www.research.att.com/~njas/sequences/"),
-                 ("OldWikisource", "http://wikisource.org/wiki/"),
+                 ("OEIS", "http://oeis.org/"),
                  ("OLPC", "http://wiki.laptop.org/go/"),
+                 ("OSI", "http://wiki.tigma.ee/index.php/"),
+                 ("OTRS", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketID="),
+                 ("OTRSwiki", "http://otrs-wiki.wikimedia.org/wiki/"),
+                 ("OldWikisource", "http://wikisource.org/wiki/"),
                  ("OneLook", "http://www.onelook.com/?ls=b&w="),
                  ("OpenFacts", "http://openfacts.berlios.de/index-en.phtml?title="),
-                 ("Openstreetmap", "http://wiki.openstreetmap.org/wiki/"),
                  ("OpenWetWare", "http://openwetware.org/wiki/"),
                  ("OpenWiki", "http://openwiki.com/?"),
+                 ("Openlibrary", "http://openlibrary.org/"),
+                 ("Openstreetmap", "http://wiki.openstreetmap.org/wiki/"),
                  ("Opera7Wiki", "http://operawiki.info/"),
                  ("OrganicDesign", "http://www.organicdesign.co.nz/"),
-                 ("OrgPatterns", "http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?"),
                  ("OrthodoxWiki", "http://orthodoxwiki.org/"),
-                 ("OSI", "http://wiki.tigma.ee/index.php/"),
-                 ("OTRS", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketID="),
-                 ("OTRSwiki", "http://otrs-wiki.wikimedia.org/wiki/"),
                  ("OurMedia", "http://www.socialtext.net/ourmedia/index.cgi?"),
+                 ("Outreach", "http://outreach.wikimedia.org/wiki/"),
                  ("OutreachWiki", "http://outreach.wikimedia.org/wiki/"),
-                 ("PaganWiki", "http://www.paganwiki.org/wiki/index.php?title="),
-                 ("Panawiki", "http://wiki.alairelibre.net/wiki/"),
-                 ("PangalacticOrg", "http://www.pangalactic.org/Wiki/"),
+                 ("PHWiki", "http://wiki.pocketheaven.com/"),
+                 ("PMEG", "http://www.bertilow.com/pmeg/.php"),
+                 ("Panawiki", "http://wiki.alairelibre.net/index.php?title="),
                  ("PatWIKI", "http://gauss.ffii.org/"),
-                 ("PerlConfWiki", "http://perl.conf.hu/index.php/"),
                  ("PerlNet", "http://perl.net.au/wiki/"),
-                 ("PersonalTelco", "http://www.personaltelco.net/index.cgi/"),
-                 ("PHWiki", "http://wiki.pocketheaven.com/"),
+                 ("PersonalTelco", "http://www.personaltelco.net/"),
                  ("PhpWiki", "http://phpwiki.sourceforge.net/phpwiki/index.php?"),
                  ("PlanetMath", "http://planetmath.org/?op=getobj&from=objects&id="),
-                 ("PMEG", "http://www.bertilow.com/pmeg/.php"),
-                 ("PMWiki", "http://old.porplemontage.com/wiki/index.php/"),
-                 ("PurlNet", "http://purl.oclc.org/NET/"),
-                 ("pyrev", "http://svn.wikimedia.org/viewvc/pywikipedia?view=rev&revision="),
+                 ("PyWiki", "http://c2.com/cgi/wiki?"),
                  ("PythonInfo", "http://www.python.org/cgi-bin/moinmoin/"),
                  ("PythonWiki", "http://www.pythonwiki.de/"),
-                 ("PyWiki", "http://c2.com/cgi/wiki?"),
-                 ("psycle", "http://psycle.sourceforge.net/wiki/"),
-                 ("qcwiki", "http://wiki.quantumchemistry.net/index.php/"),
                  ("Quality", "http://quality.wikimedia.org/wiki/"),
-                 ("Qwiki", "http://qwiki.caltech.edu/wiki/"),
-                 ("r3000", "http://prinsig.se/weekee/"),
-                 ("RakWiki", "http://rakwiki.no-ip.info/"),
-                 ("Raec", "http://www.raec.clacso.edu.ar:8080/raec/Members/raecpedia/"),
-                 ("rev", "http://www.mediawiki.org/wiki/Special:Code/MediaWiki/"),
-                 ("ReVo", "http://purl.org/NET/voko/revo/art/.html"),
                  ("RFC", "http://tools.ietf.org/html/rfc"),
-                 ("RheinNeckar", "http://wiki.rhein-neckar.de/index.php/"),
-                 ("RoboWiki", "http://robowiki.net/?"),
+                 ("ReVo", "http://purl.org/NET/voko/revo/art/.html"),
                  ("ReutersWiki", "http://glossary.reuters.com/index.php/"),
+                 ("RheinNeckar", "http://rhein-neckar-wiki.de/"),
                  ("RoWiki", "http://wiki.rennkuckuck.de/index.php/"),
-                 ("rtfm", "http://s23.org/wiki/"),
+                 ("RoboWiki", "http://robowiki.net/?"),
+                 ("SLWiki", "http://wiki.secondlife.com/wiki/"),
+                 ("SMikipedia", "http://www.smiki.de/"),
+                 ("SVGWiki", "http://wiki.svg.org/index.php/"),
                  ("Scholar", "http://scholar.google.com/scholar?q="),
                  ("SchoolsWP", "http://schools-wikipedia.org/wiki/"),
                  ("Scores", "http://imslp.org/wiki/"),
@@ -319,118 +279,89 @@
                  ("SeaPig", "http://www.seapig.org/"),
                  ("SeattleWiki", "http://seattlewiki.org/wiki/"),
                  ("SeattleWireless", "http://seattlewireless.net/?"),
-                 ("SLWiki", "http://wiki.secondlife.com/wiki/"),
                  ("SenseisLibrary", "http://senseis.xmp.net/?"),
-                 ("silcode", "http://www.sil.org/iso639-3/documentation.asp?id="),
                  ("Slashdot", "http://slashdot.org/article.pl?sid="),
-                 ("SMikipedia", "http://www.smiki.de/"),
                  ("SourceForge", "http://sourceforge.net/"),
-                 ("spcom", "http://spcom.wikimedia.org/wiki/"),
                  ("Species", "http://species.wikimedia.org/wiki/"),
                  ("Squeak", "http://wiki.squeak.org/squeak/"),
-                 ("stable", "http://stable.toolserver.org/"),
                  ("Strategy", "http://strategy.wikimedia.org/wiki/"),
                  ("StrategyWiki", "http://strategywiki.org/wiki/"),
                  ("Sulutil", "http://toolserver.org/~vvv/sulutil.php?user="),
-                 ("Susning", "http://www.susning.nu/"),
-                 ("Swtrain", "http://train.spottingworld.com/"),
-                 ("svn", "http://svn.wikimedia.org/viewvc/mediawiki/?view=log"),
-                 ("SVGWiki", "http://www.protocol7.com/svg-wiki/default.asp?"),
                  ("SwinBrain", "http://mercury.it.swin.edu.au/swinbrain/index.php/"),
                  ("SwingWiki", "http://www.swingwiki.org/"),
+                 ("Swtrain", "http://train.spottingworld.com/"),
+                 ("TESOLTaiwan", "http://www.tesol-taiwan.org/wiki/index.php/"),
+                 ("TMBW", "http://tmbw.net/wiki/"),
+                 ("TMwiki", "http://www.EasyTopicMaps.com/?page="),
+                 ("TVIV", "http://tviv.org/wiki/"),
+                 ("TVtropes", "http://www.tvtropes.org/pmwiki/pmwiki.php/Main/"),
+                 ("TWiki", "http://twiki.org/cgi-bin/view/"),
                  ("TabWiki", "http://www.tabwiki.com/index.php/"),
-                 ("Takipedia", "http://www.takipedia.org/wiki/"),
                  ("Tavi", "http://tavi.sourceforge.net/"),
                  ("TclersWiki", "http://wiki.tcl.tk/"),
                  ("Technorati", "http://www.technorati.com/search/"),
-                 ("TEJO", "http://www.tejo.org/vikio/"),
-                 ("TESOLTaiwan", "http://www.tesol-taiwan.org/wiki/index.php/"),
+                 ("Tenwiki", "http://ten.wikipedia.org/wiki/"),
                  ("Testwiki", "http://test.wikipedia.org/wiki/"),
                  ("Thelemapedia", "http://www.thelemapedia.org/index.php/"),
                  ("Theopedia", "http://www.theopedia.com/"),
                  ("ThinkWiki", "http://www.thinkwiki.org/wiki/"),
                  ("TibiaWiki", "http://tibia.erig.net/"),
                  ("Ticket", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketNumber="),
-                 ("TMBW", "http://tmbw.net/wiki/"),
                  ("TmNet", "http://www.technomanifestos.net/?"),
-                 ("TMwiki", "http://www.EasyTopicMaps.com/?page="),
-                 ("TokyoNights", "http://wiki.tokyo-nights.com/wiki/"),
                  ("Tools", "http://toolserver.org/"),
-                 ("tswiki", "http://wiki.toolserver.org/view/"),
-                 ("translatewiki", "http://translatewiki.net/wiki/"),
                  ("Trash!Italia", "http://trashware.linux.it/wiki/"),
                  ("Turismo", "http://www.tejo.org/turismo/"),
-                 ("TVIV", "http://tviv.org/wiki/"),
-                 ("TVtropes", "http://www.tvtropes.org/pmwiki/pmwiki.php/Main/"),
-                 ("TWiki", "http://twiki.org/cgi-bin/view/"),
-                 ("TwistedWiki", "http://purl.net/wiki/twisted/"),
                  ("TyvaWiki", "http://www.tyvawiki.org/wiki/"),
+                 ("USEJ", "http://www.tejo.org/usej/"),
                  ("Uncyclopedia", "http://uncyclopedia.org/wiki/"),
                  ("Unreal", "http://wiki.beyondunreal.com/wiki/"),
                  ("Urbandict", "http://www.urbandictionary.com/define.php?term="),
-                 ("USEJ", "http://www.tejo.org/usej/"),
                  ("UseMod", "http://www.usemod.com/cgi-bin/wiki.pl?"),
-                 ("usability", "http://usability.wikimedia.org/wiki/"),
+                 ("VKoL", "http://kol.coldfront.net/thekolwiki/index.php/"),
+                 ("VLOS", "http://www.thuvienkhoahoc.com/tusach/"),
                  ("ValueWiki", "http://www.valuewiki.com/w/"),
                  ("Veropedia", "http://en.veropedia.com/a/"),
                  ("Vinismo", "http://vinismo.com/en/"),
-                 ("VLOS", "http://www.thuvienkhoahoc.com/tusach/"),
-                 ("VKoL", "http://kol.coldfront.net/thekolwiki/index.php/"),
                  ("VoIPinfo", "http://www.voip-info.org/wiki/view/"),
-                 ("WarpedView", "http://www.warpedview.com/mediawiki/index.php/"),
-                 ("WebDevWikiNL", "http://www.promo-it.nl/WebDevWiki/index.php?page="),
+                 ("WLUG", "http://www.wlug.org.nz/"),
+                 ("WMF", "http://wikimediafoundation.org/wiki/"),
                  ("Webisodes", "http://www.webisodes.org/"),
-                 ("WebSeitzWiki", "http://webseitz.fluxent.com/wiki/"),
-                 ("wg", "http://wg.en.wikipedia.org/wiki/"),
                  ("Wiki", "http://c2.com/cgi/wiki?"),
+                 ("WikiChristian", "http://www.wikichristian.org/index.php?title="),
+                 ("WikiF1", "http://www.wikif1.org/"),
+                 ("WikiFur", "http://en.wikifur.com/wiki/"),
+                 ("WikiIndex", "http://wikiindex.org/"),
+                 ("WikiLemon", "http://wiki.illemonati.com/"),
+                 ("WikiMac-de", "http://apfelwiki.de/wiki/Main/"),
+                 ("WikiSkripta", "http://www.wikiskripta.eu/index.php/"),
+                 ("WikiTI", "http://wikiti.denglend.net/index.php?title="),
+                 ("WikiTravel", "http://wikitravel.org/en/"),
+                 ("WikiTree", "http://wikitree.org/index.php?title="),
+                 ("WikiWeet", "http://wikiweet.nl/wiki/"),
+                 ("WikiWikiWeb", "http://c2.com/cgi/wiki?"),
                  ("Wikia", "http://www.wikia.com/wiki/c:"),
                  ("WikiaSite", "http://www.wikia.com/wiki/c:"),
-                 ("Wikianso", "http://www.ansorena.de/mediawiki/wiki/"),
-                 ("Wikible", "http://wikible.org/en/"),
                  ("Wikibooks", "http://en.wikibooks.org/wiki/"),
                  ("Wikichat", "http://www.wikichat.org/"),
-                 ("WikiChristian", "http://www.wikichristian.org/index.php?title="),
                  ("Wikicities", "http://www.wikia.com/wiki/"),
                  ("Wikicity", "http://www.wikia.com/wiki/c:"),
-                 ("WikiF1", "http://www.wikif1.org/"),
-                 ("WikiFur", "http://en.wikifur.com/wiki/"),
-                 ("wikiHow", "http://www.wikihow.com/"),
-                 ("WikiIndex", "http://wikiindex.com/"),
-                 ("WikiLemon", "http://wiki.illemonati.com/"),
                  ("Wikilivres", "http://wikilivres.info/wiki/"),
-                 ("WikiMac-de", "http://apfelwiki.de/wiki/Main/"),
-                 ("WikiMac-fr", "http://www.wikimac.org/index.php/"),
                  ("Wikimedia", "http://wikimediafoundation.org/wiki/"),
                  ("Wikinews", "http://en.wikinews.org/wiki/"),
                  ("Wikinfo", "http://www.wikinfo.org/index.php/"),
-                 ("Wikinurse", "http://wikinurse.org/media/index.php?title="),
                  ("Wikinvest", "http://www.wikinvest.com/"),
                  ("Wikipaltz", "http://www.wikipaltz.com/wiki/"),
                  ("Wikipedia", "http://en.wikipedia.org/wiki/"),
                  ("WikipediaWikipedia", "http://en.wikipedia.org/wiki/Wikipedia:"),
                  ("Wikiquote", "http://en.wikiquote.org/wiki/"),
-                 ("Wikireason", "http://wikireason.net/wiki/"),
                  ("Wikischool", "http://www.wikischool.de/wiki/"),
-                 ("wikisophia", "http://wikisophia.org/index.php?title="),
                  ("Wikisource", "http://en.wikisource.org/wiki/"),
                  ("Wikispecies", "http://species.wikimedia.org/wiki/"),
                  ("Wikispot", "http://wikispot.org/?action=gotowikipage&v="),
-                 ("Wikitech", "https://wikitech.wikimedia.org/view/"),
-                 ("WikiTI", "http://wikiti.denglend.net/index.php?title="),
-                 ("WikiTravel", "http://wikitravel.org/en/"),
-                 ("WikiTree", "http://wikitree.org/index.php?title="),
+                 ("Wikitech", "http://wikitech.wikimedia.org/view/"),
                  ("Wikiversity", "http://en.wikiversity.org/wiki/"),
-                 ("betawikiversity", "http://beta.wikiversity.org/wiki/"),
-                 ("WikiWikiWeb", "http://c2.com/cgi/wiki?"),
                  ("Wiktionary", "http://en.wiktionary.org/wiki/"),
                  ("Wipipedia", "http://www.londonfetishscene.com/wipi/index.php/"),
-                 ("WLUG", "http://www.wlug.org.nz/"),
-                 ("wmau", "http://wikimedia.org.au/wiki/"),
-                 ("wmcz", "http://meta.wikimedia.org/wiki/Wikimedia_Czech_Republic/"),
-                 ("wmno", "http://no.wikimedia.org/wiki/"),
-                 ("wmrs", "http://rs.wikimedia.org/wiki/"),
-                 ("wmse", "http://se.wikimedia.org/wiki/"),
-                 ("wmuk", "http://uk.wikimedia.org/wiki/"),
                  ("Wm2005", "http://wikimania2005.wikimedia.org/wiki/"),
                  ("Wm2006", "http://wikimania2006.wikimedia.org/wiki/"),
                  ("Wm2007", "http://wikimania2007.wikimedia.org/wiki/"),
@@ -438,16 +369,73 @@
                  ("Wm2009", "http://wikimania2009.wikimedia.org/wiki/"),
                  ("Wm2010", "http://wikimania2010.wikimedia.org/wiki/"),
                  ("Wm2011", "http://wikimania2011.wikimedia.org/wiki/"),
+                 ("Wm2011", "http://wikimania2011.wikimedia.org/wiki/"),
                  ("Wmania", "http://wikimania.wikimedia.org/wiki/"),
-                 ("WMF", "http://wikimediafoundation.org/wiki/"),
+                 ("Wmteam", "http://wikimaniateam.wikimedia.org/wiki/"),
+                 ("WoWWiki", "http://www.wowwiki.com/"),
                  ("Wookieepedia", "http://starwars.wikia.com/wiki/"),
                  ("World66", "http://www.world66.com/"),
-                 ("WoWWiki", "http://www.wowwiki.com/"),
                  ("Wqy", "http://wqy.sourceforge.net/cgi-bin/index.cgi?"),
                  ("WurmPedia", "http://www.wurmonline.com/wiki/index.php/"),
-                 ("WZNAN", "http://www.wikiznanie.ru/wiki/article/"),
-                 ("Xboxic", "http://wiki.xboxic.com/"),
                  ("ZRHwiki", "http://www.zrhwiki.ch/wiki/"),
                  ("ZUM", "http://wiki.zum.de/"),
                  ("ZWiki", "http://www.zwiki.org/"),
+                 ("arXiv", "http://arxiv.org/abs/"),
+                 ("betawiki", "http://translatewiki.net/wiki/"),
+                 ("betawikiversity", "http://beta.wikiversity.org/wiki/"),
+                 ("bugzilla", "https://bugzilla.wikimedia.org/show_bug.cgi?id="),
+                 ("bulba", "http://bulbapedia.bulbagarden.net/wiki/"),
+                 ("buzztard", "http://buzztard.org/index.php/"),
+                 ("comune", "http://rete.comuni-italiani.it/wiki/"),
+                 ("dbdump", "http://download.wikimedia.org//latest/"),
+                 ("distributedproofreaders", "http://www.pgdp.net/wiki/"),
+                 ("distributedproofreadersca", "http://www.pgdpcanada.net/wiki/index.php/"),
+                 ("dmoz", "http://www.dmoz.org/"),
+                 ("dmozs", "http://www.dmoz.org/cgi-bin/search?search="),
+                 ("doom_wiki", "http://doom.wikia.com/wiki/"),
+                 ("download", "http://download.wikimedia.org/"),
+                 ("gutenberg", "http://www.gutenberg.org/etext/"),
+                 ("gutenbergwiki", "http://www.gutenberg.org/wiki/"),
+                 ("heroeswiki", "http://heroeswiki.com/"),
+                 ("infoAnarchy", "http://www.infoanarchy.org/en/"),
+                 ("lyricwiki", "http://lyrics.wikia.com/"),
+                 ("mailarchive", "http://lists.wikimedia.org/pipermail/"),
+                 ("psycle", "http://psycle.sourceforge.net/wiki/"),
+                 ("pyrev", "http://svn.wikimedia.org/viewvc/pywikipedia?view=rev&revision="),
+                 ("qcwiki", "http://wiki.quantumchemistry.net/index.php/"),
+                 ("rev", "http://www.mediawiki.org/wiki/Special:Code/MediaWiki/"),
+                 ("rtfm", "http://s23.org/wiki/"),
+                 ("semantic-mw", "http://www.semantic-mediawiki.org/wiki/"),
+                 ("silcode", "http://www.sil.org/iso639-3/documentation.asp?id="),
+                 ("spcom", "http://spcom.wikimedia.org/wiki/"),
+                 ("stable", "http://stable.toolserver.org/"),
+                 ("svn", "http://svn.wikimedia.org/viewvc/mediawiki/?view=log"),
+                 ("translatewiki", "http://translatewiki.net/wiki/"),
+                 ("tswiki", "http://wiki.toolserver.org/view/"),
+                 ("usability", "http://usability.wikimedia.org/wiki/"),
+                 ("wg", "http://wg.en.wikipedia.org/wiki/"),
+                 ("wikiHow", "http://www.wikihow.com/"),
+                 ("wikisophia", "http://wikisophia.org/index.php?title="),
+                 ("wmar", "http://www.wikimedia.org.ar/wiki/"),
+                 ("wmau", "http://wikimedia.org.au/wiki/"),
+                 ("wmca", "http://wikimedia.ca/index.php/"),
+                 ("wmch", "http://www.wikimedia.ch/"),
+                 ("wmcz", "http://meta.wikimedia.org/wiki/Wikimedia_Czech_Republic/"),
+                 ("wmde", "http://wikimedia.de/wiki/"),
+                 ("wmfi", "http://fi.wikimedia.org/wiki/"),
+                 ("wmhk", "http://wikimedia.hk/index.php/"),
+                 ("wmhu", "http://wiki.media.hu/wiki/"),
+                 ("wmid", "http://www.wikimedia.or.id/wiki/"),
+                 ("wmil", "http://www.wikimedia.org.il/"),
+                 ("wmin", "http://wikimedia.in/wiki/"),
+                 ("wmit", "http://wikimedia.it/index.php/"),
+                 ("wmnl", "http://nl.wikimedia.org/wiki/"),
+                 ("wmno", "http://no.wikimedia.org/wiki/"),
+                 ("wmpl", "http://pl.wikimedia.org/wiki/"),
+                 ("wmrs", "http://rs.wikimedia.org/wiki/"),
+                 ("wmru", "http://ru.wikimedia.org/wiki/"),
+                 ("wmse", "http://se.wikimedia.org/wiki/"),
+                 ("wmtw", "http://wikimedia.tw/wiki/index.php5/"),
+                 ("wmuk", "http://uk.wikimedia.org/wiki/"),
+                 ("ĈEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),
                  ("ZZZ", "http://wiki.zzz.ee/index.php/") ]
diff --git a/plugins/PigLatin.hs b/plugins/PigLatin.hs
--- a/plugins/PigLatin.hs
+++ b/plugins/PigLatin.hs
@@ -25,7 +25,7 @@
 pigLatinStr x       = x
 
 isConsonant :: Char -> Bool
-isConsonant c = c `notElem` "aeiouAEIOU" 
+isConsonant c = c `notElem` "aeiouAEIOU"
 
 capitalize :: String -> String
 capitalize "" = ""
diff --git a/plugins/Subst.hs b/plugins/Subst.hs
--- a/plugins/Subst.hs
+++ b/plugins/Subst.hs
@@ -17,11 +17,11 @@
 plugin = mkPageTransformM substituteIntoBlock
 
 substituteIntoBlock :: [Block] -> PluginM [Block]
-substituteIntoBlock ((Para [Link ref ("!subst", _)]):xs) = 
-     do let target = inlinesToString ref 
+substituteIntoBlock ((Para [Link ref ("!subst", _)]):xs) =
+     do let target = inlinesToString ref
         cfg <- askConfig
         let fs = filestoreFromConfig cfg
-        article <- try $ liftIO (retrieve fs (target ++ ".page") Nothing) 
+        article <- try $ liftIO (retrieve fs (target ++ ".page") Nothing)
         case article :: Either FileStoreError String of
           Left  _    -> let txt = Str ("[" ++ target ++ "](!subst)")
                             alt = "'" ++ target ++ "' doesn't exist. Click here to create it."
diff --git a/plugins/WebArchiver.hs b/plugins/WebArchiver.hs
--- a/plugins/WebArchiver.hs
+++ b/plugins/WebArchiver.hs
@@ -3,55 +3,38 @@
 (It will also submit them to Alexa (the source for the Internet Archive), but Alexa says that
 its bots take weeks to visit and may not ever.)
 
+This module employs the archiver daemon <http://hackage.haskell.org/package/archiver> as a library; `cabal install archiver` will install it.
+
 Limitations:
 * Only parses Markdown, not ReST or any other format; this is because 'readMarkdown'
 is hardwired into it.
+* No rate limitation or choking; will fire off all requests as fast as possible.
+  If pages have more than 20 external links or so, this may result in your IP being temporarily
+  banned by WebCite. To avoid this, you can use WebArchiverBot.hs instead, which will parse & dump
+  URLs into a file processed by the archiver daemon (which *is* rate-limited).
 
 By: Gwern Branwen; placed in the public domain -}
 
 module WebArchiver (plugin) where
 
 import Control.Concurrent (forkIO)
-import Control.Monad (when)
-import Control.Monad.Trans (MonadIO)
-import Data.Maybe (fromJust)
-import Network.Browser (browse, formToRequest, request, Form(..))
-import Network.HTTP (getRequest, rspBody, simpleHTTP, RequestMethod(POST))
-import Network.URI (isURI, parseURI, uriPath)
-
-import Network.Gitit.Interface (askUser, liftIO, processWithM, uEmail, Plugin(PreCommitTransform), Inline(Link))
+import Network.URL.Archiver as A (checkArchive)
+import Network.Gitit.Interface (askUser, bottomUpM, liftIO, uEmail, Plugin(PreCommitTransform), Inline(Link))
 import Text.Pandoc (defaultParserState, readMarkdown)
 
 plugin :: Plugin
 plugin = PreCommitTransform archivePage
 
--- archivePage :: (MonadIO m) => String -> ReaderT (Config, Maybe User) (StateT IO) String
+-- archivePage :: String -> ReaderT PluginData (StateT Context IO) String
 archivePage x = do mbUser <- askUser
                    let email = case mbUser of
                         Nothing -> "nobody@mailinator.com"
                         Just u  -> uEmail u
                    let p = readMarkdown defaultParserState x
                    -- force evaluation and archiving side-effects
-                   _p' <- liftIO $ processWithM (archiveLinks email) p
+                   _p' <- liftIO $ bottomUpM (archiveLinks email) p
                    return x -- note: this is read-only - don't actually change page!
 
 archiveLinks :: String -> Inline -> IO Inline
-archiveLinks e x@(Link _ (uln, _)) = forkIO (checkArchive e uln) >> return x
+archiveLinks e x@(Link _ (uln, _)) = forkIO (A.checkArchive e uln) >> return x
 archiveLinks _ x                   = return x
-
--- | Error check the URL and then archive it both ways
-checkArchive :: (MonadIO m) => String -> String -> m ()
-checkArchive email url = when (isURI url) $ liftIO (webciteArchive email url >> alexaArchive url)
-
-webciteArchive :: String -> String -> IO ()
-webciteArchive email url = ignore $ openURL ("http://www.webcitation.org/archive?url=" ++ url ++ "&email=" ++ email)
-   where openURL = simpleHTTP . getRequest
-         ignore = fmap $ const ()
-
-alexaArchive :: String -> IO ()
-alexaArchive url = do let archiveform = Form POST
-                             (fromJust $ parseURI "http://www.alexa.com/help/crawlrequest")
-                                 [("url", url), ("submit", "")]
-                      (uri, resp) <- browse $ request $ formToRequest archiveform
-                      when (uriPath uri /= "/help/crawlthanks") $
-                           error $ "Request failed! Did Alexa change webpages? Response:" ++ rspBody resp
