diff --git a/Boolean/README b/Boolean/README
--- a/Boolean/README
+++ b/Boolean/README
@@ -1,3 +1,3 @@
 
-A general boolean algebra class and some instances for haskell. 
+A general boolean algebra class and some instances for haskell.
 The main module is Boolean.Algebra.
diff --git a/CacheIO.hs b/CacheIO.hs
--- a/CacheIO.hs
+++ b/CacheIO.hs
@@ -152,8 +152,8 @@
 combineVal :: (Readable c1,Readable c2) => c1 a -> c2 b -> IO (ArbitraryReader (a,b))
 combineVal sva svb = do
     let lsv = do
-	av <- readVal sva
-	bv <- readVal svb
+        av <- readVal sva
+        bv <- readVal svb
         return (av,bv)
     return $ ArbitraryReader lsv
 
diff --git a/ConfigFile.hs b/ConfigFile.hs
--- a/ConfigFile.hs
+++ b/ConfigFile.hs
@@ -89,11 +89,11 @@
 configFile fn = Config $ \k -> do
     cf <- readVal config_files_var
     case lookup fn cf of
-	Just cl -> basicLookup fn cl k
-	Nothing -> do
-	    cl <- E.catch (fmap parseConfigFile $ readFile fn) (\(_ :: E.IOException) -> return [])
-	    mapVal config_files_var ((fn,cl):)
-	    basicLookup fn cl k
+        Just cl -> basicLookup fn cl k
+        Nothing -> do
+            cl <- E.catch (fmap parseConfigFile $ readFile fn) (\(_ :: E.IOException) -> return [])
+            mapVal config_files_var ((fn,cl):)
+            basicLookup fn cl k
 
 configEnv :: Config
 configEnv = Config $ \k -> do
@@ -106,17 +106,17 @@
 instance Monoid Config where
     mempty =  Config $ \_ -> return []
     mappend (Config c1) (Config c2) = Config $ \s -> do
-	x <- c1 s
-	y <- c2 s
-	return (x ++ y)
+        x <- c1 s
+        y <- c2 s
+        return (x ++ y)
 
 configShow :: [String] -> Config -> IO String
 configShow ss (Config c) = do
     v <- mapM c ss
     return $ unlines $ map p $ zip ss v where
-	p (k,((w,(k',v))):_) = k ++ " " ++ v ++ "\n#  in " ++ w ++
-	    if k' /= k then " as " ++ k' else ""
-	p (k,[]) = "#" ++ k ++ " Not Found."
+        p (k,((w,(k',v))):_) = k ++ " " ++ v ++ "\n#  in " ++ w ++
+            if k' /= k then " as " ++ k' else ""
+        p (k,[]) = "#" ++ k ++ " Not Found."
 
 
 -- types of config sources:
@@ -141,7 +141,7 @@
     fixup (x:xs) = x: fixup xs
     fixup [] = []
     bl s = let (n,r) = span isPropName (dropWhile isSpace s) in
-	if null n then [] else [(n,dropWhile isSpace r)]
+        if null n then [] else [(n,dropWhile isSpace r)]
 
 {-
 
@@ -166,9 +166,9 @@
 configLookupBool k = do
     x <- configLookup k
     case x of
-	Just s | cond -> return True where
-	    cond = (map toLower s) `elem` ["true", "yes", "on", "y", "t"]
-	_ -> return False
+        Just s | cond -> return True where
+            cond = (map toLower s) `elem` ["true", "yes", "on", "y", "t"]
+        _ -> return False
 
 
 configLookup k = do
diff --git a/Curses.hsc b/Curses.hsc
--- a/Curses.hsc
+++ b/Curses.hsc
@@ -251,10 +251,10 @@
     i l = zipWithM_ f [1 .. n] l >> return n where n = length l
     -- the full intensity colors of a w*w*w rgb box
     rgbbox w = [ b+o |
-	  b <- [0, w*w .. (w-2)*w*w ],
-	  o <- [w-1, 2*w-1 .. (w-1)*w-1] -- blue
-	    ++ [(w-1)*w .. w*w-1] ] -- green
-	++ [(w-1)*w*w .. w*w*w-1] -- red
+          b <- [0, w*w .. (w-2)*w*w ],
+          o <- [w-1, 2*w-1 .. (w-1)*w-1] -- blue
+            ++ [(w-1)*w .. w*w-1] ] -- green
+        ++ [(w-1)*w*w .. w*w*w-1] -- red
     icp _ n
       | n < 2 = return 0
       | n < 8 = initPair (Pair 1) defaultForeground bg >> return 1
@@ -268,7 +268,7 @@
     b <- hasColors
     when b $ do
         startColor
-	initColorPairs (Color 0)
+        initColorPairs (Color 0)
     --when b useDefaultColors
     --cBreak True
     cbreak
@@ -440,7 +440,7 @@
 withColorId :: Window -> Int -> IO a -> IO a
 withColorId win c action = do
     ncp <- readIORef nColorPairs
-    if ncp == 0 
+    if ncp == 0
       then action
       else withColor win (Pair $ 1 + c `mod` ncp) action
 
@@ -694,8 +694,8 @@
 
 withCursor :: CursorVisibility -> IO a -> IO a
 withCursor nv action = Control.Exception.bracket (cursSet (vis_c nv)) (\v -> case v of
-		(#const ERR) -> return 0
-		x -> cursSet x) (\_ -> action)
+                (#const ERR) -> return 0
+                x -> cursSet x) (\_ -> action)
 
 
 
@@ -967,7 +967,7 @@
 
 data ButtonEvent = ButtonPressed Int | ButtonReleased Int | ButtonClicked Int |
     ButtonDoubleClicked Int | ButtonTripleClicked Int | ButtonShift | ButtonControl | ButtonAlt
-	deriving(Eq,Show)
+        deriving(Eq,Show)
 
 withMouseEventMask :: [ButtonEvent] -> IO a -> IO a
 
diff --git a/EIO.hs b/EIO.hs
--- a/EIO.hs
+++ b/EIO.hs
@@ -24,29 +24,29 @@
 hGetRawContents h = do
     a <- newArray_ (1,bufSize)
     getall a where
-	getall a = do
-	    sz <- hGetArray h a bufSize
-	    av <- getElems a
-	    if sz == 0 then return [] else do
-		r <- getall a
-		return (take sz av ++ r)
+        getall a = do
+            sz <- hGetArray h a bufSize
+            av <- getElems a
+            if sz == 0 then return [] else do
+                r <- getall a
+                return (take sz av ++ r)
 
 hPutRawContents :: Handle -> [Word8] -> IO ()
 hPutRawContents h xs = do
     a <- newArray_ (1,bufSize)
     prc a h xs where
-	prc _ _ [] = return ()
-	prc a h xs@(_:_) = do
-	    let (ys,zs) = splitAt bufSize xs
-	    if null zs
-	      then do -- work around a ghc bug in hPutArray
-	        let lys = length ys
-		a' <- newListArray (1,lys) ys
-		hPutArray h a' lys
-	      else do
-		zipWithM_ (writeArray a) [1..] ys
-		hPutArray h a bufSize
-		prc a h zs
+        prc _ _ [] = return ()
+        prc a h xs@(_:_) = do
+            let (ys,zs) = splitAt bufSize xs
+            if null zs
+              then do -- work around a ghc bug in hPutArray
+                let lys = length ys
+                a' <- newListArray (1,lys) ys
+                hPutArray h a' lys
+              else do
+                zipWithM_ (writeArray a) [1..] ys
+                hPutArray h a bufSize
+                prc a h zs
 
 
 putRaw :: Handle -> [Word8] -> IO ()
@@ -93,14 +93,14 @@
 memoIO ioa = do
     v <- readIORef var
     case v of
-	Just x -> return x
-	Nothing -> do
-	    x <- ioa
-	    writeIORef var (Just x)
-	    return x
+        Just x -> return x
+        Nothing -> do
+            x <- ioa
+            writeIORef var (Just x)
+            return x
      where
         {-# NOTINLINE var #-}
-	var = unsafePerformIO $ newIORef Nothing
+        var = unsafePerformIO $ newIORef Nothing
 
 getTempFileName :: IO String
 getTempFileName = do
diff --git a/ErrorLog.hs b/ErrorLog.hs
--- a/ErrorLog.hs
+++ b/ErrorLog.hs
@@ -82,12 +82,12 @@
 -- to stderr. note, that while the errorlog will function properly with concurrent
 -- applications, a single errorlog is shared by all threads.
 withErrorLog :: String    -- ^ filename of log
-		-> IO a      -- ^ action to execute with logging to file
-		-> IO a
+                -> IO a      -- ^ action to execute with logging to file
+                -> IO a
 withErrorLog "-" action = bracket (swapMVar ior stderr) (swapMVar ior) (\_ -> action)
 withErrorLog fn action = E.bracket (openFile fn WriteMode) hClose $ \h -> do
-	hSetBuffering h LineBuffering
-	bracket (swapMVar ior h) (swapMVar ior) (\_ -> action)
+        hSetBuffering h LineBuffering
+        bracket (swapMVar ior h) (swapMVar ior) (\_ -> action)
 
 -- | sets log level to new value, returns old log level.
 setLogLevel :: LogLevel -> IO LogLevel
@@ -97,14 +97,14 @@
 -- If the action throws an exception, it will be logged along with the
 -- exit entry.
 withStartEndEntrys :: String  -- ^ title to use in log entries
-		      -> IO a    -- ^ action to execute
-		      -> IO a
+                      -> IO a    -- ^ action to execute
+                      -> IO a
 withStartEndEntrys n action = do
     gct >>= \ct -> putLogLn (ct ++ " " ++ n ++ " Starting")
     handle
-	(\(e :: SomeException) -> gct >>= \ct -> putLogException (ct ++ " " ++ n ++ " Ending due to Exception:" ) e >> throw e)
-	(action >>= \r -> gct >>= \ct -> putLogLn (ct ++ " " ++ n ++ " Ending") >> return r) where
-	    gct = getClockTime >>= \ct -> return $ "[" ++ show ct ++ "]"
+        (\(e :: SomeException) -> gct >>= \ct -> putLogException (ct ++ " " ++ n ++ " Ending due to Exception:" ) e >> throw e)
+        (action >>= \r -> gct >>= \ct -> putLogLn (ct ++ " " ++ n ++ " Ending") >> return r) where
+            gct = getClockTime >>= \ct -> return $ "[" ++ show ct ++ "]"
 
 
 -- | run an action, printing an error message to the log if it ends with an exception.
@@ -112,9 +112,9 @@
 withErrorMessage :: String -> IO a -> IO a
 withErrorMessage n action = do
     handle
-	(\(e :: SomeException) -> gct >>= \ct -> putLogLn (normalize n ++ ct ++ " Ending due to Exception:\n" ++ indent 4 (show e) ) >> throw e )
-	action  where
-	    gct = getClockTime >>= \ct -> return $ "[" ++ show ct ++ "]"
+        (\(e :: SomeException) -> gct >>= \ct -> putLogLn (normalize n ++ ct ++ " Ending due to Exception:\n" ++ indent 4 (show e) ) >> throw e )
+        action  where
+            gct = getClockTime >>= \ct -> return $ "[" ++ show ct ++ "]"
 
 
 -- | set routine with same signature as 'hPutStr' to use for writing to log.
@@ -171,7 +171,7 @@
 -- | attempt an action, add a log entry with the exception if it
 -- fails
 attemptIO :: IO a -> IO ()
-attemptIO action = E.catch (action >> return ()) 
+attemptIO action = E.catch (action >> return ())
   (\(e :: IOException) -> putLogException "attempt ExceptionCaught" e)
 
 tryMapM :: (a -> IO b) -> [a] -> IO [b]
@@ -194,9 +194,9 @@
 
 -- | Retry an action until it succeeds.
 retryIO :: Float      -- ^ number of seconds to pause between trys
-	 -> String  -- ^ string to annotate log entries with when retrying
-	 -> IO a    -- ^ action to retry
-	 -> IO a
+         -> String  -- ^ string to annotate log entries with when retrying
+         -> IO a    -- ^ action to retry
+         -> IO a
 retryIO delay n action = do
     handle (\(e :: IOException) -> putLogException (n ++ " (retrying in " ++ show delay ++ "s):") e >> threadDelay (floor $ 1000000 * delay) >> retryIO delay n action) action
 
@@ -213,15 +213,15 @@
     v <- newEmptyMVar
     ts <- mapM (forkIO . f v) arms
     g v ts where
-	f v arm = do
-	    t <- myThreadId
-	    r <- tryIO arm
-	    putMVar v (t,r)
-	g v ts = do
-	    (t,r) <- takeMVar v
-	    let ts' = delete t ts
-	    case r of
-		Left e -> if null ts' then throw e else g v ts'
-		Right x -> do
-		    mapM_ killThread ts'
-		    return x
+        f v arm = do
+            t <- myThreadId
+            r <- tryIO arm
+            putMVar v (t,r)
+        g v ts = do
+            (t,r) <- takeMVar v
+            let ts' = delete t ts
+            case r of
+                Left e -> if null ts' then throw e else g v ts'
+                Right x -> do
+                    mapM_ killThread ts'
+                    return x
diff --git a/Filter.hs b/Filter.hs
--- a/Filter.hs
+++ b/Filter.hs
@@ -59,7 +59,7 @@
     parseTwiddle = do
         char '~'
         f <- satisfy isAlpha
-	option ':' (char ':')
+        option ':' (char ':')
         s <- string
         spaces
         z f s
@@ -94,43 +94,43 @@
 parseFilter :: Monad m => String -> m Filter
 parseFilter = parser (between spaces eof rmp) where
     rmp = do
-	fa <- mp
-	r <- rmp'
-	case r of
-	    [] -> return fa
-	    _ -> return $ FilterOr (fa:r)
+        fa <- mp
+        r <- rmp'
+        case r of
+            [] -> return fa
+            _ -> return $ FilterOr (fa:r)
     rmp' = (do
-	char ';'
-	spaces
-	v <- mp
-	r <- rmp'
-	return (v:r)) <|> return []
+        char ';'
+        spaces
+        v <- mp
+        r <- rmp'
+        return (v:r)) <|> return []
     mp = many1 fp >>= \v -> case v of
-	    [x] -> return x
-	    xs -> return $ FilterAnd xs
+            [x] -> return x
+            xs -> return $ FilterAnd xs
     fp = nf <|> flt <|> pr <|> bool <|> do s <- (token string); liftM (FilterRegex f_messageBody) (compileRx s)
     pr = do
-	char '('
-	spaces
-	v <- rmp
-	char ')'
-	spaces
-	return v
+        char '('
+        spaces
+        v <- rmp
+        char ')'
+        spaces
+        return v
     nf = char '!' >> liftM FilterNot fp
     string = qstring <|> many1 (noneOf " \t\n~'!();%")
     qstring = between (char '\'') (char '\'') $ many ((char '\'' >> char '\'' >> return '\'') <|> noneOf "'")
     bool = do
-	char '%'
-	c <- sat isAlpha
-	spaces
-	return $ b c
+        char '%'
+        c <- sat isAlpha
+        spaces
+        return $ b c
     flt = do
-	char '~'
-	c <- sat isAlpha
-	option ':' (char ':')
-	s <- string
-	spaces
-	z c s
+        char '~'
+        c <- sat isAlpha
+        option ':' (char ':')
+        s <- string
+        spaces
+        z c s
     z 'a' s = return $ FilterAuthor s
     z 'c' s = return $ FilterCategory (catParseNew s)
     z 't' s = return $ FilterCategory (catParseNew s) -- TODO
@@ -173,6 +173,7 @@
     f _ = []
 -}
 
+siglookup c (RequestingKey _ (Key n _) _ _:_) | c == n = True
 siglookup c (Unverifyable (Key n _):_) | c == n = True
 siglookup c (Signed (Key n _):_) | c == n = True
 siglookup c (_:xs) = siglookup c xs
diff --git a/Format.hs b/Format.hs
--- a/Format.hs
+++ b/Format.hs
@@ -114,19 +114,19 @@
 fmt :: String -> [F] -> String
 fmt ('%':'%':xs) fs = '%':fmt xs fs
 fmt ('%':xs) (f:fs) =  case xs5 of
-	('\'':cs) -> let (a,b) = gs "" cs in formatString a bp ++ fmt b fs
-	(c:cs) -> f (bp {patternChar = c}) ++ fmt cs fs
-	[] -> []
+        ('\'':cs) -> let (a,b) = gs "" cs in formatString a bp ++ fmt b fs
+        (c:cs) -> f (bp {patternChar = c}) ++ fmt cs fs
+        [] -> []
      where
     bp = pattern {patternSub = ss, patternFlags = flags, patternWidth = w, patternPrecision =  p}
     (flags,xs2) = span (`elem` "# 0-+'") xs
     (w,xs3) = grabNum xs2
     (p,xs4) = case xs3 of
-	('.':xs) -> grabNum xs
-	xs -> (Nothing, xs)
+        ('.':xs) -> grabNum xs
+        xs -> (Nothing, xs)
     (ss,xs5) = case xs4 of
-	('(':xs) -> let (u,v) = span (/= ')') xs in ([u],tail v)
-	xs -> ([],xs)
+        ('(':xs) -> let (u,v) = span (/= ')') xs in ([u],tail v)
+        xs -> ([],xs)
     grabNum xs = let (u,v) = span isDigit xs in if null u then (Nothing,v) else (Just (read u),v)
     gs x ('\'':'\'':cs) = gs ('\'':x) cs
     gs x ('\'':cs) = (reverse x,cs)
@@ -154,7 +154,7 @@
     hex = "0123456789abcdef"
     foo 0 = ""
     foo x = let (u,v) = x `divMod` (toInteger base) in
-	(hex !! fromIntegral v) : foo u
+        (hex !! fromIntegral v) : foo u
 
 
 childFmt :: Pattern -> [F] -> String
diff --git a/Gale/Gale.hs b/Gale/Gale.hs
--- a/Gale/Gale.hs
+++ b/Gale/Gale.hs
@@ -21,8 +21,10 @@
 import Data.List
 import Data.Maybe
 import System.Time
+import System.Timeout (timeout)
 
 import Control.Concurrent
+import Control.Concurrent.Async (async, wait)
 import Control.Exception as E
 import Data.Bits
 import Network.BSD
@@ -136,13 +138,13 @@
 connectTo hostname port = do
     --proto <- getProtocolNumber "tcp"
     bracketOnError
-	(socket AF_INET Stream 6)
-	(sClose)  -- only done if there's an error
+        (socket AF_INET Stream 6)
+        (sClose)  -- only done if there's an error
         (\sock -> do
-      	  he <- getHostByName hostname
-      	  connect sock (SockAddrInet port (hostAddress he))
-      	  socketToHandle sock ReadWriteMode
-	)
+                he <- getHostByName hostname
+                connect sock (SockAddrInet port (hostAddress he))
+                socketToHandle sock ReadWriteMode
+        )
 
 
 
@@ -157,12 +159,12 @@
 
 emptyPuffer :: MVar Handle -> IO ()
 emptyPuffer hv = repeatM_ (threadDelay 30000000 >> sendEmptyPuff) where
-	sendEmptyPuff = withMVar hv $ \h -> do
-		putWord32 h 0
-		putWord32 h 8
-		putWord32 h 0
-		putWord32 h 0
-		hFlush h
+        sendEmptyPuff = withMVar hv $ \h -> do
+                putWord32 h 0
+                putWord32 h 8
+                putWord32 h 0
+                putWord32 h 0
+                hFlush h
 
 connectThread :: GaleContext ->  [String] -> MVar Handle -> IO ()
 connectThread gc _ hv = retryIO 5.0 ("ConnectionError") doit where
@@ -171,30 +173,52 @@
         swapMVar (connectionStatus gc)  $ Left $ "Attempting to connect to: " ++ unwords ds
         trySeveral (map attemptConnect ds)
     doit = bracket openHandle (hClose . fst) $ \(h,hn) -> do
-	putWord32 h 1
-	_ <- readWord32 h  -- version
+        putWord32 h 1
+        _ <- readWord32 h  -- version
         sendGimme gc
         swapMVar (connectionStatus gc) $ Right hn
-	bracket_ (putMVar hv h) (takeMVar hv) $
-	  bracket (forkIO (emptyPuffer hv)) killThread $ \_ -> repeatM_ $ do
-	    w <- readWord32 h
-	    l <- readWord32 h
+        bracket_ (putMVar hv h) (takeMVar hv) $
+          bracket (forkIO (emptyPuffer hv)) killThread $ \_ -> repeatM_ $ do
+            w <- readWord32 h
+            l <- readWord32 h
             bs <- LBS.hGet h (fromIntegral l)
-	    when (w == 0) $ do
+            when (w == 0) $ do
                 let hash = sha1 bs
                     (catl,puff) = runGet decodePuff bs
                     cat = mapMaybe parseCategoryOld (spc catl)
                 ct <- getClockTime
                 let ef = \xs -> ((fromString "_ginsu.timestamp",FragmentTime ct):(fromString "_ginsu.spumbuster", FragmentText (packString (bsToHex hash))):xs)
                 p' <- galeDecryptPuff gc Puff { signature = [], cats = cat, fragments = ef puff}
-                writeChan (channel gc) $ p'
-                case getFragmentData p' f_answerKey' of
+                np <- case [(kh, k, data_, sig)
+                           | RequestingKey kh k data_ sig <- signature p'] of
+                      [] -> return p'
+                      (kh, k, data_, sig):_ -> do
+                        res <- timeout 4000000 $ wait kh
+                        let unver = p' { signature = [Unverifyable k] }
+                        case res of
+                          Just x -> case x of
+                            DestEncrypted (k':_) -> do
+                              mkey <- verifySignature k' data_ sig
+                              return $ p' { signature = [mkey] }
+                            _ -> return unver
+                          Nothing -> return unver
+                writeChan (channel gc) np
+                case getFragmentData np f_answerKey' of
                     Just d -> putKey (keyCache gc) d
                     Nothing -> return ()
-                case (cats p',getFragmentString p' f_answerKeyError') of
+                case (cats np,getFragmentString np f_answerKeyError') of
                     ([Category (n,d)],Just _) | "_gale.key." `isPrefixOf` n -> noKey (keyCache gc) (catShowNew $ Category (drop 10 n,d))
                     (_,_) -> return ()
+                maybeReplyToKeyQuery gc np
 
+maybeReplyToKeyQuery :: GaleContext -> Puff -> IO ()
+maybeReplyToKeyQuery gc p | Just kn <- getFragmentString p f_questionKey = do
+  let kn' = unpackPS kn
+  mkb <- getPubKeyBytes (keyCache gc) kn'
+  d <- createPuff gc False $ keyResponsePuff mkb kn'
+  putLog LogDebug $ "sending reply for: " ++ kn'
+  retryIO 3.0 "error sending puff" $ withMVar (gHandle gc) $ \h -> LBS.hPut h d >> hFlush h
+maybeReplyToKeyQuery _ _ = return ()
 
 decodePuff :: Get (String,FragmentList)
 decodePuff = do
@@ -242,11 +266,13 @@
 
 collectSigs :: [Signature] -> ([String],[String])
 collectSigs ss = liftT2 (snub, snub) $ cs ss ([],[]) where
-    cs ((Unverifyable _):_) _ = error "attempt to create unverifyable puff"
-    cs (Signed (Key k _):ss) (ks,es) = cs ss (k:ks,es)
-    cs (Encrypted es':ss) (ks,es) = cs ss (ks,es' ++ es)
-    cs [] x = x
-
+    cs sig x@(ks,es) = case sig of
+      (RequestingKey _ _ _ _):_ -> lose
+      (Unverifyable _):_ -> lose
+      (Signed (Key k _)):ss -> cs ss (k:ks,es)
+      (Encrypted es':ss) -> cs ss (ks,es' ++ es)
+      [] -> x
+    lose = error "attempt to create unverifiable puff"
 
 tagWord :: Int -> Put -> Put
 tagWord i p = putWord32be (fromIntegral i) >> p
@@ -273,14 +299,14 @@
     evaluate $ runPut pd
 createPuff gc will p | (kn:_,es) <-  collectSigs (signature p) = do
     getPrivateKey (keyCache gc) kn >>= \v -> case v of
-	Nothing -> createPuff gc will $ p {signature = []}
-	Just (Key _ kfl,pkey) -> do
-	    sfl <- case fragmentString f_keyOwner kfl of
-		Just o -> return [(f_messageSender, FragmentText o)]
-		Nothing -> return []
-	    let fl = runPutBS $ tagWord 0 (putFragments (fragments p `mergeFrags` sfl))
-	    sig <- signAll pkey fl
-	    let sd = runPutBS $ do
+        Nothing -> createPuff gc will $ p {signature = []}
+        Just (Key _ kfl,pkey) -> do
+            sfl <- case fragmentString f_keyOwner kfl of
+                Just o -> return [(f_messageSender, FragmentText o)]
+                Nothing -> return []
+            let fl = runPutBS $ tagWord 0 (putFragments (fragments p `mergeFrags` sfl))
+            sig <- signAll pkey fl
+            let sd = runPutBS $ do
                     putByteString bs_signature_magic1
                     tagWord (BS.length sig) (putByteString sig)
                     putByteString (BS.pack pubkey_magic3)
@@ -288,7 +314,7 @@
                 fd = tagWord (BS.length sd) (putByteString sd) >> putByteString fl
                 fragments = [(f_securitySignature,FragmentData (runPutBS fd))]
             nfragments <- cryptFragments gc es fragments
-	    createPuff gc will $ p {signature = [], fragments = nfragments }
+            createPuff gc will $ p {signature = [], fragments = nfragments }
 createPuff _ _ _ = error "createPuff: invalid arguments"
 
 cryptFragments :: GaleContext -> [String] -> FragmentList -> IO FragmentList
@@ -329,8 +355,8 @@
 putFragments fl = mapM_ f fl where
     f (s',f) = putWord32be t >> putWord32be (fromIntegral $ LBS.length nxs) >> putLazyByteString nxs  where
         nxs = runPut (n >> xs)
-	(t, xs) = g f
-	n = putWord32be (fromIntegral $ length s) >> (putGaleString s)
+        (t, xs) = g f
+        n = putWord32be (fromIntegral $ length s) >> (putGaleString s)
         s = toString s'
     g (FragmentData ws) = (1, putByteString ws)
     g (FragmentText s) = (0, putGaleString (unpackPS s))
@@ -351,11 +377,11 @@
     bl [_] = []
     bl (x:xs) = x:bl xs
     p = do
-	char '@'
-	d <- many (noneOf "/")
-	parseExact "/user/"
-	c <- parseRest
-	return (Category (con (bl c),d))
+        char '@'
+        d <- many (noneOf "/")
+        parseExact "/user/"
+        c <- parseRest
+        return (Category (con (bl c),d))
 
 catShowOld :: Category -> String
 catShowOld (Category (c,d)) = "@" ++ d ++ "/user/" ++ con c ++ "/" where
@@ -368,35 +394,55 @@
 -- Security routines
 --------------------
 
+verifySignature :: Key -> BS.ByteString -> BS.ByteString -> IO Signature
+verifySignature k data_ sig = do
+  pkey <- keyToPkey k
+  rv <- verifyAll pkey data_ sig
+  return $ (if rv then Signed else Unverifyable) k
+
 galeDecryptPuff :: GaleContext -> Puff -> IO Puff
 galeDecryptPuff gc p = handle (\(_ :: IOException) -> return p) $ galeDecryptPuff' gc p
 galeDecryptPuff' gc p | (Just xs) <- getFragmentData p f_securitySignature = do
     let (l,xs') = xdrReadUInt (BS.unpack xs)
-	(sb,xs'') = xdrReadUInt (drop 4 xs')
-	fl = (decodeFragments $ drop (fromIntegral l + 4) xs') ++ [f|f <- fragments p, fst f /= f_securitySignature]
-    key <- maybe (fail "parseKey") return $ parseKey $ take (fromIntegral (l - (8 + sb))) (drop (fromIntegral sb) xs'')
-    galeDecryptPuff' gc $ p {signature = (Unverifyable key): signature p, fragments = fl}
+        (sb,xs'') = xdrReadUInt (drop 4 xs')
+        fl = (decodeFragments $ drop (fromIntegral l + 4) xs') ++ [f | f <- fragments p, fst f /= f_securitySignature]
+        sigoff = 12 -- skip length hdr (4), sig magic (4), sig len (4)
+        siglen = fromIntegral sb
+        keylen = fromIntegral l - (8 + siglen)
+        keyoff = sigoff + siglen -- key directly follows sig
+        sig = BS.take siglen $ BS.drop sigoff xs
+        data_ = BS.drop (keyoff + keylen) xs
+    key <- maybe (fail "parseKey") return $ parseKey $ take keylen (drop siglen xs'')
+    let (Key kn _) = key
+    kgc <- getKey (keyCache gc) kn
+    mkey <- case kgc of
+      Nothing -> do
+        let senderc = catParseNew kn
+        h <- async (findDest gc senderc)
+        return $ RequestingKey h key data_ sig
+      Just k -> verifySignature k data_ sig
+    galeDecryptPuff' gc $ p {signature = mkey: signature p, fragments = fl}
 galeDecryptPuff' gc p | (Just xs) <- getFragmentData p f_securityEncryption = do
     (cd,ks) <- maybe (fail "parseKey") return $ parser pe (BS.unpack xs)
     dfl <- firstIO (map (td' (BS.pack cd)) [ (BS.pack x,y,BS.pack z) | (x,y,z) <- ks])
-    let dfl' = dfl ++ [f|f <- fragments p, fst f /= f_securityEncryption]
+    let dfl' = dfl ++ [f | f <- fragments p, fst f /= f_securityEncryption]
     galeDecryptPuff' gc $ p {signature = (Encrypted (map (\(_,n,_) -> n) ks)): signature p, fragments = dfl'}  where
-	pe = (parseExact cipher_magic1 >> pr parseNullString) <|> (parseExact cipher_magic2 >> pr parseLenString)
-	pk pkname iv = do
-	    kname <- pkname
-	    keydata <- parseLenData
-	    return $ (iv,kname,keydata)
-	pr pkname = do
-	    iv <- parseSome 8
-	    keycount <-  parseIntegral32
-	    ks <- replicateM keycount (pk pkname iv)
-	    xs <- parseRest
-	    return (xs,ks)
-	td' cd (iv,kname,keydata) = do
-	    Just (_,pkey) <- getPrivateKey (keyCache gc) kname
-	    dd <- decryptAll keydata iv pkey cd
-	    --let dfl = decodeFragments (drop 4 (BS.unpack dd))
-	    return $ runGet decodeFrags (LBS.fromChunks [BS.drop 4 dd])
+        pe = (parseExact cipher_magic1 >> pr parseNullString) <|> (parseExact cipher_magic2 >> pr parseLenString)
+        pk pkname iv = do
+            kname <- pkname
+            keydata <- parseLenData
+            return $ (iv,kname,keydata)
+        pr pkname = do
+            iv <- parseSome 8
+            keycount <-  parseIntegral32
+            ks <- replicateM keycount (pk pkname iv)
+            xs <- parseRest
+            return (xs,ks)
+        td' cd (iv,kname,keydata) = do
+            Just (_,pkey) <- getPrivateKey (keyCache gc) kname
+            dd <- decryptAll keydata iv pkey cd
+            --let dfl = decodeFragments (drop 4 (BS.unpack dd))
+            return $ runGet decodeFrags (LBS.fromChunks [BS.drop 4 dd])
 galeDecryptPuff' _ x = return x
 
 
@@ -465,18 +511,6 @@
         r k = fk (map unpackPS (getFragmentStrings k f_keyMember) ++ ss) ((s,Just k):xs)
 -}
 
-data Dest = DestPublic | DestUnknown [String] | DestEncrypted [Key]
-    deriving(Eq,Show)
-
-instance Monoid Dest where
-    mempty = DestEncrypted []
-    DestUnknown a `mappend` DestUnknown b = DestUnknown (a ++ b)
-    DestUnknown a `mappend` _ = DestUnknown a
-    _ `mappend` DestUnknown a  = DestUnknown a
-    DestPublic `mappend` _ = DestPublic
-    _ `mappend` DestPublic = DestPublic
-    DestEncrypted a `mappend` DestEncrypted b = DestEncrypted (a ++ b)
-
 normalizeDest DestPublic = DestPublic
 normalizeDest (DestUnknown xs) = DestUnknown $ snub xs
 normalizeDest (DestEncrypted xs) = DestEncrypted $ snub xs
@@ -577,13 +611,13 @@
     when (n /= 4) $ fail "short read."
     [b1,b2,b3,b4] <- getElems a
     return $ (fromIntegral b4) .|. (fromIntegral b3 `shiftL` 8) .|.
-	     (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
+             (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
 
 galeEncodeString :: String -> [Word8]
 galeEncodeString cs = concatMap (f . ord) (concat $ map (\c -> if c == '\n' then "\r\n" else [c]) cs) where
     f x = (b1:b2:[]) where
-	b1 = fromIntegral $ (x `shiftR` 8) .&. 0xFF
-	b2 = fromIntegral $ x .&. 0xFF
+        b1 = fromIntegral $ (x `shiftR` 8) .&. 0xFF
+        b2 = fromIntegral $ x .&. 0xFF
 
 
 --------------
diff --git a/Gale/KeyCache.hs b/Gale/KeyCache.hs
--- a/Gale/KeyCache.hs
+++ b/Gale/KeyCache.hs
@@ -12,7 +12,10 @@
     putKey,
     noKey,
     numberKeys,
-    keyRequestPuff
+    keyRequestPuff,
+    keyToPkey,
+    getPubKeyBytes,
+    keyResponsePuff
 
     ) where
 
@@ -63,23 +66,29 @@
 data KeyCache = KeyCache {
     pkeyCache :: !(MVar (Map.Map String (Weak (Key,EvpPkey)))),
     kkeyCache :: !(MVar (Map.Map String (Weak Key))),
+    pubkeyCache :: !(MVar (Map.Map String (Weak BS.ByteString))),
     galeDir :: String
     }
 
 numberKeys kc = fmap Map.size $ readMVar (kkeyCache kc)
 
-keyIsPubKey k =  (hasFragment k f_rsaExponent)
+keyIsPubKey :: HasFragmentList a => a -> Bool
+keyIsPubKey k = not (hasFragment k f_rsaPrivateExponent)
+                && hasFragment k f_rsaExponent
+keyIsPrivKey :: HasFragmentList a => a -> Bool
 keyIsPrivKey k =  (hasFragment k f_rsaPrivateExponent)
+keyIsPublic :: HasFragmentList a => a -> Bool
 keyIsPublic k = any nullPS $ getFragmentStrings k f_keyMember
 
 newKeyCache galeDir = do
     pk <- newMVar Map.empty
     kk <- newMVar Map.empty
-    return KeyCache { {- keyCache = kc, publicKeyCache = pkc,-} galeDir = galeDir, pkeyCache = pk, kkeyCache = kk }
+    pbk <- newMVar Map.empty
+    return KeyCache { {- keyCache = kc, publicKeyCache = pkc,-} galeDir = galeDir, pkeyCache = pk, kkeyCache = kk, pubkeyCache = pbk }
 
 keyToRSAElems :: Monad m => Key -> m (RSAElems BS.ByteString)
 keyToRSAElems fl = do
-    if not (keyIsPubKey fl) then fail "key does not have bits" else do
+    if not (keyIsPrivKey fl) then fail "key does not have bits" else do
     n <- getFragmentData fl f_rsaModulus
     e <- getFragmentData fl f_rsaExponent
     if not (keyIsPrivKey fl) then
@@ -185,6 +194,37 @@
                     v' <- mkWeakPtr v' Nothing
                     return (Map.insert kn v' kkeyCache, ())
 
+-- XXX duplication; the key cache should store a ByteString of the pubkey too so
+-- that we don't need this separate function
+getPubKeyBytes :: KeyCache -> String -> IO (Maybe BS.ByteString)
+getPubKeyBytes kc kn = modifyMVar (pubkeyCache kc) f where
+    f pkc | Just v <- Map.lookup kn pkc = do
+      v' <- deRefWeak v
+      case v' of
+        Just _ -> return (pkc, v')
+        Nothing -> g pkc
+    f pkc = g pkc
+    g pkc = do
+      v <- tryIO getFromDisk
+      case v of
+        Left _ -> return (pkc, Nothing)
+        Right v' -> do
+          v'' <- mkWeakPtr v' Nothing
+          return (Map.insert kn v'' pkc, Just v')
+    getFromDisk = do
+        let pp = galeDir kc ++ "/auth/private/"
+            knames = pp ++ kn ++ ".gpub"
+            gf fn = readRawFile fn >>= \xs -> do
+              let pk = BS.pack xs
+                  k = parseKey xs
+              return (fn, pk, k)
+        xs <- tryIO $ gf knames
+        case xs of
+          Left _ -> lose
+          Right (_,bs,mk) | Just _ <- mk -> return bs
+          Right _ -> lose
+      where
+        lose = ioError $ userError "bad key"
 
 getKey :: KeyCache -> String -> IO (Maybe Key)
 getKey kc kn = modifyMVar (kkeyCache kc) f where
@@ -195,7 +235,7 @@
             Nothing -> g kkeyCache
     f kkeyCache = g kkeyCache
     g kkeyCache = do
-	    v <- tryIO getFromDisk
+            v <- tryIO getFromDisk
             case v of
                 Left _ -> return (kkeyCache, Nothing)
                 Right v' -> do
@@ -212,9 +252,9 @@
         let ks = [ k | (_,Just k@(Key n _)) <- xs, kn == n]
         let nk = foldr (\(Key _ fl) (Key n fl') -> Key n (fl `mergeFrags` fl')) (Key kn []) ks
         --(key_c, fn) <- (first  gn)
-	--key <- parseKey key_c
-	--rsa <- pubKeyToRSA key
-	--pkey <- pkeyNewRSA rsa
+        --key <- parseKey key_c
+        --rsa <- pubKeyToRSA key
+        --pkey <- pkeyNewRSA rsa
         if null (getFragmentList nk) then ioError $ userError "bad key" else return nk
 
 flipLocalPart :: String -> String
@@ -231,6 +271,13 @@
     s' = flipLocalPart s
     Category (n,d) = catParseNew s
 
+keyResponsePuff :: Maybe BS.ByteString -> String -> Puff
+keyResponsePuff kb s = emptyPuff { cats = [Category ("_gale.key." ++ n, d)], fragments = [resp]} where
+    resp = case kb of
+      Just x -> (f_answerKey', FragmentData x)
+      Nothing -> (f_answerKeyError', FragmentText $ packString $ "cannot find " ++ s)
+    Category (n,d) = catParseNew s
+
 --la xs = listArray (0, length xs - 1) xs
 --la xs = BS.pack xs
 
@@ -241,7 +288,7 @@
     b3 <- get
     b4 <- get
     return $ (fromIntegral b4) .|. (fromIntegral b3 `shiftL` 8) .|.
-	    (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
+            (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
 
 parseWord16 :: ReadP Word16
 parseWord16 = do
@@ -261,13 +308,13 @@
     privatePrimeExponentD <- splitRLE (galeRSAPrimeLen * 2)
     privateCoefficientD <- splitRLE galeRSAPrimeLen
     let fl = [("rsa.modulus",FragmentData  modulusD),
-	  ("rsa.exponent",FragmentData exponentD),
-	  ("rsa.private.exponent",FragmentData  privateExponentD),
-	  ("rsa.private.prime",FragmentData  privatePrimeD),
-	  ("rsa.private.prime.exponent",FragmentData privatePrimeExponentD),
-	  ("rsa.private.coefficient",FragmentData  privateCoefficientD),
-	  ("rsa.bits", FragmentInt (fromIntegral bits))
-	 ]
+          ("rsa.exponent",FragmentData exponentD),
+          ("rsa.private.exponent",FragmentData  privateExponentD),
+          ("rsa.private.prime",FragmentData  privatePrimeD),
+          ("rsa.private.prime.exponent",FragmentData privatePrimeExponentD),
+          ("rsa.private.coefficient",FragmentData  privateCoefficientD),
+          ("rsa.bits", FragmentInt (fromIntegral bits))
+         ]
     return [ (fromString x,y) | (x,y) <- fl]
     {-
     (bits,ys) = xdrReadUInt xs
@@ -298,60 +345,62 @@
 keyParse :: ReadP Key
 keyParse = choice [ppk1,ppk2,ppk3,pk1,pk2,pk3] where
     ppk1 = do
-	string private_magic1
-	kn <- fmap flipLocalPart parseNullString
-	fl <- keyDecode12
-	return $ Key kn fl
+        string private_magic1
+        kn <- fmap flipLocalPart parseNullString
+        fl <- keyDecode12
+        return $ Key kn fl
     ppk2 = do
-	string private_magic2
-	kn <- fmap flipLocalPart parseLenString
-	fl <- keyDecode12
-	return $ Key kn fl
+        string private_magic2
+        kn <- fmap flipLocalPart parseLenString
+        fl <- keyDecode12
+        return $ Key kn fl
     ppk3 = do
-	string private_magic3
-	kn <- fmap flipLocalPart parseLenString
-	fl <- toParser decodeFrags
-	return $ Key kn fl
+        string private_magic3
+        kn <- fmap flipLocalPart parseLenString
+        fl <- toParser decodeFrags
+        return $ Key kn fl
     pk1 = do
-	string pubkey_magic1
-	kn <- fmap flipLocalPart parseNullString
-	(eof >> return (Key kn [])) +++ do
-	comment <- parseNullString
-	fl <- parsePublic12
-	skipMany get -- the signature
-	return $ Key kn ([(f_keyOwner, FragmentText (packString comment))] ++ fl)
+        string pubkey_magic1
+        kn <- fmap flipLocalPart parseNullString
+        (eof >> return (Key kn [])) +++ do
+        comment <- parseNullString
+        fl <- parsePublic12
+        skipMany get -- the signature
+        return $ Key kn ([(f_keyOwner, FragmentText (packString comment))] ++ fl)
     pk2 = do
-	string pubkey_magic2
-	kn <- fmap flipLocalPart parseLenString
-	(eof >> return (Key kn [])) +++ do
-	comment <- parseLenString
-	fl' <- parsePublic12
-	let fl = ((f_keyOwner, FragmentText (packString comment)):fl')
-	(eof >> return (Key kn fl)) +++ do
-	ts <- replicateM 16 get
-	te <- replicateM 16 get
+        string pubkey_magic2
+        kn <- fmap flipLocalPart parseLenString
+        (eof >> return (Key kn [])) +++ do
+        comment <- parseLenString
+        fl' <- parsePublic12
+        let fl = ((f_keyOwner, FragmentText (packString comment)):fl')
+        (eof >> return (Key kn fl)) +++ do
+        ts <- replicateM 16 get
+        te <- replicateM 16 get
         skipMany get
-	--_signature <- parseRest
-	return $ Key kn $ fl ++ [(f_keySigned,FragmentTime (decodeTime ts)), (f_keyExpires,FragmentTime (decodeTime te))]
+        --_signature <- parseRest
+        return $ Key kn $ fl ++ [(f_keySigned,FragmentTime (decodeTime ts)), (f_keyExpires,FragmentTime (decodeTime te))]
     pk3 = do
-	string pubkey_magic3
-	kn <- fmap flipLocalPart parseLenString
-	fl <- toParser decodeFrags
-	return $ Key kn fl
+        string pubkey_magic3
+        kn <- fmap flipLocalPart parseLenString
+        fl <- toParser decodeFrags
+        return $ Key kn fl
     parsePublic12 = do
-	bits <- parseIntegral32
-	--modulus <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
-	--exponent <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
-	modulus <- splitRLE galeRSAModulusLen
-	exponent <- splitRLE galeRSAModulusLen
-	return [(f_rsaModulus, FragmentData modulus),
-		(f_rsaExponent, FragmentData exponent),
-		(f_rsaBits, FragmentInt $ fromIntegral bits)]
+        bits <- parseIntegral32
+        --modulus <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
+        --exponent <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
+        modulus <- splitRLE galeRSAModulusLen
+        exponent <- splitRLE galeRSAModulusLen
+        return [(f_rsaModulus, FragmentData modulus),
+                (f_rsaExponent, FragmentData exponent),
+                (f_rsaBits, FragmentInt $ fromIntegral bits)]
 
 
-parser :: ReadP a -> BS.ByteString -> Maybe a
+parser :: Show a => ReadP a -> BS.ByteString -> Maybe a
 parser p bs = case readP_to_S p bs of
-    [(a,bs)] | BS.null bs -> Just a
+    x:xs ->
+        let (a,bs) = if null xs then x else last xs in
+        if BS.null bs then Just a else Nothing
     _ -> Nothing
 
 parseKey :: [Word8] -> Maybe Key
@@ -416,9 +465,9 @@
 unsignData xs = do
     key <- parseKey $ take (fromIntegral (l - (8 + sb))) (drop (fromIntegral sb) xs'')
     return (fl,key) where
-	(l,xs') = xdrReadUInt xs
-	(sb,xs'') = xdrReadUInt (drop 4 xs')
-	fl = (decodeFragments $ drop (fromIntegral l + 4) xs')
+        (l,xs') = xdrReadUInt xs
+        (sb,xs'') = xdrReadUInt (drop 4 xs')
+        fl = (decodeFragments $ drop (fromIntegral l + 4) xs')
 
 unsignFragments :: FragmentList -> Maybe FragmentList
 unsignFragments tfl | (xs:_) <- [xs | (n,FragmentData xs) <- tfl, n == f_securitySignature] = do
@@ -450,21 +499,21 @@
 getPrivateKey :: KeyCache -> String -> IO (Maybe (Key,EvpPkey))
 getPrivateKey gc kn = modifyMVar (keyCache gc) f where
     f kc = case [x|x@(Key kn' _,_) <- kc, kn == kn'] of
-	(v:_) -> return (kc,Just v)
-	[] -> do
-	    v <- ioMp getFromDisk
-	    return ((maybeToList v ++ kc),v)
+        (v:_) -> return (kc,Just v)
+        [] -> do
+            v <- ioMp getFromDisk
+            return ((maybeToList v ++ kc),v)
     getFromDisk = do
-	let gd = galeDir gc
-	let p = (gd ++ "/auth/private/")
-	    knames = [p ++ kn, p ++ kn ++ ".gpri"]
-	    gn = (map (\fn -> (readRawFile fn >>= \c -> return (c,fn))) knames)
-	(key_c, fn) <- (first  gn)
-	key <- parseKey key_c
-	rsa <- privkeyToRSA key
-	pkey <- pkeyNewRSA rsa
-	putLog LogNotice $ "Retrieved private key from disk: " ++ fn
-	return (key,pkey)
+        let gd = galeDir gc
+        let p = (gd ++ "/auth/private/")
+            knames = [p ++ kn, p ++ kn ++ ".gpri"]
+            gn = (map (\fn -> (readRawFile fn >>= \c -> return (c,fn))) knames)
+        (key_c, fn) <- (first  gn)
+        key <- parseKey key_c
+        rsa <- privkeyToRSA key
+        pkey <- pkeyNewRSA rsa
+        putLog LogNotice $ "Retrieved private key from disk: " ++ fn
+        return (key,pkey)
 
 
 
@@ -472,7 +521,7 @@
 getPublicKey gc kn = modifyMVar (publicKeyCache gc) f where
     f keyCache | Just v <- lookupFM keyCache kn = return (keyCache, Just v)
     f keyCache = do
-	    v <- ioM getFromDisk
+            v <- ioM getFromDisk
             return $ case v of
                 Nothing -> (keyCache, Nothing)
                 Just v' -> (addToFM keyCache kn v', Just v')
@@ -484,9 +533,9 @@
             gn = (map (\fn -> (readRawFile fn >>= \c -> return (c,fn))) knames)
         putLog LogDebug $ "Looking for: " ++ show  knames
         (key_c, fn) <- (first  gn)
-	key <- parseKey key_c
-	rsa <- pubKeyToRSA key
-	pkey <- pkeyNewRSA rsa
+        key <- parseKey key_c
+        rsa <- pubKeyToRSA key
+        pkey <- pkeyNewRSA rsa
         return (key,pkey)
 -}
 
diff --git a/Gale/Proto.hs b/Gale/Proto.hs
--- a/Gale/Proto.hs
+++ b/Gale/Proto.hs
@@ -56,14 +56,14 @@
 xdrReadUInt :: [Word8] -> ( Word32,[Word8])
 xdrReadUInt (b1:b2:b3:b4:bs) = (x,bs) where
     x = (fromIntegral b4) .|. (fromIntegral b3 `shiftL` 8) .|.
-	    (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
+            (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
 xdrReadUInt bs = error $ "xdrReadUInt " ++ show bs
 
 parseWord32 :: GenParser Word8 Word32
 parseWord32 = do
     [b1,b2,b3,b4] <- parseSome 4
     return $ (fromIntegral b4) .|. (fromIntegral b3 `shiftL` 8) .|.
-	    (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
+            (fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
 
 parseLenData = parseWord32 >>= parseSome
 
@@ -86,10 +86,10 @@
 getGaleDir = do
     gd <- GenUtil.lookupEnv "GALE_DIR"
     case gd of
-	Just v -> return $ v ++ "/"
-	Nothing -> do
-	    h <- getEnv "HOME"
-	    return (h ++ "/.gale/")
+        Just v -> return $ v ++ "/"
+        Nothing -> do
+            h <- getEnv "HOME"
+            return (h ++ "/.gale/")
 
 decodeFrags :: Get FragmentList
 decodeFrags = df [] where
diff --git a/Gale/Puff.hs b/Gale/Puff.hs
--- a/Gale/Puff.hs
+++ b/Gale/Puff.hs
@@ -7,6 +7,7 @@
     FragmentList,
     HasFragmentList(..),
     Category(Category),
+    Dest(..),
     readPuffs, writePuffs, emptyPuff,
     categoryHead, categoryCell,
     catParseNew, catShowNew,
@@ -46,8 +47,9 @@
     ) where
 
 import Atom
-import Data.Binary
 import qualified Control.Exception as E
+import Data.Binary
+import Data.Monoid
 import EIO
 import ErrorLog
 import GenUtil
@@ -55,6 +57,7 @@
 import Data.List
 import Data.Maybe(isJust)
 import PackedString
+import Control.Concurrent.Async (Async)
 import System.IO
 import System.Time
 import RSA
@@ -82,8 +85,21 @@
     | FragmentInt !Int32
     | FragmentNest FragmentList
 
+data Dest = DestPublic | DestUnknown [String] | DestEncrypted [Key]
+    deriving(Eq,Show)
+
+instance Monoid Dest where
+    mempty = DestEncrypted []
+    DestUnknown a `mappend` DestUnknown b = DestUnknown (a ++ b)
+    DestUnknown a `mappend` _ = DestUnknown a
+    _ `mappend` DestUnknown a  = DestUnknown a
+    DestPublic `mappend` _ = DestPublic
+    _ `mappend` DestPublic = DestPublic
+    DestEncrypted a `mappend` DestEncrypted b = DestEncrypted (a ++ b)
+
 data Signature =
-    Unverifyable Key
+    RequestingKey (Async Dest) Key BS.ByteString BS.ByteString
+    | Unverifyable Key
     | Signed Key
     | Encrypted [String]
 
@@ -148,15 +164,22 @@
 getAuthor p = maybe "unknown" id (getSigner p)
 
 getSigner p = ss (signature p) where
-	    ss (Signed (Key n _):_) = Just n
-	    ss (Unverifyable (Key n _):_) = Just n
-	    ss (_:sig) = ss sig
-	    ss [] = Nothing
+            ss (RequestingKey _ (Key n _) _ _:_) = Just n
+            ss (Signed (Key n _):_) = Just n
+            ss (Unverifyable (Key n _):_) = Just n
+            ss (_:sig) = ss sig
+            ss [] = Nothing
 
-showSignature (Signed (Key n _)) = "Signed by " ++ n
-showSignature (Unverifyable (Key n _)) = "Signed by " ++ n ++ " (unverified)"
-showSignature (Encrypted xs) = "Encrypted to " ++ concatInter ", " xs
+unverifiableStr :: Key -> String
+unverifiableStr (Key n _) = "Signed by " ++ n ++ " (unverified)"
 
+showSignature :: Signature -> String
+showSignature s = case s of
+  Signed (Key n _) -> "Signed by " ++ n
+  RequestingKey _ k _ _ -> unverifiableStr k
+  Unverifyable k -> unverifiableStr k
+  Encrypted xs -> "Encrypted to " ++ concatInter ", " xs
+
 emptyPuff = Puff {cats = [], signature = [], fragments = []}
 
 fragmentData :: Monad m => Atom -> FragmentList -> m BS.ByteString
@@ -288,9 +311,9 @@
 
 instance Data.Binary.Binary Puff where
     put (Puff aa ab ac) = do
-	    Data.Binary.put aa
-	    Data.Binary.put ab
-	    Data.Binary.put ac
+            Data.Binary.put aa
+            Data.Binary.put ab
+            Data.Binary.put ac
     get = do
     aa <- get
     ab <- get
@@ -299,68 +322,73 @@
 
 instance Data.Binary.Binary Fragment where
     put (FragmentText aa) = do
-	    Data.Binary.putWord8 0
-	    Data.Binary.put aa
+            Data.Binary.putWord8 0
+            Data.Binary.put aa
     put (FragmentData ab) = do
-	    Data.Binary.putWord8 1
-	    Data.Binary.put ab
+            Data.Binary.putWord8 1
+            Data.Binary.put ab
     put (FragmentTime ac) = do
-	    Data.Binary.putWord8 2
-	    Data.Binary.put ac
+            Data.Binary.putWord8 2
+            Data.Binary.put ac
     put (FragmentInt ad) = do
-	    Data.Binary.putWord8 3
-	    Data.Binary.put ad
+            Data.Binary.putWord8 3
+            Data.Binary.put ad
     put (FragmentNest ae) = do
-	    Data.Binary.putWord8 4
-	    Data.Binary.put ae
+            Data.Binary.putWord8 4
+            Data.Binary.put ae
     get = do
-	    h <- Data.Binary.getWord8
-	    case h of
-	      0 -> do
-		    aa <- Data.Binary.get
-		    return (FragmentText aa)
-	      1 -> do
-		    ab <- Data.Binary.get
-		    return (FragmentData ab)
-	      2 -> do
-		    ac <- Data.Binary.get
-		    return (FragmentTime ac)
-	      3 -> do
-		    ad <- Data.Binary.get
-		    return (FragmentInt ad)
-	      4 -> do
-		    ae <- Data.Binary.get
-		    return (FragmentNest ae)
-	      _ -> fail "invalid binary data found"
+            h <- Data.Binary.getWord8
+            case h of
+              0 -> do
+                    aa <- Data.Binary.get
+                    return (FragmentText aa)
+              1 -> do
+                    ab <- Data.Binary.get
+                    return (FragmentData ab)
+              2 -> do
+                    ac <- Data.Binary.get
+                    return (FragmentTime ac)
+              3 -> do
+                    ad <- Data.Binary.get
+                    return (FragmentInt ad)
+              4 -> do
+                    ae <- Data.Binary.get
+                    return (FragmentNest ae)
+              _ -> fail "invalid binary data found"
 
 instance Data.Binary.Binary Signature where
+    -- we do not differentiate between signatures that were in mid-flight fetch
+    -- and signatures that could not be verified
+    put (RequestingKey _ aa _ _) = do
+            Data.Binary.putWord8 0
+            Data.Binary.put aa
     put (Unverifyable aa) = do
-	    Data.Binary.putWord8 0
-	    Data.Binary.put aa
+            Data.Binary.putWord8 0
+            Data.Binary.put aa
     put (Signed ab) = do
-	    Data.Binary.putWord8 1
-	    Data.Binary.put ab
+            Data.Binary.putWord8 1
+            Data.Binary.put ab
     put (Encrypted ac) = do
-	    Data.Binary.putWord8 2
-	    Data.Binary.put ac
+            Data.Binary.putWord8 2
+            Data.Binary.put ac
     get = do
-	    h <- Data.Binary.getWord8
-	    case h of
-	      0 -> do
-		    aa <- Data.Binary.get
-		    return (Unverifyable aa)
-	      1 -> do
-		    ab <- Data.Binary.get
-		    return (Signed ab)
-	      2 -> do
-		    ac <- Data.Binary.get
-		    return (Encrypted ac)
-	      _ -> fail "invalid binary data found"
+            h <- Data.Binary.getWord8
+            case h of
+              0 -> do
+                    aa <- Data.Binary.get
+                    return (Unverifyable aa)
+              1 -> do
+                    ab <- Data.Binary.get
+                    return (Signed ab)
+              2 -> do
+                    ac <- Data.Binary.get
+                    return (Encrypted ac)
+              _ -> fail "invalid binary data found"
 
 instance Data.Binary.Binary Key where
     put (Key aa ab) = do
-	    Data.Binary.put aa
-	    Data.Binary.put ab
+            Data.Binary.put aa
+            Data.Binary.put ab
     get = do
     aa <- get
     ab <- get
diff --git a/GenUtil.hs b/GenUtil.hs
--- a/GenUtil.hs
+++ b/GenUtil.hs
@@ -121,8 +121,8 @@
 import Control.Monad
 import qualified System.IO as IO
 import qualified System.IO.Error as IO
-import qualified System.Exit as System 
-import qualified System.Environment as System 
+import qualified System.Exit as System
+import qualified System.Environment as System
 import System.Random(StdGen, newStdGen, Random(randomR))
 import System.Time
 import System.CPUTime
diff --git a/GinsuConfig.hs b/GinsuConfig.hs
--- a/GinsuConfig.hs
+++ b/GinsuConfig.hs
@@ -54,20 +54,20 @@
 
 
     mapM_ putStrLn $ buildTableLL [
-	( "GALE_DIR ",  galeDir),
-	( "GALE_DOMAIN ", galeDomain),
-	( "GALE_ID ", gid),
-	( "GALE_PROXY ", simpleQuote gp),
-	( "GALE_SUBSCRIBE ", unwords (snub galeSubscribe)) ]
+        ( "GALE_DIR ",  galeDir),
+        ( "GALE_DOMAIN ", galeDomain),
+        ( "GALE_ID ", gid),
+        ( "GALE_PROXY ", simpleQuote gp),
+        ( "GALE_SUBSCRIBE ", unwords (snub galeSubscribe)) ]
     when (galeAliases /= []) $ putStrLn $ "GALE_ALIASES \n" ++
-	unlines ( map  (\ (l,cat) -> "  " ++ l ++ " -> " ++ show cat)  galeAliases)
+        unlines ( map  (\ (l,cat) -> "  " ++ l ++ " -> " ++ show cat)  galeAliases)
 
     let p = (galeDir ++ "/auth/private/")
-	knames = [p ++ gid, p ++ gid ++ ".gpri"]
+        knames = [p ++ gid, p ++ gid ++ ".gpri"]
     gn <- (mapM (\fn -> (doesFileExist fn >>= \b -> if b then return (Just fn) else return Nothing)) knames)
     case msum gn of
-	Just fn -> putStrLn $ "Private key found in: " ++ fn
-	Nothing -> putStrLn $ "Private key for '" ++ gid ++ "' not found in " ++ p
+        Just fn -> putStrLn $ "Private key found in: " ++ fn
+        Nothing -> putStrLn $ "Private key for '" ++ gid ++ "' not found in " ++ p
     putStrLn ""
     --putStrLn showKeyInfo
     --kt <- buildKeyTable
@@ -87,12 +87,12 @@
 getGaleProxy = do
     gps <- configLookup "GALE_PROXY"
     case gps of
-	Nothing -> do
-	    v <- configLookup "GALE_DOMAIN"
+        Nothing -> do
+            v <- configLookup "GALE_DOMAIN"
             case v of
                 Just v -> return $ hostStrings v
                 Nothing -> return []
-	(Just x) -> return (words x)
+        (Just x) -> return (words x)
 
 
 getGaleId :: IO String
@@ -118,10 +118,10 @@
     fs <- handle (\(_ :: IOException) -> return []) $ getDirectoryContents v
     --putLog LogDebug $ "aliases/ " ++ show fs
     let f fn = do
-	--fc <- first [readFile (v ++ fn), readSymbolicLink (v ++ fn)]
+        --fc <- first [readFile (v ++ fn), readSymbolicLink (v ++ fn)]
         fc <- readAlias (v ++ fn)
         --putLog LogDebug $ "readAlias " ++ (v ++ fn) ++ " " ++ fc
-	return (fn, catParseNew fc)
+        return (fn, catParseNew fc)
     tryMapM f fs
 
 
diff --git a/Help.hs b/Help.hs
--- a/Help.hs
+++ b/Help.hs
@@ -62,12 +62,12 @@
 
 filterExamples = "Filter Examples:\n"  ++ indentLines 4 (unlines es) where
     es = [
-	"(~a:john@ugcs.caltech.edu)\n  all puffs by john@ugcs",
-	"(~c:pub.tv.buffy ~a:jtr@ofb.net)\n  puffs from jtr and to pub.tv.buffy",
-	"(/ginsu ; ~c:pub.gale.ginsu)\n  puffs containing the word ginsu or directed to pub.comp.ginsu",
-	"(!~k:spoil)\n  no spoilers",
-	"(~c:pub.tv.buffy !~c:pub.meow)\n  puffs to buffy which are not about cats"
-	]
+        "(~a:john@ugcs.caltech.edu)\n  all puffs by john@ugcs",
+        "(~c:pub.tv.buffy ~a:jtr@ofb.net)\n  puffs from jtr and to pub.tv.buffy",
+        "(/ginsu ; ~c:pub.gale.ginsu)\n  puffs containing the word ginsu or directed to pub.comp.ginsu",
+        "(!~k:spoil)\n  no spoilers",
+        "(~c:pub.tv.buffy !~c:pub.meow)\n  puffs to buffy which are not about cats"
+        ]
 
 {-
 
diff --git a/KeyName.hs b/KeyName.hs
--- a/KeyName.hs
+++ b/KeyName.hs
@@ -24,8 +24,8 @@
 --stringToKeys ('<':c:'-':x:'>':cs) | c `elem` "cC" = KeyChar (ord (toLower x) - 0x40) : stringToKeys cs
 stringToKeys ('<':cs) = let (n,r) = span (/= '>') cs in
      case lookup ((map toLower $ filter (not . isSpace) n)) ftl of
-	Just k -> k
-	Nothing -> KeyUnknown 0
+        Just k -> k
+        Nothing -> KeyUnknown 0
       : stringToKeys (drop 1 r)
 stringToKeys (c:cs) = KeyChar c : stringToKeys cs
 
@@ -79,7 +79,7 @@
 
 kg :: [[Key]]
 kg = transitiveGroup $ map (snub . map snd) (groupBy (\(x,_) (y,_) -> x == y)
-	(sort ftl))
+        (sort ftl))
 
 lkg :: Key -> [Key]
 lkg k = case lookup k kgm of
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -75,19 +75,19 @@
     withErrorMessage bugMsg $ do
 
     logname <- case envErrorLog flags of
-	Just x -> return x
-	Nothing -> galeFile "ginsu.errorlog"
+        Just x -> return x
+        Nothing -> galeFile "ginsu.errorlog"
 
     withErrorLog logname $ withStartEndEntrys "Ginsu" $ do
 
     case (envVerbose flags) of
-	1 -> do
-	    setLogLevel LogInfo
-	    putLog LogNotice $ "Verbosity level: Info"
-	n | n > 1 -> do
-	    setLogLevel LogDebug
-	    putLog LogNotice $ "Verbosity level: Debug"
-	_ -> return ()
+        1 -> do
+            setLogLevel LogInfo
+            putLog LogNotice $ "Verbosity level: Info"
+        n | n > 1 -> do
+            setLogLevel LogDebug
+            putLog LogNotice $ "Verbosity level: Debug"
+        _ -> return ()
 
     cs <- configLookup "CHARSET"
 
@@ -119,7 +119,7 @@
     putLog LogInfo $ "GALE_ID " ++ gid
     putLog LogInfo $ "GALE_PROXY " ++ simpleQuote gp
     putLog LogInfo $ "GALE_ALIASES \n" ++
-	unlines ( map  (\(l,cat) -> "  " ++ l ++ " -> " ++ show cat)  galeAliases)
+        unlines ( map  (\(l,cat) -> "  " ++ l ++ " -> " ++ show cat)  galeAliases)
 
 
     Control.Exception.bracket_ initCurses endWin $ do
@@ -136,8 +136,8 @@
     Posix.installHandler Posix.sigINT Posix.Ignore Nothing
     Posix.installHandler Posix.sigPIPE Posix.Ignore Nothing
     case (b,cursesSigWinch) of
-	(False,Just s) -> Posix.installHandler s (Posix.Catch (resizeRenderContext redraw_r (writeChan ic (MainEventKey KeyResize)))) Nothing >> return ()
-	_ -> return ()
+        (False,Just s) -> Posix.installHandler s (Posix.Catch (resizeRenderContext redraw_r (writeChan ic (MainEventKey KeyResize)))) Nothing >> return ()
+        _ -> return ()
 
     gs <- fmap (concat . map words) $ configLookupList "GALE_SUBSCRIBE"
     gps <- getGaleProxy
@@ -154,6 +154,7 @@
     galeAddCategories gc $ map catParseNew (snub $ c ++ nc)
     Category (name,domain) <- fmap catParseNew getGaleId
     galeAddCategories gc $ [Category ("_gale.rr." ++ name ,domain)]
+    galeAddCategories gc $ [Category ("_gale.query." ++ name, domain)]
     doRender widgetEmpty
     pl <- forkIO $ puffLoop ic gc
     gl <- forkIO $ getchLoop ic
@@ -182,9 +183,9 @@
     n' <- readVal pcount_r
     if n' /= n
         then do
-	    ps <- liftM (map snd) $ readVal ps_r
-	    writePufflog ps
-	    pufflogLoop ps_r pcount_r n'
+            ps <- liftM (map snd) $ readVal ps_r
+            writePufflog ps
+            pufflogLoop ps_r pcount_r n'
         else pufflogLoop ps_r pcount_r n
 
 writePufflog ps = do
@@ -208,9 +209,9 @@
     as <- getGaleAliases
     gd <- getGaleDomain
     let ec x@(Category (c,"")) = de $ head $ [Category (tc ++ drop (length f) c,td) | (f,Category (tc,td)) <- as, f `isPrefixOf` c] ++ [x]
-	ec x = x
-	de (Category (c,"")) = Category (c,gd)
-	de x = x
+        ec x = x
+        de (Category (c,"")) = Category (c,gd)
+        de x = x
     return $ map ec cats
 
 {-
@@ -268,15 +269,15 @@
     u <- newUnique
     gn <- configLookup "GALE_NAME"
     n <- case gn of
-	Just n -> return ((f_messageSender,FragmentText (packString n)):)
-	Nothing -> return id
+        Just n -> return ((f_messageSender,FragmentText (packString n)):)
+        Nothing -> return id
     let mid = sha1 . LBS.pack $ map (fromIntegral . ord) (show t ++ " " ++ idText ++ " " ++ show (hashUnique u))
     let sig = [Signed (Key gi [])]
     let ef = n [
-	    (f_messageId, FragmentText $ packString $ bsToHex mid),
-	    (fromString "id/class",FragmentText $ packString (package ++ "/" ++ version) ),
-	    (fromString "id/instance", FragmentText $ packString idText),
-	    (f_idTime,FragmentTime t)]
+            (f_messageId, FragmentText $ packString $ bsToHex mid),
+            (fromString "id/class",FragmentText $ packString (package ++ "/" ++ version) ),
+            (fromString "id/instance", FragmentText $ packString idText),
+            (f_idTime,FragmentTime t)]
     return emptyPuff {fragments = ef, signature = sig}
 
 
@@ -371,7 +372,7 @@
     icmain <- newChan
 
     let fsw = newSVarWidget filter_r $ \fs ->
-	    widgetHorizontalBox False (intersperse (NoExpand, widgetText " ") $ map (\w -> (NoExpand,widgetAttr [AttrReverse] (widgetText w))) (map showFilter fs))
+            widgetHorizontalBox False (intersperse (NoExpand, widgetText " ") $ map (\w -> (NoExpand,widgetAttr [AttrReverse] (widgetText w))) (map showFilter fs))
 
     selPuff <- newCacheIO $ do
         ps <-  readVal psr
@@ -406,9 +407,9 @@
                 Right _ -> id
                 Left s -> \w -> widgetVerticalBox False [ne w, (NoExpand,widgetAttr [AttrReverse] (widgetText $ "*** " ++ s ++ " ***"))]
         return $ f $ widgetHorizontalBox False [ne m, (Expand, widgetEmpty), wt "Workspace:", ne ws ,wt " ", ne s, wt " (", ne nf , wt "/", ne pc , wt ") " ]
-    let	keyError s k = messageBox rc fw (s ++ ": " ++ keysToString [k] ++ " -- " ++ helpText) >> return ()
+    let keyError s k = messageBox rc fw (s ++ ": " ++ keysToString [k] ++ " -- " ++ helpText) >> return ()
         setMessage m = messageBox rc fw m >> return ()
-	scrollPuffs x = mapVal yor (+ x)
+        scrollPuffs x = mapVal yor (+ x)
         autoSaveMark = do
             n <- readVal (gsWorkspace gs)
             s <- readVal selected_r
@@ -423,25 +424,25 @@
             maybe (return ()) (writeVal selected_r) s
             select_perhaps select_next
             center_puff
-	filter_thread = do
-	    p <-  selPuff
-	    case p of
-		Just (Puff {cats = [c]}) -> modifyFilter (BoolJust (FilterCategory c):)
-		Just (Puff {cats = cs}) -> modifyFilter (or (map (BoolJust . FilterCategory) cs):)
-		_ -> return ()
+        filter_thread = do
+            p <-  selPuff
+            case p of
+                Just (Puff {cats = [c]}) -> modifyFilter (BoolJust (FilterCategory c):)
+                Just (Puff {cats = cs}) -> modifyFilter (or (map (BoolJust . FilterCategory) cs):)
+                _ -> return ()
 
-	addPuff p = do
+        addPuff p = do
             let author = getAuthor p
             Category (name,domain) <- fmap catParseNew getGaleId
-	    case getFragmentString p (f_noticePresence') of
-		Nothing -> return ()
-		Just pn -> do
-		    let np@(a',(b',_)) = (author, (maybe "unknown" unpackPS (getFragmentString p (fromString "id/instance")), unpackPS pn))
-		    mapVal presence_r (\xs -> (np:[x | x@(a,(b,_)) <- xs, a /= a' || b /= b']))
-	    case getFragmentTime p (fromString "status.idle") of
-		Nothing -> return ()
+            case getFragmentString p (f_noticePresence') of
+                Nothing -> return ()
+                Just pn -> do
+                    let np@(a',(b',_)) = (author, (maybe "unknown" unpackPS (getFragmentString p (fromString "id/instance")), unpackPS pn))
+                    mapVal presence_r (\xs -> (np:[x | x@(a,(b,_)) <- xs, a /= a' || b /= b']))
+            case getFragmentTime p (fromString "status.idle") of
+                Nothing -> return ()
                 Just pn | pn == epoch -> Hash.delete idleHash author >> getClockTime >>=  Hash.insert idleHash author
-		Just pn -> do
+                Just pn -> do
                     mt <- Hash.lookup idleHash author
                     Hash.delete idleHash author
                     Hash.insert idleHash author $ fromJust $ max mt (Just pn)
@@ -453,136 +454,136 @@
                 _ -> return ()
 
 
-	    case getFragmentString p (f_questionReceipt) of
-		Nothing -> return ()
-		Just _ -> buildReciept p >>= \p -> forkIO (galeSendPuff gc p) >> return ()
-	    case fmap unpackPS $ getFragmentString p f_messageBody of
-		Nothing -> return ()
-		Just n -> when (not $ (all isSpace n) && (all (("_gale" `isPrefixOf`) . categoryHead) (cats p))) $ do
+            case getFragmentString p (f_questionReceipt) of
+                Nothing -> return ()
+                Just _ -> buildReciept p >>= \p -> forkIO (galeSendPuff gc p) >> return ()
+            case fmap unpackPS $ getFragmentString p f_messageBody of
+                Nothing -> return ()
+                Just n -> when (not $ (all isSpace n) && (all (("_gale" `isPrefixOf`) . categoryHead) (cats p))) $ do
                     mt <- Hash.lookup idleHash author
                     pn <- getClockTime
                     Hash.insert idleHash author $ fromJust $ max mt (Just pn)
-		    v <- configLookupList "BEEP"
-		    let f = or (rights (map parseFilter v))
-		    when (filterAp f p) Curses.beep
-		    n <- readVal next_r
-		    mapVal next_r (+1)
-		    mapVal pcount_r (+1)
-		    mapVal psr (\ps -> ((n,p):ps))
+                    v <- configLookupList "BEEP"
+                    let f = or (rights (map parseFilter v))
+                    when (filterAp f p) Curses.beep
+                    n <- readVal next_r
+                    mapVal next_r (+1)
+                    mapVal pcount_r (+1)
+                    mapVal psr (\ps -> ((n,p):ps))
                     sbsize <- fmap (read . fromJust) $ configLookup "SCROLLBACK_SIZE"
-		    case sbsize of
-		        0 -> return ()
+                    case sbsize of
+                        0 -> return ()
                         _ -> do
                             let keep = min n sbsize
                             mapVal psr (\ps -> zip [keep, (keep - 1) .. 1] (snds $ take keep ps))
                             mapVal selected_r (\s -> s - (n - keep))
                             mapVal next_r (\s -> s - (n - keep))
-		    select_perhaps select_next
-		    --touchRenderContext rc
-	composePuff :: IO () -> [Category] -> [PackedString] -> IO ()
-	composePuff done cs kwds = do
-	    p <- puffTemplate
-	    editPuff (p {cats = cs, fragments = fragments p ++ [ (f_messageKeyword,FragmentText k) | k <- kwds]}) ic done
-	getPresenceFrags = do
-	    mp <- readVal myPresence
-	    (TOD ct _) <- getClockTime
-	    uaT@(TOD uat _) <- readVal userActionTime
-	    (it,is,notIdle) <- return $ case ct - uat of
-		n | n < idleThreshold -> (epoch," (not idle)",True)
-		n -> (uaT," (" ++ (showDuration n) ++ " idle)",False)
-	    return ([
-		    (f_noticePresence',FragmentText (packString (mp ++ is))),
-		    (fromString "status.presence", FragmentText $ packString mp),
-		    (fromString "status.idle", FragmentTime it)],notIdle)
-	buildReciept p = do
-	    np <- puffTemplate
-	    r <- getFragmentString p (fromString "question.receipt")
-	    gid <- getGaleId
-	    let rid = maybe [] (\x -> [(fromString "receipt.id",FragmentText x)]) $ getFragmentString p (f_messageId)
-	    (pf,_) <- getPresenceFrags
-	    return $ np { cats = [catParseNew (unpackPS r)], fragments = fragments np ++ pf ++ rid ++ [(fromString "answer.receipt",FragmentText $ packString gid)]}
-	presenceLoop = do
-	    let loopIsIdle ct = do
+                    select_perhaps select_next
+                    --touchRenderContext rc
+        composePuff :: IO () -> [Category] -> [PackedString] -> IO ()
+        composePuff done cs kwds = do
+            p <- puffTemplate
+            editPuff (p {cats = cs, fragments = fragments p ++ [ (f_messageKeyword,FragmentText k) | k <- kwds]}) ic done
+        getPresenceFrags = do
+            mp <- readVal myPresence
+            (TOD ct _) <- getClockTime
+            uaT@(TOD uat _) <- readVal userActionTime
+            (it,is,notIdle) <- return $ case ct - uat of
+                n | n < idleThreshold -> (epoch," (not idle)",True)
+                n -> (uaT," (" ++ (showDuration n) ++ " idle)",False)
+            return ([
+                    (f_noticePresence',FragmentText (packString (mp ++ is))),
+                    (fromString "status.presence", FragmentText $ packString mp),
+                    (fromString "status.idle", FragmentTime it)],notIdle)
+        buildReciept p = do
+            np <- puffTemplate
+            r <- getFragmentString p (fromString "question.receipt")
+            gid <- getGaleId
+            let rid = maybe [] (\x -> [(fromString "receipt.id",FragmentText x)]) $ getFragmentString p (f_messageId)
+            (pf,_) <- getPresenceFrags
+            return $ np { cats = [catParseNew (unpackPS r)], fragments = fragments np ++ pf ++ rid ++ [(fromString "answer.receipt",FragmentText $ packString gid)]}
+        presenceLoop = do
+            let loopIsIdle ct = do
                     ct' <- waitJVarEq userActionTime ct
                     notIdle <- sendPresence True
                     if notIdle then loopNotIdle else loopIsIdle ct'
-		loopNotIdle = do
-		    notIdle <- sendPresence False
-		    threadDelay $ (fromIntegral idleThreshold) * 1000000
-		    if notIdle then loopNotIdle else readVal userActionTime >>= loopIsIdle
-	    sendPresence True
-	    loopNotIdle
-	sendPresence sendIfNotIdle = do
+                loopNotIdle = do
+                    notIdle <- sendPresence False
+                    threadDelay $ (fromIntegral idleThreshold) * 1000000
+                    if notIdle then loopNotIdle else readVal userActionTime >>= loopIsIdle
+            sendPresence True
+            loopNotIdle
+        sendPresence sendIfNotIdle = do
             notifyCategory <- getMyNotifyCategory
-	    (pf,notIdle) <- getPresenceFrags
-	    when (sendIfNotIdle || not notIdle) $ do
-		p <- puffTemplate
-		galeSendPuff gc $ p {cats = [notifyCategory], fragments = fragments p ++ pf}
-	    return notIdle
-	gonePresencePuff = do
+            (pf,notIdle) <- getPresenceFrags
+            when (sendIfNotIdle || not notIdle) $ do
+                p <- puffTemplate
+                galeSendPuff gc $ p {cats = [notifyCategory], fragments = fragments p ++ pf}
+            return notIdle
+        gonePresencePuff = do
             notifyCategory <- getMyNotifyCategory
-	    p <- puffTemplate
-	    let ef = [(f_noticePresence' ,FragmentText (toPackedString f_outGone)), (f_noticePresence, FragmentText (toPackedString f_outGone))]
-	    return $ p {cats = [notifyCategory], fragments = fragments p ++ ef }
-	modifyFilter f = do
-	    mapVal filter_r f
-	    select_perhaps select_next >> select_perhaps select_prev
-	    center_puff
-	center_puff = do
-	    (ys,_) <- scrSize
-	    selected <- readVal selected_r
-	    ps <- liftM reverse getFilteredPuffs
-	    let f t (((n,_),h):_) | n == selected = (t,t+h)
-		f t ((_,h):rest) = f (t + h) rest
-		f _ [] = (1000000,0)
-	    let (mn,mx) = (f 0) ps
-	    mapVal yor (min mn)
-	    sh <- statusHeight
-	    mapVal yor (max (mx - (ys - sh) ))
-	is_at_end = do
-	    (ys,_) <- scrSize
-	    bs <- buf_size
-	    sh <- statusHeight
-	    fmap ((max (bs - (ys - sh)) 0) ==) $ readVal yor
+            p <- puffTemplate
+            let ef = [(f_noticePresence' ,FragmentText (toPackedString f_outGone)), (f_noticePresence, FragmentText (toPackedString f_outGone))]
+            return $ p {cats = [notifyCategory], fragments = fragments p ++ ef }
+        modifyFilter f = do
+            mapVal filter_r f
+            select_perhaps select_next >> select_perhaps select_prev
+            center_puff
+        center_puff = do
+            (ys,_) <- scrSize
+            selected <- readVal selected_r
+            ps <- liftM reverse getFilteredPuffs
+            let f t (((n,_),h):_) | n == selected = (t,t+h)
+                f t ((_,h):rest) = f (t + h) rest
+                f _ [] = (1000000,0)
+            let (mn,mx) = (f 0) ps
+            mapVal yor (min mn)
+            sh <- statusHeight
+            mapVal yor (max (mx - (ys - sh) ))
+        is_at_end = do
+            (ys,_) <- scrSize
+            bs <- buf_size
+            sh <- statusHeight
+            fmap ((max (bs - (ys - sh)) 0) ==) $ readVal yor
         scroll_end = do
-	    (ys,_) <- scrSize
-	    bs <- buf_size
-	    sh <- statusHeight
-	    writeVal yor (max (bs - (ys - sh)) 0)
-	doRedraw _ (xs,ys) = do
-	    bs <- buf_size
-	    sh <- statusHeight
-	    mapVal yor (min (max 0 (bs - (ys - sh))))
-	    selected <- readVal selected_r
-	    ps <- getFilteredPuffs
-	    yo <- readVal yor
-	    isRot13 <- readVal rot13_r
-	    let rp _ [] =  return ()
-		rp y (((i,p),n):rest) = do
-		    when ((0,ys) `overlaps` (y - yo, y - yo + n)) $ renderPuff p xs stdScr (y - yo) (xs,ys) (i == selected) (if isRot13 then rot13 else id)
-		    rp (y + n) rest
-	    attemptIO (rp 0 (reverse ps))
-	select_perhaps a = do
-	    ps <- getFilteredPuffs
-	    n <- readVal selected_r
-	    unless (maybe False (const True) $ lookup n (fsts ps)) a
-	select_prev = do
-	    ps <- liftM fsts getFilteredPuffs
-	    let f sel ((n,_):_) | n < sel = n
-		f sel (_:ps) = f sel ps
-		f sel [] = sel
-	    mapVal selected_r (\s ->(f s ps))
-	select_next = do
-	    ps <- liftM fsts getFilteredPuffs
-	    let f sel _ ((n,_):ps) | n > sel = f sel n ps
-		f sel bg ((n,_):ps) | n > sel = f sel bg ps
-		f _ bg _ = bg
-	    mapVal selected_r (\s ->(f s s ps))
+            (ys,_) <- scrSize
+            bs <- buf_size
+            sh <- statusHeight
+            writeVal yor (max (bs - (ys - sh)) 0)
+        doRedraw _ (xs,ys) = do
+            bs <- buf_size
+            sh <- statusHeight
+            mapVal yor (min (max 0 (bs - (ys - sh))))
+            selected <- readVal selected_r
+            ps <- getFilteredPuffs
+            yo <- readVal yor
+            isRot13 <- readVal rot13_r
+            let rp _ [] =  return ()
+                rp y (((i,p),n):rest) = do
+                    when ((0,ys) `overlaps` (y - yo, y - yo + n)) $ renderPuff p xs stdScr (y - yo) (xs,ys) (i == selected) (if isRot13 then rot13 else id)
+                    rp (y + n) rest
+            attemptIO (rp 0 (reverse ps))
+        select_perhaps a = do
+            ps <- getFilteredPuffs
+            n <- readVal selected_r
+            unless (maybe False (const True) $ lookup n (fsts ps)) a
+        select_prev = do
+            ps <- liftM fsts getFilteredPuffs
+            let f sel ((n,_):_) | n < sel = n
+                f sel (_:ps) = f sel ps
+                f sel [] = sel
+            mapVal selected_r (\s ->(f s ps))
+        select_next = do
+            ps <- liftM fsts getFilteredPuffs
+            let f sel _ ((n,_):ps) | n > sel = f sel n ps
+                f sel bg ((n,_):ps) | n > sel = f sel bg ps
+                f _ bg _ = bg
+            mapVal selected_r (\s ->(f s s ps))
 
         fw = widgetVerticalBox False [(ExpandFill, mwidget), (NoExpand, widgetAttr [AttrBold] sb), (NoExpand, fsw)]
-	mwidget = widgetEmpty { render = rnd,  processKey = pk}
-	rnd canvas = doRedraw (origin canvas) (bounds canvas)
-	continue = return True
+        mwidget = widgetEmpty { render = rnd,  processKey = pk}
+        rnd canvas = doRedraw (origin canvas) (bounds canvas)
+        continue = return True
         keyTable' = (map (\(a,b) -> (a, perform_action b)) keyTable)
         pk x = case lookup x keyTable' of
             Nothing -> case x of
@@ -762,53 +763,53 @@
                             pcw <- puffConfirm gc (setRenderWidget rc fw) p ic
                             setRenderWidget rc pcw
                     continue
-		"redraw_screen" -> attemptIO (touchWin stdScr)  >> resizeRenderContext rc (return ()) >> continue
-		"show_help_screen" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) hw) >> continue
-		"show_status_screen" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) statusWidget) >> continue
-		"show_presence_status" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) presenceWidget) >> continue
+                "redraw_screen" -> attemptIO (touchWin stdScr)  >> resizeRenderContext rc (return ()) >> continue
+                "show_help_screen" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) hw) >> continue
+                "show_status_screen" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) statusWidget) >> continue
+                "show_presence_status" -> setRenderWidget rc (keyCatcherWidget (\_ -> setRenderWidget rc fw >> return True) presenceWidget) >> continue
                 act -> setMessage ("Unknown action: " ++ act ++ " -- " ++ helpText) >> continue
-	puffDetailsWidget p = do
-	    (_,xs) <- scrSize
-	    let pv = newSVarWidget presence_r (presenceViewUser (getAuthor p))
+        puffDetailsWidget p = do
+            (_,xs) <- scrSize
+            let pv = newSVarWidget presence_r (presenceViewUser (getAuthor p))
             wr <- case fmap unpackPS (getFragmentString p (f_messageId)) of
                     Nothing -> return ""
                     Just mid ->  Hash.lookup puffRRHash mid >>= \ml -> case ml of
                         Nothing -> return ""
                         Just ml -> return $ "\nReceieved by:\n" ++ unlines (snub ml)
-	    v <- widgetScroll (widgetVerticalBox False [(NoExpand,widgetText $ chunkText (xs - 1) ((showPuff p) ++ wr)), (NoExpand, widgetText "\n\n\n"), (NoExpand, pv)])
-	    return v
-	justGetKey = readChan ic >>= \v -> case v of
-	    (MainEventPuff p) -> addPuff p >> justGetKey
-	    (MainEventKey k) -> do
-		    getClockTime >>= writeVal userActionTime
-		    return $ keyCanon k
-	    _ -> writeChan icmain v >> justGetKey
-	nextKey = do
-	    ce <- isEmptyChan ic
-	    when ce $ tryDrawRenderContext rc
-	    cemain <- isEmptyChan icmain
-	    v <- if cemain then readChan ic else readChan icmain
-	    case v of
-		(MainEventPuff p) -> do
+            v <- widgetScroll (widgetVerticalBox False [(NoExpand,widgetText $ chunkText (xs - 1) ((showPuff p) ++ wr)), (NoExpand, widgetText "\n\n\n"), (NoExpand, pv)])
+            return v
+        justGetKey = readChan ic >>= \v -> case v of
+            (MainEventPuff p) -> addPuff p >> justGetKey
+            (MainEventKey k) -> do
+                    getClockTime >>= writeVal userActionTime
+                    return $ keyCanon k
+            _ -> writeChan icmain v >> justGetKey
+        nextKey = do
+            ce <- isEmptyChan ic
+            when ce $ tryDrawRenderContext rc
+            cemain <- isEmptyChan icmain
+            v <- if cemain then readChan ic else readChan icmain
+            case v of
+                (MainEventPuff p) -> do
                     v <- is_at_end
-		    addPuff p
+                    addPuff p
                     when v scroll_end
-		    s <- configLookupElse "ON_INCOMING_PUFF" ""
-		    mapM_ (processKey mwidget) (stringToKeys s)
-		    nextKey
-		(MainEventKey KeyResize) ->  nextKey
-		(MainEventKey k) -> do
-		    getClockTime >>= writeVal userActionTime
-		    b <- keyRenderContext rc $ keyCanon k
-		    if b then nextKey else return ()
-		(MainEventComposed ep done) -> do
-		    case ep of
-			Nothing -> setMessage "Puff cancelled"
-			Just p -> do
-			    pcw <- puffConfirm gc done p ic
-			    setRenderWidget rc pcw
-		    writeVal editing_r False
-		    nextKey
+                    s <- configLookupElse "ON_INCOMING_PUFF" ""
+                    mapM_ (processKey mwidget) (stringToKeys s)
+                    nextKey
+                (MainEventKey KeyResize) ->  nextKey
+                (MainEventKey k) -> do
+                    getClockTime >>= writeVal userActionTime
+                    b <- keyRenderContext rc $ keyCanon k
+                    if b then nextKey else return ()
+                (MainEventComposed ep done) -> do
+                    case ep of
+                        Nothing -> setMessage "Puff cancelled"
+                        Just p -> do
+                            pcw <- puffConfirm gc done p ic
+                            setRenderWidget rc pcw
+                    writeVal editing_r False
+                    nextKey
     np <- configLookupBool "NO_PRESENCE_NOTIFY"
 
     done <- if np then return (return ()) else do
@@ -855,13 +856,13 @@
         c = fromIntegral $ hashPS (packString (x ++ "@" ++ y))
     doit = do
         let ac = fromIntegral $ hashPS $ packString (getAuthor p)
-	when (y >= 0 && y < ys) $ do
-	    wmove w y 0
-	    when selected $ standout >> return ()
-	    sequence (intersperse (space w) $ map (addCat w) cats)
-	    when selected $ standend >> return ()
-	    space w >> space w
-	    when selected $ standout >> return ()
+        when (y >= 0 && y < ys) $ do
+            wmove w y 0
+            when selected $ standout >> return ()
+            sequence (intersperse (space w) $ map (addCat w) cats)
+            when selected $ standend >> return ()
+            space w >> space w
+            when selected $ standout >> return ()
             waddstr w (unwords (map (('/':) . unpackPS)  kwds))
             Just fmt <- configLookup "PUFF_DATE_FORMAT"
             st <-  fmap (formatCalendarTime defaultTimeLocale fmt) $ toCalendarTime time
@@ -1084,28 +1085,28 @@
     where
     l n v = min (max n 0) (length v)
     cr cloc v = pc cloc v >> ic >>= \x -> case x of
-	(KeyChar '\n') -> return $ return (reverse v)
-	(KeyChar '\r') -> return $ return (reverse v)
-	(KeyEnter) -> return $ return (reverse v)
-	(KeyChar '\b') -> let z = (let (a,b) = splitAt (length v - cloc) v in a ++ drop 1 b) in  cr (l (cloc - 1) z) z
-	(KeyBackspace) -> let z = (let (a,b) = splitAt (length v - cloc) v in a ++ drop 1 b) in  cr (l (cloc - 1) z) z
+        (KeyChar '\n') -> return $ return (reverse v)
+        (KeyChar '\r') -> return $ return (reverse v)
+        (KeyEnter) -> return $ return (reverse v)
+        (KeyChar '\b') -> let z = (let (a,b) = splitAt (length v - cloc) v in a ++ drop 1 b) in  cr (l (cloc - 1) z) z
+        (KeyBackspace) -> let z = (let (a,b) = splitAt (length v - cloc) v in a ++ drop 1 b) in  cr (l (cloc - 1) z) z
         (KeyHome) -> cr 0 v
         (KeyEnd) -> cr (length v) v
         (KeyLeft) -> cr (l (cloc - 1) v) v
         (KeyRight) -> cr (l (cloc + 1) v) v
         (KeyChar '\x0B') -> cr cloc (drop (length v - cloc) v)
         (KeyChar '\BEL') -> return (fail "")
-	(KeyChar c) -> cr (cloc + 1) (let (a,b) = splitAt (length v - cloc) v in a ++ [c] ++ b)
+        (KeyChar c) -> cr (cloc + 1) (let (a,b) = splitAt (length v - cloc) v in a ++ [c] ++ b)
 
-	_ -> return (fail "unknown key")
+        _ -> return (fail "unknown key")
     pc cloc v = do
-	wmove win yloc 0
-	wClrToEol win
-	wAttrOn win attrBold
-	waddstr win prompt
-	wAttrOff win attrBold
-	waddstr win (reverse v)
-	wmove win yloc (cloc + length prompt)
-	refresh
+        wmove win yloc 0
+        wClrToEol win
+        wAttrOn win attrBold
+        waddstr win prompt
+        wAttrOff win attrBold
+        waddstr win (reverse v)
+        wmove win yloc (cloc + length prompt)
+        refresh
 
 
diff --git a/Options.hs b/Options.hs
--- a/Options.hs
+++ b/Options.hs
@@ -68,16 +68,16 @@
 ginsuOpts = do
     args <- getArgs
     r@(env,_) <- case (getOpt Permute (options ++ privateOptions) args) of
-	   (as,n,[]) -> return (foldr ($) env as ,n)
-    	   (_,_,errs) -> putErrDie (concat errs ++ usage)
+           (as,n,[]) -> return (foldr ($) env as ,n)
+           (_,_,errs) -> putErrDie (concat errs ++ usage)
     case (envVerbose env) of
-	1 -> do
-	    setLogLevel LogInfo
-	    putLog LogNotice $ "Verbosity Level: Info"
-	n | n > 1 -> do
-	    setLogLevel LogDebug
-	    putLog LogNotice $ "Verbosity Level: Debug"
-	_ -> return ()
+        1 -> do
+            setLogLevel LogInfo
+            putLog LogNotice $ "Verbosity Level: Info"
+        n | n > 1 -> do
+            setLogLevel LogDebug
+            putLog LogNotice $ "Verbosity Level: Debug"
+        _ -> return ()
     envAction env
     return r
 
diff --git a/PackedString.hs b/PackedString.hs
--- a/PackedString.hs
+++ b/PackedString.hs
@@ -20,29 +20,29 @@
 -- changed to a trivial wrapper for Data.ByteString.UTF8 by Dylan Simon
 
 module PackedString (
-	-- * The @PackedString@ type
+        -- * The @PackedString@ type
         PackedString,      -- abstract, instances: Eq, Ord, Show, Typeable
 
          -- * Converting to and from @PackedString@s
-	packString,  -- :: String -> PackedString
-	unpackPS,    -- :: PackedString -> String
+        packString,  -- :: String -> PackedString
+        unpackPS,    -- :: PackedString -> String
         showsPS,
         -- toString,
 --        lengthPS,
 --        utfLengthPS,
 
-	joinPS,      -- :: PackedString -> [PackedString] -> PackedString
-	-- * List-like manipulation functions
-	nilPS,       -- :: PackedString
-	consPS,      -- :: Char -> PackedString -> PackedString
-	nullPS,      -- :: PackedString -> Bool
-	appendPS,    -- :: PackedString -> PackedString -> PackedString
+        joinPS,      -- :: PackedString -> [PackedString] -> PackedString
+        -- * List-like manipulation functions
+        nilPS,       -- :: PackedString
+        consPS,      -- :: Char -> PackedString -> PackedString
+        nullPS,      -- :: PackedString -> Bool
+        appendPS,    -- :: PackedString -> PackedString -> PackedString
 --        foldrPS,
         hashPS,
 --        filterPS,
 --        foldlPS,
 --        headPS,
-	concatPS    -- :: [PackedString] -> PackedString
+        concatPS    -- :: [PackedString] -> PackedString
 
 
 
diff --git a/RSA.hsc b/RSA.hsc
--- a/RSA.hsc
+++ b/RSA.hsc
@@ -7,7 +7,8 @@
     createPkey,
     sha1,
     bsToHex,
-    RSAElems(..)
+    RSAElems(..),
+    verifyAll
     ) where
 
 #include "my_rsa.h"
@@ -50,7 +51,7 @@
 
 returnData :: Int -> (Ptr CUChar -> Ptr CInt -> IO z) -> IO BS.ByteString
 returnData sz f = do
-	alloca $ \bp ->
+        alloca $ \bp ->
             allocaArray sz $ \m -> do
                 f m bp
                 s <- peek bp
@@ -103,6 +104,9 @@
 foreign import ccall unsafe "openssl/evp.h EVP_des_ede3_cbc" evpDesEde3Cbc :: IO (Ptr EVP_CIPHER)
 foreign import ccall unsafe "openssl/evp.h EVP_md5" evpMD5 :: IO (Ptr EVP_MD)
 
+foreign import ccall unsafe "openssl/evp.h EVP_DigestInit" evpDigestInit :: Ptr EVP_MD_CTX -> Ptr EVP_MD -> IO CInt
+foreign import ccall unsafe "openssl/evp.h EVP_DigestUpdate" evpDigestUpdate :: Ptr EVP_MD_CTX -> Ptr a -> CInt -> IO CInt
+foreign import ccall unsafe "openssl/evp.h EVP_VerifyFinal" evpVerifyFinal :: Ptr EVP_MD_CTX -> Ptr CUChar -> CInt -> Ptr EVP_PKEY -> IO CInt
 
 foreign import ccall unsafe evpCipherContextBlockSize :: Ptr EVP_CIPHER_CTX -> IO Int
 
@@ -129,9 +133,9 @@
 
 withCipherCtx :: (Ptr EVP_CIPHER_CTX -> IO a) -> IO a
 withCipherCtx action = allocaBytes (#const sizeof(EVP_CIPHER_CTX)) $ \cctx ->
-	    E.bracket_ (evpCipherCtxInit cctx)
-		(evpCipherCtxCleanup cctx)
-		    (action cctx)
+            E.bracket_ (evpCipherCtxInit cctx)
+                (evpCipherCtxCleanup cctx)
+                    (action cctx)
 
 withMdCtx :: (Ptr EVP_MD_CTX -> IO a) -> IO a
 withMdCtx = allocaBytes (#const sizeof(EVP_MD_CTX))
@@ -149,6 +153,17 @@
 cipher = unsafePerformIO evpDesEde3Cbc
 md5 = unsafePerformIO evpMD5
 
+verifyAll :: EvpPkey -> BS.ByteString -> BS.ByteString -> IO Bool
+verifyAll pkey data_ sig = do
+    withMdCtx $ \mdctx -> do
+    evpDigestInit mdctx md5
+    withData data_ $ \a b -> evpDigestUpdate mdctx a b
+    withForeignPtr pkey $ \pk -> do
+    withData sig $ \a b -> do
+    rv <- evpVerifyFinal mdctx a b pk
+    return $ case rv of
+      1 -> True
+      _ -> False
 
 signAll :: EvpPkey -> BS.ByteString -> IO BS.ByteString
 signAll  pkey xs = withMdCtx $ \cctx -> do
@@ -214,10 +229,10 @@
 
 foreign import ccall unsafe "BN_bin2bn" bnBin2Bn :: Ptr CUChar -> CInt -> Ptr BIGNUM -> IO (Ptr BIGNUM)
 
-newtype SHA_CTX = SHA_CTX (Ptr SHA_CTX)   
+newtype SHA_CTX = SHA_CTX (Ptr SHA_CTX)
 
-foreign import ccall unsafe "SHA1_Init" sha1Init :: SHA_CTX -> IO () 
-foreign import ccall unsafe "SHA1_Update" sha1Update :: SHA_CTX -> Ptr a -> CULong -> IO () 
+foreign import ccall unsafe "SHA1_Init" sha1Init :: SHA_CTX -> IO ()
+foreign import ccall unsafe "SHA1_Update" sha1Update :: SHA_CTX -> Ptr a -> CULong -> IO ()
 foreign import ccall unsafe "SHA1_Final" sha1Final :: Ptr CChar -> SHA_CTX -> IO ()
 
 
@@ -233,20 +248,18 @@
 bsToHex bs = BS.foldr f [] bs where
     f w = showHex x . showHex y where
         (x,y) = divMod w 16
-        
 
+
 withSHA_CTX :: (SHA_CTX -> IO a) -> IO a
 withSHA_CTX action = allocaBytes (#const sizeof(SHA_CTX)) $ \cctx ->
     sha1Init (SHA_CTX cctx) >> action (SHA_CTX cctx)
 
-type NEvpPkey = ForeignPtr EVP_PKEY
-
 foreign import ccall unsafe pkeyNewRSA :: RSA -> IO (Ptr EVP_PKEY)
 
 -- foreign import ccall "&EVP_PKEY_free" evpPkeyFreePtr :: FunPtr (Ptr EVP_PKEY -> IO ())
 foreign import ccall "get_KEY" evpPkeyFreePtr :: FunPtr (Ptr EVP_PKEY -> IO ())
 
-createPkey :: RSAElems BS.ByteString -> IO NEvpPkey
+createPkey :: RSAElems BS.ByteString -> IO EvpPkey
 createPkey re =  create_rsa re >>= create_pkey where
     setBn pb d = do
         np <- peek pb
diff --git a/Screen.hs b/Screen.hs
--- a/Screen.hs
+++ b/Screen.hs
@@ -117,10 +117,10 @@
 tryDrawRenderContext rc = do
     y <- swapMVar (needsResize rc) False
     withMVar (drawingStuff rc) $ \(z,_,_) -> do
-	erase
-	when y (attemptIO endWin >> refresh)
-	z
-	refresh
+        erase
+        when y (attemptIO endWin >> refresh)
+        z
+        refresh
 
 setRenderContext rc dr = do
     modifyMVar_ (drawingStuff rc)  $ \(_,_,final) -> do
@@ -133,9 +133,9 @@
     final
     --touchRenderContext rc
     return (wdr, processKey w, return (){-, nf-}) where
-	wdr = do
-	    c <- newCanvas
-	    {- eannM ("doRender " ++ show c) $ -}
+        wdr = do
+            c <- newCanvas
+            {- eannM ("doRender " ++ show c) $ -}
             render w c
 
 keyRenderContext :: RenderContext -> Key -> IO Bool
@@ -178,15 +178,15 @@
 
 drawString :: Canvas -> String -> IO ()
 drawString canvas s = {- eannM (fmtSs "drawString %s %s" [(show canvas), s]) $ -} ds ml yb (origin canvas) where
-	ds (s:ls) yb (x,y) | yb > 0 = tryIO (mvWAddStr (window canvas) y x (take xb s)) >> ds ls (yb - 1) (x,y + 1)
-	ds _ _ _ = return ()
-	(xb,yb) = bounds canvas
-	ls = lines s
-	(wx,wy) = widgetOrigin canvas
-	ml = map (mv ' ' wx) (mv "" wy ls)
-	mv _ 0 ss = ss
-	mv _ n ss | n > 0 = drop n ss
-	mv ec n ss = replicate (0 - n) ec ++ ss
+        ds (s:ls) yb (x,y) | yb > 0 = tryIO (mvWAddStr (window canvas) y x (take xb s)) >> ds ls (yb - 1) (x,y + 1)
+        ds _ _ _ = return ()
+        (xb,yb) = bounds canvas
+        ls = lines s
+        (wx,wy) = widgetOrigin canvas
+        ml = map (mv ' ' wx) (mv "" wy ls)
+        mv _ 0 ss = ss
+        mv _ n ss | n > 0 = drop n ss
+        mv ec n ss = replicate (0 - n) ec ++ ss
 
 
 boundCanvas :: Maybe Int -> Maybe Int -> Canvas -> Canvas
@@ -200,12 +200,12 @@
 
 renderCentered :: Widget -> Canvas -> IO ()
 renderCentered w canvas = do
-	(xs,ys) <- getBounds w
-	let (xs',ys') =  bounds canvas
-	    f cs ws | ws >= cs = 0
-	    f cs ws = (cs - ws) `div` 2
-	    nc = (canvasTranslate canvas (f xs' xs,f ys' ys))
-	renderChild w nc {bounds=(min xs $ fst $ bounds nc ,min ys $ snd $ bounds nc)}
+        (xs,ys) <- getBounds w
+        let (xs',ys') =  bounds canvas
+            f cs ws | ws >= cs = 0
+            f cs ws = (cs - ws) `div` 2
+            nc = (canvasTranslate canvas (f xs' xs,f ys' ys))
+        renderChild w nc {bounds=(min xs $ fst $ bounds nc ,min ys $ snd $ bounds nc)}
 
 
 renderChild :: Widget -> Canvas -> IO ()
@@ -229,19 +229,19 @@
     pk k = tk ws k
     tk [] _ = return False
     tk (w:ws) k = do
-	b <- processKey w k
-	if b then return True else tk ws k
+        b <- processKey w k
+        if b then return True else tk ws k
 -- layout widgets
 
 -- | centers child widget, causes child to not use any extra space.
 centerWidget :: Widget -> Widget
 centerWidget w = w {render = rst} where
     rst canvas = do
-	(xs,ys) <- getBounds w
-	let (xs',ys') =  bounds canvas
-	renderChild w (canvasTranslate canvas (f xs' xs,f ys' ys)) {bounds=(min xs $ fst $ bounds canvas ,min ys $ snd $ bounds canvas)} where
-	    f cs ws | ws >= cs = 0
-	    f cs ws = (cs - ws) `div` 2
+        (xs,ys) <- getBounds w
+        let (xs',ys') =  bounds canvas
+        renderChild w (canvasTranslate canvas (f xs' xs,f ys' ys)) {bounds=(min xs $ fst $ bounds canvas ,min ys $ snd $ bounds canvas)} where
+            f cs ws | ws >= cs = 0
+            f cs ws = (cs - ws) `div` 2
 
 
 -- General Widgets
@@ -251,18 +251,18 @@
 newScrollWidget w = do
     yr <- newMVar 0
     let rst canvas = do
-	    y <- readMVar yr
-	    renderChild w (canvasWTranslate canvas (0,y))
-	pk (KeyChar 'j') = modifyMVar_ yr (\x -> return $ x + 1) >> keyDone
-	pk KeyDown = modifyMVar_ yr (\x -> return $ x + 1) >> keyDone
-	pk (KeyChar 'k') = modifyMVar_ yr (\x -> return $ x - 1) >> keyDone
-	pk KeyUp = modifyMVar_ yr (\x -> return $ x - 1) >> keyDone
-	pk KeyHome = swapMVar yr 0 >> keyDone
-	pk (KeyChar 'g') = swapMVar yr 0 >> keyDone
-	pk KeyNPage = modifyMVar_ yr (\x -> return $ x + 25) >> keyDone
-	pk KeyPPage = modifyMVar_ yr (\x -> return $ x - 25) >> keyDone
-	pk k = processKey w k
-	keyDone = modifyMVar_ yr (\x -> return  (max 0 x)) >> return True
+            y <- readMVar yr
+            renderChild w (canvasWTranslate canvas (0,y))
+        pk (KeyChar 'j') = modifyMVar_ yr (\x -> return $ x + 1) >> keyDone
+        pk KeyDown = modifyMVar_ yr (\x -> return $ x + 1) >> keyDone
+        pk (KeyChar 'k') = modifyMVar_ yr (\x -> return $ x - 1) >> keyDone
+        pk KeyUp = modifyMVar_ yr (\x -> return $ x - 1) >> keyDone
+        pk KeyHome = swapMVar yr 0 >> keyDone
+        pk (KeyChar 'g') = swapMVar yr 0 >> keyDone
+        pk KeyNPage = modifyMVar_ yr (\x -> return $ x + 25) >> keyDone
+        pk KeyPPage = modifyMVar_ yr (\x -> return $ x - 25) >> keyDone
+        pk k = processKey w k
+        keyDone = modifyMVar_ yr (\x -> return  (max 0 x)) >> return True
     return w {processKey = pk, render = rst}
 
 
@@ -271,14 +271,14 @@
 boxWidget w = w {render = rst, getBounds = gb} where
     gb = getBounds w >>= \(x,y) -> return (x + 2, y + 2)
     rst canvas@(Canvas {bounds = (xb,yb)}) = do
-	-- let tb = '+' : (replicate (xb - 2) '-' ++ "+")
-	let tb = ulCorner ++ (replicate (xb - 2) hLine ++ urCorner)
-	    bb = llCorner ++ (replicate (xb - 2) hLine ++ lrCorner)
-	    bc c@(Canvas {bounds = (xb,yb)}) = c {bounds = (max (xb - 1) 0, max (yb - 1) 0)}
-	renderChild w (canvasTranslate (bc canvas) (1,1))	
-	drawString canvas tb
-	drawString (canvasTranslate canvas (0,yb - 1)) bb
-	sequence_ [drawString (canvasTranslate canvas (xt,yt)) (vLine :: String) | yt <- [1..yb - 2], xt <- [0,xb - 1]]
+        -- let tb = '+' : (replicate (xb - 2) '-' ++ "+")
+        let tb = ulCorner ++ (replicate (xb - 2) hLine ++ urCorner)
+            bb = llCorner ++ (replicate (xb - 2) hLine ++ lrCorner)
+            bc c@(Canvas {bounds = (xb,yb)}) = c {bounds = (max (xb - 1) 0, max (yb - 1) 0)}
+        renderChild w (canvasTranslate (bc canvas) (1,1))
+        drawString canvas tb
+        drawString (canvasTranslate canvas (0,yb - 1)) bb
+        sequence_ [drawString (canvasTranslate canvas (xt,yt)) (vLine :: String) | yt <- [1..yb - 2], xt <- [0,xb - 1]]
 
 
 {-
@@ -293,35 +293,35 @@
     rst canvas = drawString canvas s
 
 
-	
 
 
+
 verticalBox :: [(Packing,Widget)] -> [(Packing,Widget)] -> Widget
 verticalBox starts ends = (childrenWidget aw) {render = rd, getBounds = gb} where
     awpb = starts ++ ends
     awp = if any isExpand $ fsts awpb then awpb else starts ++ [(Expand,emptyWidget)] ++ ends
     aw = snds awp
     gb = do
-	(xs,ys) <- fmap unzip $ mapM getBounds aw
-	return (maximum xs, sum ys)
+        (xs,ys) <- fmap unzip $ mapM getBounds aw
+        return (maximum xs, sum ys)
     rd canvas = do
-	let (_,yb) = bounds canvas
+        let (_,yb) = bounds canvas
         --let ywo = snd $ widgetOrigin $ canvas
-	(_,ys) <- gb
-	let ne = length (filter isExpand $ fsts awp)
-	    el = if yb > ys then splitSpace (yb - ys) ne else replicate ne 0
-	    f ((e:es),canvas) (ps,w) | isExpand ps = do
-		(_,wby) <- getBounds w
-		if isFill ps then
-		    renderChild w (boundCanvas Nothing (Just (wby + e)) canvas)
-			else renderCentered w (boundCanvas Nothing (Just (wby + e)) canvas)
-		return (es,canvasTranslate canvas (0,wby + e))
-	    f (es,canvas) (_,w) = do
-		(_,wby) <- getBounds w
-		--renderCentered w (boundCanvas Nothing (Just wby) canvas)
-		renderChild w (boundCanvas Nothing (Just wby) canvas)
-		return (es,canvasTranslate canvas (0,wby))
-	foldlM_ f (el,canvas) awp
+        (_,ys) <- gb
+        let ne = length (filter isExpand $ fsts awp)
+            el = if yb > ys then splitSpace (yb - ys) ne else replicate ne 0
+            f ((e:es),canvas) (ps,w) | isExpand ps = do
+                (_,wby) <- getBounds w
+                if isFill ps then
+                    renderChild w (boundCanvas Nothing (Just (wby + e)) canvas)
+                        else renderCentered w (boundCanvas Nothing (Just (wby + e)) canvas)
+                return (es,canvasTranslate canvas (0,wby + e))
+            f (es,canvas) (_,w) = do
+                (_,wby) <- getBounds w
+                --renderCentered w (boundCanvas Nothing (Just wby) canvas)
+                renderChild w (boundCanvas Nothing (Just wby) canvas)
+                return (es,canvasTranslate canvas (0,wby))
+        foldlM_ f (el,canvas) awp
 
 
 horizontalBox :: [(Packing,Widget)] -> [(Packing,Widget)] -> Widget
@@ -330,60 +330,60 @@
     awp = if any isExpand $ fsts awpb then awpb else starts ++ [(Expand,emptyWidget)] ++ ends
     aw = snds awp
     gb = do
-	(xs,ys) <- fmap unzip $ mapM getBounds aw
-	return (sum xs, maximum ys)
+        (xs,ys) <- fmap unzip $ mapM getBounds aw
+        return (sum xs, maximum ys)
     rd canvas = do
-	let (xb,_) = bounds canvas
-	(xs,_) <- gb
-	let ne = length (filter isExpand $ fsts awp)
-	    el = if xb > xs then splitSpace (xb - xs) ne else replicate ne 0
-	    f ((e:es),canvas) (ps,w) | isExpand ps = do
-		(wbx,_) <- getBounds w
-		if isFill ps then
-		    renderChild w (boundCanvas (Just (wbx + e)) Nothing canvas)
-			else renderCentered w (boundCanvas (Just (wbx + e)) Nothing canvas)
-		return (es,canvasTranslate canvas (wbx + e,0))
-	    f (es,canvas) (_,w) = do
-		(wbx,_) <- getBounds w
-		--renderCentered w (boundCanvas (Just wbx) Nothing canvas)
-		renderChild w (boundCanvas (Just wbx) Nothing canvas)
-		return (es,canvasTranslate canvas (wbx,0))
-	foldlM_ f (el,canvas) awp
+        let (xb,_) = bounds canvas
+        (xs,_) <- gb
+        let ne = length (filter isExpand $ fsts awp)
+            el = if xb > xs then splitSpace (xb - xs) ne else replicate ne 0
+            f ((e:es),canvas) (ps,w) | isExpand ps = do
+                (wbx,_) <- getBounds w
+                if isFill ps then
+                    renderChild w (boundCanvas (Just (wbx + e)) Nothing canvas)
+                        else renderCentered w (boundCanvas (Just (wbx + e)) Nothing canvas)
+                return (es,canvasTranslate canvas (wbx + e,0))
+            f (es,canvas) (_,w) = do
+                (wbx,_) <- getBounds w
+                --renderCentered w (boundCanvas (Just wbx) Nothing canvas)
+                renderChild w (boundCanvas (Just wbx) Nothing canvas)
+                return (es,canvasTranslate canvas (wbx,0))
+        foldlM_ f (el,canvas) awp
 
 homVerticalBox :: [(Packing,Widget)] -> Widget
 homVerticalBox awp = (childrenWidget aw) {render = rd, getBounds = gb } where
     aw = snds awp
     gb = do
-	(xs,ys) <- fmap unzip $ mapM getBounds aw
-	return (maximum xs, (maximum ys) * length aw)
+        (xs,ys) <- fmap unzip $ mapM getBounds aw
+        return (maximum xs, (maximum ys) * length aw)
     rd canvas = do
-	let (_,yb) = bounds canvas
-	    el = splitSpace yb (length aw)
-	    f ([],_) _ = error "no space to split"
-	    f ((e:es),canvas) (ps,w)  = do
-		if isFill ps then
-		    renderChild w (boundCanvas Nothing (Just e) canvas)
-			else renderCentered w (boundCanvas Nothing (Just e) canvas)
-		return (es,canvasTranslate canvas (0,e))
-	foldlM_ f (el,canvas) awp
+        let (_,yb) = bounds canvas
+            el = splitSpace yb (length aw)
+            f ([],_) _ = error "no space to split"
+            f ((e:es),canvas) (ps,w)  = do
+                if isFill ps then
+                    renderChild w (boundCanvas Nothing (Just e) canvas)
+                        else renderCentered w (boundCanvas Nothing (Just e) canvas)
+                return (es,canvasTranslate canvas (0,e))
+        foldlM_ f (el,canvas) awp
 
 
 homHorizontalBox :: [(Packing,Widget)] -> Widget
 homHorizontalBox awp = (childrenWidget aw) {render = rd, getBounds = gb } where
     aw = snds awp
     gb = do
-	(xs,ys) <- fmap unzip $ mapM getBounds aw
-	return (maximum xs * length aw, maximum ys)
+        (xs,ys) <- fmap unzip $ mapM getBounds aw
+        return (maximum xs * length aw, maximum ys)
     rd canvas = do
-	let (xb,_) = bounds canvas
-	    el = splitSpace xb (length aw)
-	    f ([],_) _ = error "no space to split"
-	    f ((e:es),canvas) (ps,w)  = do
-		if isFill ps then
-		    renderChild w (boundCanvas (Just e) Nothing canvas)
-			else renderCentered w (boundCanvas (Just e) Nothing canvas)
-		return (es,canvasTranslate canvas (e,0))
-	foldlM_ f (el,canvas) awp
+        let (xb,_) = bounds canvas
+            el = splitSpace xb (length aw)
+            f ([],_) _ = error "no space to split"
+            f ((e:es),canvas) (ps,w)  = do
+                if isFill ps then
+                    renderChild w (boundCanvas (Just e) Nothing canvas)
+                        else renderCentered w (boundCanvas (Just e) Nothing canvas)
+                return (es,canvasTranslate canvas (e,0))
+        foldlM_ f (el,canvas) awp
 
 splitSpace m n = f (m `mod` n) $ replicate n (m `div` n) where
     f 0 xs = xs
@@ -391,21 +391,21 @@
     f n' [] = errorf "splitSpace %i %i (f %i []): this can't happen." [fi m, fi n, fi n']
 
 
-	
+
 newSVarWidget :: (Readable c ) => c a -> (a -> Widget) -> Widget
 newSVarWidget sv f = emptyWidget {render = rw, getBounds = gb} where
     rw canvas = do
-	v <- readVal sv
-	render (f v) canvas
+        v <- readVal sv
+        render (f v) canvas
     gb = do
-	v <- readVal sv
-	getBounds (f v)
+        v <- readVal sv
+        getBounds (f v)
 
 dynamicWidget :: IO Widget -> Widget
 dynamicWidget w = emptyWidget {render = rw, getBounds = gb, processKey = pk {- , processEvent = pe -} } where
     rw canvas = do
-	v <- w
-	render v canvas
+        v <- w
+        render v canvas
     gb = w >>= getBounds
     pk k = w >>= \x -> processKey x k
 --    pe a b = w >>= \x -> processEvent x a b
@@ -417,8 +417,8 @@
 stackedWidgets ws@(w:_) = widgetEmpty { render = rnd, processKey = \k -> processKey w k, getBounds = gb  } where
     rnd canvas = mapM_ (flip render canvas) (reverse ws)
     gb = do
-	bs <- mapM getBounds ws
-	return $ (liftT2 (maximum, maximum)) (unzip bs)
+        bs <- mapM getBounds ws
+        return $ (liftT2 (maximum, maximum)) (unzip bs)
 
 {-
 newAlternate :: Widget -> IO (Widget, Alternate)
@@ -427,29 +427,29 @@
     final <- connect (changedSignal w) (\() -> signal sig ())
     r <- newMVar (w,final)
     let w = emptyWidget {render = rw, getBounds = gb, processKey = pk }
-	rw canvas = do
-	    (w,_) <- readMVar r
-	    renderChild w canvas
-	gb = do
-	    (w,_) <- readMVar r
-	    getBounds w
-	pk k = do
-	    (w,_) <- readMVar r
-	    processKey w k
+        rw canvas = do
+            (w,_) <- readMVar r
+            renderChild w canvas
+        gb = do
+            (w,_) <- readMVar r
+            getBounds w
+        pk k = do
+            (w,_) <- readMVar r
+            processKey w k
     return (w,Alternate r sig)
 
 
 switchAlternate :: Alternate -> Widget -> IO ()
 switchAlternate (Alternate mv sig) w = (modifyMVar_ mv $ \(_,final) ->
-	(final >> connect (changedSignal w) (\() -> signal sig ()) >>= \nf -> return (w,nf)))
-	    >> signal sig ()
+        (final >> connect (changedSignal w) (\() -> signal sig ()) >>= \nf -> return (w,nf)))
+            >> signal sig ()
 -}
 
 keyCatcherWidget :: (Curses.Key -> IO Bool) -> Widget -> Widget
 keyCatcherWidget kr w = w {processKey = kr'} where
     kr' k = do
-	b <- processKey w k
-	if b then return True else (kr k)
+        b <- processKey w k
+        if b then return True else (kr k)
 
 attrWidget :: Attr -> Widget -> Widget
 attrWidget a w = w {render = nr} where
diff --git a/dist/build/ginsu/ginsu-tmp/ExampleConf.hs b/dist/build/ginsu/ginsu-tmp/ExampleConf.hs
--- a/dist/build/ginsu/ginsu-tmp/ExampleConf.hs
+++ b/dist/build/ginsu/ginsu-tmp/ExampleConf.hs
@@ -58,7 +58,7 @@
     "# MARK_[123..] sets the initial filter for a given marked screen. use the",
     "# number keys to quickly switch to a mark.",
     "#",
-    "# the default has '7' show puffs to or from you, ",
+    "# the default has '7' show puffs to or from you,",
     "# '8' shows traffic about gale and '9' filters spoilers away.",
     "",
     "MARK_7 ~c:$GALE_ID ; ~a:$GALE_ID",
@@ -67,7 +67,7 @@
     "",
     "",
     "# beep on any puffs matching the following filters",
-    "BEEP ~c:$GALE_ID   ",
+    "BEEP ~c:$GALE_ID",
     "",
     "",
     "# number of puffs to store in pufflog on exit.",
@@ -77,7 +77,7 @@
     "SCROLLBACK_SIZE 0",
     "",
     "# charset, one of utf8, latin1, or ascii. if not set, will try to determine it from $LANG",
-    "# this only affects what format it assumes you are editing puffs in. locale settings are ",
+    "# this only affects what format it assumes you are editing puffs in. locale settings are",
     "# always used for the UI",
     "#CHARSET latin1",
     "",
@@ -95,8 +95,8 @@
     "",
     "BROWSER links",
     "",
-    "apphook WikiWord ",
-    "        '([[:space:]]|^)(([[:upper:]][[:lower:]]+){2,})([[:space:]]|$)' ",
+    "apphook WikiWord",
+    "        '([[:space:]]|^)(([[:upper:]][[:lower:]]+){2,})([[:space:]]|$)'",
     "        '$BROWSER http://wiki.ofb.net/?$2'",
     "        '$2'",
     "",
@@ -106,10 +106,10 @@
     "",
     "#default key bindings",
     "",
-    "bind <F1> show_help_screen ",
+    "bind <F1> show_help_screen",
     "bind ? show_help_screen",
-    "#bind <F2> show_main_index ",
-    "bind <F3> show_presence_status ",
+    "#bind <F2> show_main_index",
+    "bind <F3> show_presence_status",
     "",
     "bind j next_puff",
     "bind k previous_puff",
@@ -172,5 +172,5 @@
     "bind <C-l> redraw_screen",
     "bind v goto_match",
     "bind S show_status_screen",
-    " ",
+    "",
   ""]
diff --git a/dist/build/ginsu/ginsu-tmp/KeyHelpTable.hs b/dist/build/ginsu/ginsu-tmp/KeyHelpTable.hs
--- a/dist/build/ginsu/ginsu-tmp/KeyHelpTable.hs
+++ b/dist/build/ginsu/ginsu-tmp/KeyHelpTable.hs
@@ -1,53 +1,53 @@
 module KeyHelpTable(keyHelpTable) where
 
 keyHelpTable gk = [
-	 Left "Main Screens"
-	,Right (gk "show_help_screen", "Show help screen")
-	,Right (gk "show_presence_status", "show presence status")
-	,Right (gk "show_puff_details", "show puff details")
-	,Right (gk "show_status_screen", "show status screen")
-	,Left "Puff Movement"
-	,Right (gk "next_puff", "Goto the next puff")
-	,Right (gk "previous_puff", "Goto the previous puff")
-	,Right (gk "first_puff", "Goto the first puff")
-	,Right (gk "last_puff", "Goto the last puff")
-	,Right (gk "next_line", "Scroll one line down")
-	,Right (gk "previous_line", "Scroll one line up")
-	,Right (gk "next_page", "Scroll one page down")
-	,Right (gk "previous_page", "Scroll one page up")
-	,Right (gk "forward_half_page", "Scroll a half page down")
-	,Right (gk "backward_half_page", "Scroll a half page up")
-	,Left "Filter Manipulation"
-	,Right (gk "prompt_new_filter", "prompt new filter")
-	,Right (gk "prompt_new_filter_slash", "prompt new filter slash")
-	,Right (gk "prompt_new_filter_twiddle", "prompt new filter twiddle")
-	,Right (gk "pop_one_filter", "pop one filter")
-	,Right (gk "pop_all_filters", "pop all filters")
-	,Right (gk "invert_filter", "invert filter")
-	,Right (gk "filter_current_thread", "filter current thread")
-	,Right (gk "swap_filters", "swap filters")
-	,Right (gk "filter_current_author", "filter current author")
-	,Left "Puff Body Filtering"
-	,Right (gk "toggle_rot13", "toggle Rot13 filter")
-	,Left "Mark/Workspace Manipulation"
-	,Right (gk "set_mark", "save current position at a given mark.")
-	,Right (gk "recall_mark", "goto position saved at mark")
-	,Right (gk "set_filter_mark", "save current filter stack at given mark.")
-	,Right (gk "recall_filter_mark", "Recall filter stack at mark")
-	,Right (gk "recall_combine_mark", "Recall filter stack at mark and combine it with the current filter stack.")
-	,Right ("[1-9]", "Recall given numbered workspace and set it as current workspace, these follow marks [1-9]")
-	,Left "Composing Puffs"
-	,Right (gk "new_puff", "compose a new puff")
-	,Right (gk "follow_up", "follow up to the same category as the current puff")
-	,Right (gk "reply_to_author", "reply to the author of the current puff privatly")
-	,Right (gk "group_reply", "reply to the union of the sender and categories of the current puff")
-	,Right (gk "resend_puff", "resend puff")
-	,Left "Miscellaneous"
-	,Right (gk "goto_match", "visit link in current puff")
-	,Right (gk "modify_presence_string", "modify presence string")
-	,Right (gk "reconnect_to_servers", "reconnect to servers")
-	,Right (gk "edit_config_file", "Edit the configuration file and reload its settings")
-	,Right (gk "ask_quit", "quit ginsu")
-	,Right (gk "fast_quit", "quit ginsu without asking first")
-	,Right (gk "redraw_screen", "redraw screen")
-	]
+         Left "Main Screens"
+        ,Right (gk "show_help_screen", "Show help screen")
+        ,Right (gk "show_presence_status", "show presence status")
+        ,Right (gk "show_puff_details", "show puff details")
+        ,Right (gk "show_status_screen", "show status screen")
+        ,Left "Puff Movement"
+        ,Right (gk "next_puff", "Goto the next puff")
+        ,Right (gk "previous_puff", "Goto the previous puff")
+        ,Right (gk "first_puff", "Goto the first puff")
+        ,Right (gk "last_puff", "Goto the last puff")
+        ,Right (gk "next_line", "Scroll one line down")
+        ,Right (gk "previous_line", "Scroll one line up")
+        ,Right (gk "next_page", "Scroll one page down")
+        ,Right (gk "previous_page", "Scroll one page up")
+        ,Right (gk "forward_half_page", "Scroll a half page down")
+        ,Right (gk "backward_half_page", "Scroll a half page up")
+        ,Left "Filter Manipulation"
+        ,Right (gk "prompt_new_filter", "prompt new filter")
+        ,Right (gk "prompt_new_filter_slash", "prompt new filter slash")
+        ,Right (gk "prompt_new_filter_twiddle", "prompt new filter twiddle")
+        ,Right (gk "pop_one_filter", "pop one filter")
+        ,Right (gk "pop_all_filters", "pop all filters")
+        ,Right (gk "invert_filter", "invert filter")
+        ,Right (gk "filter_current_thread", "filter current thread")
+        ,Right (gk "swap_filters", "swap filters")
+        ,Right (gk "filter_current_author", "filter current author")
+        ,Left "Puff Body Filtering"
+        ,Right (gk "toggle_rot13", "toggle Rot13 filter")
+        ,Left "Mark/Workspace Manipulation"
+        ,Right (gk "set_mark", "save current position at a given mark.")
+        ,Right (gk "recall_mark", "goto position saved at mark")
+        ,Right (gk "set_filter_mark", "save current filter stack at given mark.")
+        ,Right (gk "recall_filter_mark", "Recall filter stack at mark")
+        ,Right (gk "recall_combine_mark", "Recall filter stack at mark and combine it with the current filter stack.")
+        ,Right ("[1-9]", "Recall given numbered workspace and set it as current workspace, these follow marks [1-9]")
+        ,Left "Composing Puffs"
+        ,Right (gk "new_puff", "compose a new puff")
+        ,Right (gk "follow_up", "follow up to the same category as the current puff")
+        ,Right (gk "reply_to_author", "reply to the author of the current puff privatly")
+        ,Right (gk "group_reply", "reply to the union of the sender and categories of the current puff")
+        ,Right (gk "resend_puff", "resend puff")
+        ,Left "Miscellaneous"
+        ,Right (gk "goto_match", "visit link in current puff")
+        ,Right (gk "modify_presence_string", "modify presence string")
+        ,Right (gk "reconnect_to_servers", "reconnect to servers")
+        ,Right (gk "edit_config_file", "Edit the configuration file and reload its settings")
+        ,Right (gk "ask_quit", "quit ginsu")
+        ,Right (gk "fast_quit", "quit ginsu without asking first")
+        ,Right (gk "redraw_screen", "redraw screen")
+        ]
diff --git a/docs/Changelog.old b/docs/Changelog.old
--- a/docs/Changelog.old
+++ b/docs/Changelog.old
@@ -8,9 +8,9 @@
     Revision:
       ginsu--main--0.6--patch-45
 
-    
-    
 
+
+
     modified files:
      .arch-inventory CWString.hsc Changelog Charset.hs Curses.hsc
      EIO.hs ErrorLog.hs Gale.hs GinsuConfig.hs Main.hs Makefile.am
@@ -28,9 +28,9 @@
     Revision:
       ginsu--main--0.6--patch-44
 
-    
-    
 
+
+
     modified files:
      Changelog Makefile.am
 
@@ -42,12 +42,12 @@
     Revision:
       ginsu--main--0.6--patch-43
 
-    Color support 
+    Color support
     major filter changes, based on boolean library now.
-    
-    
-    
 
+
+
+
     new files:
      Boolean/.arch-ids/=id Boolean/Algebra.hs unicode_map.txt
 
@@ -67,14 +67,14 @@
 2004-08-12 06:06:27 GMT	John Meacham <john@repetae.net>	patch-42
 
     Summary:
-      
+
     Revision:
       ginsu--main--0.6--patch-42
 
     made error messages modal, instead of using status line
     switched to parsec for parsing filters, better error messages
     added general status screen on 'S'
-    added ?encrypted and ?signed 
+    added ?encrypted and ?signed
     added /foo to search everything for foo
     added log buffer on status screen
 
@@ -103,15 +103,15 @@
 2004-07-21 23:50:12 GMT	John Meacham <john@repetae.net>	patch-40
 
     Summary:
-      fix puff parsing bug 
+      fix puff parsing bug
     Revision:
       ginsu--main--0.6--patch-40
 
     parse top level text fragments properly
     create better error messages on malformed puff
     don't try to reconnect to server because of malformed puff
-    
 
+
     modified files:
      Atom.hs Changelog Gale.hs UArrayParser.hs
 
@@ -135,9 +135,9 @@
     Revision:
       ginsu--main--0.6--patch-38
 
-    
-    
 
+
+
     modified files:
      .arch-inventory Changelog
 
@@ -149,11 +149,11 @@
     Revision:
       ginsu--main--0.6--patch-37
 
-    no longer cache word-wrapped text. 
+    no longer cache word-wrapped text.
     much more efficient utf8 decoding.
     hash function for atoms better.
-    
 
+
     modified files:
      Atom.hs Changelog Filter.hs Main.hs PackedString.hs
      configure.in
@@ -217,21 +217,21 @@
 2004-07-02 21:12:39 GMT	John Meacham <john@repetae.net>	patch-32
 
     Summary:
-      added azz patches, fix static build 
+      added azz patches, fix static build
     Revision:
       ginsu--main--0.6--patch-32
 
-    
-    
+
+
     Patches applied:
-    
+
      * azz@offog.org--2004/ginsu--azz--0.1--base-0
        tag of john@repetae.net--2004/ginsu--main--0.6--patch-31
-    
+
      * azz@offog.org--2004/ginsu--azz--0.1--patch-1
        don't hide last line when using filters
-    
 
+
     modified files:
      Changelog Main.hs Makefile.am
 
@@ -303,13 +303,13 @@
 2004-04-23 00:14:58 GMT	John Meacham <john@repetae.net>	patch-26
 
     Summary:
-      AKD wrapup. dates 
+      AKD wrapup. dates
     Revision:
       ginsu--main--0.6--patch-26
 
     PUFF_DATE_FORMAT string similar to 'date' command for how you want the date to
     be displayed in your puff window
-    
+
     participates in AKD semi-properly. populates .gale/auth/cache on its own
 
     modified files:
@@ -328,8 +328,8 @@
     print hash of data fragments
     use ACS macros even if we have wide character support
     made sure it compiles without ncursesw
-    
 
+
     modified files:
      Changelog Curses.hsc KeyCache.hs Puff.hs SHA1.hs configure.in
      my_curses.h
@@ -346,9 +346,9 @@
     made it use ncursesw if it exists
     uses line drawing characters in display now
     fixed problems with long utf8 lines when ncursesw is installed
-    
-    
 
+
+
     new files:
      ac-macros/.arch-ids/curslib.m4.id
      ac-macros/.arch-ids/funcdecl.m4.id ac-macros/curslib.m4
@@ -378,8 +378,8 @@
     Revision:
       ginsu--main--0.6--patch-22
 
-    
 
+
     modified files:
      Changelog GinsuConfig.hs RSA.hsc
 
@@ -473,8 +473,8 @@
 
     Completly revamped the mark system. now you can set position and filter marks independently
     Workspaces are built on top of the marks.
-    
 
+
     new files:
      DocLike.hs
 
@@ -490,9 +490,9 @@
     Revision:
       ginsu--main--0.6--patch-14
 
-    
-    
 
+
+
     modified files:
      Changelog ErrorLog.hs
 
@@ -548,7 +548,7 @@
 2004-03-16 05:31:40 GMT	John Meacham <john@repetae.net>	patch-9
 
     Summary:
-      Key mapping 
+      Key mapping
     Revision:
       ginsu--main--0.6--patch-9
 
@@ -569,7 +569,7 @@
 2004-03-16 00:59:42 GMT	John Meacham <john@repetae.net>	patch-8
 
     Summary:
-      
+
     Revision:
       ginsu--main--0.6--patch-8
 
@@ -642,7 +642,7 @@
 
     goto_match ('v' by default) will extract urls or WikiWords and let you access
     them with a web browser. The mechanism is generic and may extract arbitrary
-    regular expressions and run arbitrary programs on them. 
+    regular expressions and run arbitrary programs on them.
 
     new files:
      Regex.hs
@@ -655,7 +655,7 @@
 2004-03-04 03:49:59 GMT	John Meacham <john@repetae.net>	patch-3
 
     Summary:
-      Cleanups 
+      Cleanups
     Revision:
       ginsu--main--0.6--patch-3
 
@@ -683,9 +683,9 @@
       ginsu--main--0.6--patch-2
 
     fixed filters to use regular expressions. simplified code in several ways.
-    
-    
 
+
+
     modified files:
      Changelog Filter.hs Puff.hs
 
@@ -709,7 +709,7 @@
     Revision:
       ginsu--main--0.6--base-0
 
-    
+
     (automatically generated log message)
 
     new files:
@@ -731,12 +731,12 @@
 0.5.3
         changed id/class to use '/' convention
         rebindable keys via 'bind' in config file
-        can derive GALE_DOMAIN from GALE_ID if necessary 
+        can derive GALE_DOMAIN from GALE_ID if necessary
         added '?' as extra help key
         fixed problem with generated C code.
 
 0.5.2
-        new pufflog format. 
+        new pufflog format.
         fixed some memory leaks.
         fixed '-e' thanks to Adam Sampson
         builds cleanly with ghc 6.2 now.
@@ -745,7 +745,7 @@
         line editing for entering filters now works. supported are arrow keys,
                 backspace, home,end, ^K to delete to end of line
         added EDITOR_NEWPUFF_OPTION for options to be passed to the editor
-                when puffing to no categories 
+                when puffing to no categories
         added NO_PRESENCE_NOTIFY to stop presence status puffs from being
                 generated.
         changed checkconfig output for GALE_SUBSCRIBE
@@ -767,7 +767,7 @@
         Makefile cleanups
         requires ghc 6.0
         Fixed terminal cooruption caused by nonprintable characters in puffs
-        now uses ISO C90 charset conversion routines. 
+        now uses ISO C90 charset conversion routines.
         fixed some problems with charset detection
         much much improved autoconf/automake setup
         scrolls to bottom of screen on incoming puff when already at bottom only
@@ -788,191 +788,191 @@
 
 
 0.4.6
-	switched to -O2 for performance
-	better locale support.
-	added quit confirmation (shift-Q to quit immediatly)
-	added filter documentation to help screen
-	fixed docs for key notation consistancy
-	added --enable-unopt for unoptimized builds
-	added EDITOR_OPTION to pass arguments to your puff editor
-	added puff sending confirmation screen this lets you
-	    - set return receipts manually
-	    - send anonymous puffs
-	    - reedit puffs 
-	    - abort puffs
-	expanded dialogs
-	
+        switched to -O2 for performance
+        better locale support.
+        added quit confirmation (shift-Q to quit immediatly)
+        added filter documentation to help screen
+        fixed docs for key notation consistancy
+        added --enable-unopt for unoptimized builds
+        added EDITOR_OPTION to pass arguments to your puff editor
+        added puff sending confirmation screen this lets you
+            - set return receipts manually
+            - send anonymous puffs
+            - reedit puffs
+            - abort puffs
+        expanded dialogs
+
 0.4.5
-	changed key notation to angle notation <C-x><space> etc..
-	ON_INCOMING_PUFF works as expected now
-	angle notation can be used in config file.
-	popup informational windows for certain modal actions added
-	scrolling window responds to <PageUp> and <PageDown>
-	cursor handling much more robust/consistant
-	numerous internal changes
-	now compiles via-c for better optimization and profiling
-	'./configure --enable-profile' will enable profiling support.
-	sends \r\n instead of \n because some clients get confused otherwise.
-	ignores SIGPIPE, just in case.
-	fixed space leak on reading pufflog.
+        changed key notation to angle notation <C-x><space> etc..
+        ON_INCOMING_PUFF works as expected now
+        angle notation can be used in config file.
+        popup informational windows for certain modal actions added
+        scrolling window responds to <PageUp> and <PageDown>
+        cursor handling much more robust/consistant
+        numerous internal changes
+        now compiles via-c for better optimization and profiling
+        './configure --enable-profile' will enable profiling support.
+        sends \r\n instead of \n because some clients get confused otherwise.
+        ignores SIGPIPE, just in case.
+        fixed space leak on reading pufflog.
 
 0.4.4
-	implemented full server side of IdleTimeProtocol (by popular request)
-	prettier printing of aliases
-	added --checkconfig to verify configuration. if you are having
-	  trouble, use this.
-	updated man page.
-	doesnt display all whitespace puffs from _gale.* 
+        implemented full server side of IdleTimeProtocol (by popular request)
+        prettier printing of aliases
+        added --checkconfig to verify configuration. if you are having
+          trouble, use this.
+        updated man page.
+        doesnt display all whitespace puffs from _gale.*
 
 0.4.3
-	added presence screen via 'F3', lists all users and their status.
-	answers return reciepts now.
-	notice/presence returns idle time.
-	requests return reciepts on 'private' puffs
-	outgoing 'private' puffs placed in incoming puff queue.
-	'Will' a puff to reset presence when ginsu dies.
-	uses GALE_PRESENCE if set.
-	changed configuration file syntax to match that of .gale/conf
-	added ~i for case insensitive search.
-	updated internal docs
-	outputs error message if it can't parse the MARK_ statements.
-	filter parser now parses () and ; properly.
-	looks for .gpri files
-	uses $GALE_DIR if available.
-	prints out useful preferences to errorlog
-	puff details screen available via 'd'
+        added presence screen via 'F3', lists all users and their status.
+        answers return reciepts now.
+        notice/presence returns idle time.
+        requests return reciepts on 'private' puffs
+        outgoing 'private' puffs placed in incoming puff queue.
+        'Will' a puff to reset presence when ginsu dies.
+        uses GALE_PRESENCE if set.
+        changed configuration file syntax to match that of .gale/conf
+        added ~i for case insensitive search.
+        updated internal docs
+        outputs error message if it can't parse the MARK_ statements.
+        filter parser now parses () and ; properly.
+        looks for .gpri files
+        uses $GALE_DIR if available.
+        prints out useful preferences to errorlog
+        puff details screen available via 'd'
 
 0.4.2
-	handles fragments around security/encryption fragments properly (oops)
-	adds _ginsu.timestamp and _ginsu.spumbuster to incoming puffs now.
-	most errorlog messages supressed unless '-v' (info) or '-vv' (debug)
-	  are specified on the command line.
-	seperated pufflog command line options to allow just not writing to
-	  pufflog (but still read it on startup)
-	tweaked fragment printing in logs to keep them pretty.
-	sanified puff numbering
-	UI much less redraw-crazy. should improve responsiveness.
-	resizing much smarter. still flickers, but not as much.
-	uses $LOGNAME and $USER in preference to getlogin(3)
-	scrollable help screen for small terminals.
-	filters appear on own line when needed.
-	message string used for puff author
-	uses _ginsu.timestamp if no id/time available.
-	added '/' to substring match puffs
-	hides cursor if editor leaves it unhidden.
+        handles fragments around security/encryption fragments properly (oops)
+        adds _ginsu.timestamp and _ginsu.spumbuster to incoming puffs now.
+        most errorlog messages supressed unless '-v' (info) or '-vv' (debug)
+          are specified on the command line.
+        seperated pufflog command line options to allow just not writing to
+          pufflog (but still read it on startup)
+        tweaked fragment printing in logs to keep them pretty.
+        sanified puff numbering
+        UI much less redraw-crazy. should improve responsiveness.
+        resizing much smarter. still flickers, but not as much.
+        uses $LOGNAME and $USER in preference to getlogin(3)
+        scrollable help screen for small terminals.
+        filters appear on own line when needed.
+        message string used for puff author
+        uses _ginsu.timestamp if no id/time available.
+        added '/' to substring match puffs
+        hides cursor if editor leaves it unhidden.
 
 0.4.1
-	SIGWINCH now caught to resize terminal screen (bug 194) 
-	    DISABLE_SIGWINCH is a config option if this is causing trouble
-	now expands to GALE_DOMAIN if no alias matches (bug 200)
-	fix for different versions of OpenSSL (bug 195)
-	.spec file added for building RPMS
-	hsc2hs rebuilds files for all source builds. probably what is wanted.
-	redid Puff deconstruction, now works for more unusual but technically
-	   valid types of puff.
-	added id/instance fragment to satisfy certain loggers
-	made printing of fragment lists cosmetically prettier.
-	added --testCurses to print out some curses debugging info.
-	added --dumpKey to print info about a keyfile.
-	added --errorlog to choose file to output errors too.
-	doesnt get confused by jgaled tracking fragments
-	^L does an endwin >> refresh.
+        SIGWINCH now caught to resize terminal screen (bug 194)
+            DISABLE_SIGWINCH is a config option if this is causing trouble
+        now expands to GALE_DOMAIN if no alias matches (bug 200)
+        fix for different versions of OpenSSL (bug 195)
+        .spec file added for building RPMS
+        hsc2hs rebuilds files for all source builds. probably what is wanted.
+        redid Puff deconstruction, now works for more unusual but technically
+           valid types of puff.
+        added id/instance fragment to satisfy certain loggers
+        made printing of fragment lists cosmetically prettier.
+        added --testCurses to print out some curses debugging info.
+        added --dumpKey to print info about a keyfile.
+        added --errorlog to choose file to output errors too.
+        doesnt get confused by jgaled tracking fragments
+        ^L does an endwin >> refresh.
 
-0.4.0 
-	*** NOW is stand alone. can send signed puffs natively! wing-go! ***
-	reads ~/.gale/aliases/ for aliases
-	uses GALE_DOMAIN, GALE_ID, GALE_NAME as appropriate.
-	simplified Curses interface to take advantage of newer FFI features
-	added simple man page "ginsu (1)"
-	robustified key parsing code. should work with all known styles of key
-	cleaned up location of many routines
-	made it compile when ghc is installed in wierd places
-	added displays to let user know what is happening during delays
-	added -P to stop loading and saving of pufflog.
-	unified private key caching for sending and recieving puffs.
-	key cacheing no longer uses tricky tricks.
-	Sending Puffs is done in background thread in case it takes a while.
-	smarter error handling and retrying. better errorlog format.
-	SIGINT now caught, kills app nicely.
-	added ON_INCOMING_PUFF which is a macro run whenever a puff arrives. *EXP*
+0.4.0
+        *** NOW is stand alone. can send signed puffs natively! wing-go! ***
+        reads ~/.gale/aliases/ for aliases
+        uses GALE_DOMAIN, GALE_ID, GALE_NAME as appropriate.
+        simplified Curses interface to take advantage of newer FFI features
+        added simple man page "ginsu (1)"
+        robustified key parsing code. should work with all known styles of key
+        cleaned up location of many routines
+        made it compile when ghc is installed in wierd places
+        added displays to let user know what is happening during delays
+        added -P to stop loading and saving of pufflog.
+        unified private key caching for sending and recieving puffs.
+        key cacheing no longer uses tricky tricks.
+        Sending Puffs is done in background thread in case it takes a while.
+        smarter error handling and retrying. better errorlog format.
+        SIGINT now caught, kills app nicely.
+        added ON_INCOMING_PUFF which is a macro run whenever a puff arrives. *EXP*
 
 0.3.9
-	added PRESERVED_KEYWORDS which is a list of keywords which should be carried
-	    over when replying to or following up on a puff.
-	BEEP functionality now works when terminal supports it.
-	ON_STARTUP added which is a macro that is run at startup
-	fixed sender vs. author sillyness
-	~t means ~c until category closure code is finished
-	quoting fixed when sending things to the shell.
+        added PRESERVED_KEYWORDS which is a list of keywords which should be carried
+            over when replying to or following up on a puff.
+        BEEP functionality now works when terminal supports it.
+        ON_STARTUP added which is a macro that is run at startup
+        fixed sender vs. author sillyness
+        ~t means ~c until category closure code is finished
+        quoting fixed when sending things to the shell.
 
 0.3.8
-	now utilizes GALE_PROXY if set, otherwise it will only connect to one server
-	all configuration varibles may be set in the enviornment now
-	added locks around errorlog to keep long entries from getting mangled
-	used MVar for reading new config file, potential obscure bug fix.
-	pufflog now atomically replaces pufflog file via renaming, MVars used in BitHandle
-	pufflog is updated every 30 seconds if new puffs have been recieved.
-	** fixed major bug in openssl layer. cleaned up layer and memory usage considerably
-	BitHandle speedups, uses strict fields
+        now utilizes GALE_PROXY if set, otherwise it will only connect to one server
+        all configuration varibles may be set in the enviornment now
+        added locks around errorlog to keep long entries from getting mangled
+        used MVar for reading new config file, potential obscure bug fix.
+        pufflog now atomically replaces pufflog file via renaming, MVars used in BitHandle
+        pufflog is updated every 30 seconds if new puffs have been recieved.
+        ** fixed major bug in openssl layer. cleaned up layer and memory usage considerably
+        BitHandle speedups, uses strict fields
 
 0.3.7
-	fixed some potential problems when compiling with optimization
-	now ~ implicity starts filter addition
-	now compiles with -O -fasm
-	got rid of mySystem wackyness
-	seperated Message out of Puffs (note: add Messages somewhere else)
-	made calling out to external programs more robust
-	pufflog now 'packed'. saves factor of 8
-	code cleanups, moved out Puff and EIO functionality
+        fixed some potential problems when compiling with optimization
+        now ~ implicity starts filter addition
+        now compiles with -O -fasm
+        got rid of mySystem wackyness
+        seperated Message out of Puffs (note: add Messages somewhere else)
+        made calling out to external programs more robust
+        pufflog now 'packed'. saves factor of 8
+        code cleanups, moved out Puff and EIO functionality
 
 0.3.6
-	compose with nothing selected works now
-	added /usr/local as alternate for ghc install
-	added URL's to help screen.
-	changed puff sending keys to p,r,f to match newsreader convention
-	now you add filters with 'c'
-	added PUFFLOG_SIZE config option
-	added EDITOR, GSEND configuration option
-	added -t id/class=ginsu to gsend (doesn't seem to work.)
-	UNICODE support! add a 'CHARSET utf8|latin1|ascii' line to your ginsu.config
-	 make sure your terminal supports the given charset.
+        compose with nothing selected works now
+        added /usr/local as alternate for ghc install
+        added URL's to help screen.
+        changed puff sending keys to p,r,f to match newsreader convention
+        now you add filters with 'c'
+        added PUFFLOG_SIZE config option
+        added EDITOR, GSEND configuration option
+        added -t id/class=ginsu to gsend (doesn't seem to work.)
+        UNICODE support! add a 'CHARSET utf8|latin1|ascii' line to your ginsu.config
+         make sure your terminal supports the given charset.
 
 0.3.5
-	included sample config in binary distribution
-	added ^E ^Y ^F ^B ^U ^D 
-	added 'a' to documentation
-	added emptyPuff thread to help keep gale connections alive. (needs work)
-	added pagedown/pageup with the obvious meaning
-	added 'E' to edit and reload configuration file
-	improved filter parser
-	disposed of unnecisary redraw thread
-	added r,e,c to compose puffs, 'gsend' is needed for sending and $EDITOR
-	   is used for editing the puff.
+        included sample config in binary distribution
+        added ^E ^Y ^F ^B ^U ^D
+        added 'a' to documentation
+        added emptyPuff thread to help keep gale connections alive. (needs work)
+        added pagedown/pageup with the obvious meaning
+        added 'E' to edit and reload configuration file
+        improved filter parser
+        disposed of unnecisary redraw thread
+        added r,e,c to compose puffs, 'gsend' is needed for sending and $EDITOR
+           is used for editing the puff.
 
 0.3.4
-	added '\b' as alias for backspace
-	added '@' and '/' in cat and keyword displays
-	fixed curs_set problem on some terminals
-	added ESC as alternate for help key
-	greatly increased pufflog reading and writing effeciency. (still space inefficient)
-	improved unexpected error handling in some cases.
-	basic cryptography! ginsu will decrypt puffs for which a key can be found in
-	    ~/.gale/auth/private
-	made parsers much more robust against malformed data
-	now writes errors to log file ~/.gale/ginsu.errorlog
+        added '\b' as alias for backspace
+        added '@' and '/' in cat and keyword displays
+        fixed curs_set problem on some terminals
+        added ESC as alternate for help key
+        greatly increased pufflog reading and writing effeciency. (still space inefficient)
+        improved unexpected error handling in some cases.
+        basic cryptography! ginsu will decrypt puffs for which a key can be found in
+            ~/.gale/auth/private
+        made parsers much more robust against malformed data
+        now writes errors to log file ~/.gale/ginsu.errorlog
 
 0.3.3
-	made gale code more robust against network errors
-	finished puff persistance (stored in ~/.gale/ginsu.pufflog)
+        made gale code more robust against network errors
+        finished puff persistance (stored in ~/.gale/ginsu.pufflog)
 
 0.3.2
-	added changelog
-	recenter screen on filter change.
-	added R to reconnect to server
-	added 'f' to add to filter stack
-	started work on s,S
-	add string entry routines (cheezy, needs work)
-	began work on puff persistance
-	
+        added changelog
+        recenter screen on filter change.
+        added R to reconnect to server
+        added 'f' to add to filter stack
+        started work on s,S
+        add string entry routines (cheezy, needs work)
+        began work on puff persistance
+
 0.3.1
-	initial release
+        initial release
diff --git a/docs/ginsu.1 b/docs/ginsu.1
--- a/docs/ginsu.1
+++ b/docs/ginsu.1
@@ -26,9 +26,9 @@
 
 .SH DESCRIPTION
 .BR "ginsu"
-is a client for the 
+is a client for the
 .BR "gale chat system"
-which can be learned about at 
+which can be learned about at
 .BR "<http://gale.org/>"
 
 To get started, generate a configuration file using the following command:
@@ -41,11 +41,11 @@
 .SH USING GINSU
 
 
-            Keys:    
+            Keys:
              <F1>    Help Screen
              <F2>    Main Screen
              <F3>    Presence Status Screen
-                     
+
                 j    next puff
                 k    previous puff
            <Home>    first puff
@@ -56,9 +56,9 @@
      <C-B> <PgUp>    back one page
             <C-D>    forward one half-page
             <C-U>    back one half-page
-                     
+
                 d    Show puff details
-                     
+
               c ~    add new filter
                 u    pop one filter
                 U    pop all filters
@@ -70,15 +70,15 @@
            m[1-9]    set mark - save current filter stack at given key
             [1-9]    recall filter stack saved here with 'm'
            C[1-9]    recall filter stack and add it to current filter stack
-                     
+
             <C-O>    toggle Rot13 body filter
-                     
+
                 f    follow-up to category
                 p    compose new puff
                 r    reply to author
                 g    group reply, sends to recipients and author of puff
                 N    modify public presence string
-                     
+
             <C-R>    reconnect to all servers
                 E    edit and reload configuration file
               q Q    quit program
@@ -93,7 +93,7 @@
     the filters on the stack are conjoined together to determine which
     puffs are visible
 
-Primitive Filters:    
+Primitive Filters:
                 ~a    author
                 ~c    category
                 ~i    case insensitive substring search
@@ -115,7 +115,7 @@
  GINSU_FOO in the environment
  FOO in the config file
  FOO in the environment
- 
+
 .SH FILES
 .TP
 .BR "~/.gale/ginsu.config"
@@ -130,16 +130,16 @@
 .BR "~/.gale/auth/private/*"
 this is where ginsu looks for your private key for decoding incoming encrypted messages.
 .SH BUGS
-To report a bug go to 
+To report a bug go to
 .BR "<http://bugs.ofb.net/cgi-bin/bugzilla/enter_bug.cgi?product=Ginsu>"
-When reporting a bug, include the last few lines of 
+When reporting a bug, include the last few lines of
 .BR "~/.gale/ginsu.errorlog"
 if it seems appropriate as well as the output from
 .BR "ginsu --checkconfig"
 
 .SH AUTHOR
-ginsu was written by John Meacham. 
-The homepage is at 
+ginsu was written by John Meacham.
+The homepage is at
 .BR "<http://repetae.net/john/computer/ginsu/>"
 
 
diff --git a/docs/wiki.css b/docs/wiki.css
--- a/docs/wiki.css
+++ b/docs/wiki.css
@@ -26,7 +26,7 @@
     margin:0;
     background:#fff;
     color:#000;
-}    
+}
 
 div.header img, div.footer img { border:0; padding:0; margin:0; }
 
@@ -65,7 +65,7 @@
     margin-top: 5px;
     border-bottom: 2px solid #000000;
     text-decoration:none;
-    display:block; 
+    display:block;
 }
 
 p a.definition:hover {
@@ -88,7 +88,7 @@
     border:none;
     color:black;
     background-color:#000;
-    height:2px; 
+    height:2px;
     margin-top:2ex;
 }
 div.footer hr { height:4px; }
diff --git a/gen_keyhelp.prl b/gen_keyhelp.prl
--- a/gen_keyhelp.prl
+++ b/gen_keyhelp.prl
@@ -48,8 +48,8 @@
 
     while (<$cfd>) {
         next unless /^bind\s+(\S+)\s+(\w+)\s*$/;
-        push @{$cm{$2}}, $1;   
-    } 
+        push @{$cm{$2}}, $1;
+    }
 
 }
 
@@ -58,7 +58,7 @@
 print "|| <b>Action Name</b> || <b>Default Key</b> || <b>Description</b> ||\n" unless $o{h};
 
 print "module KeyHelpTable(keyHelpTable) where\n\n" if $o{h};
-print "keyHelpTable gk = [\n" if $o{h};                            
+print "keyHelpTable gk = [\n" if $o{h};
 
 while (<>) {
     next if /^\s*(\#.*)?\s*$/;
@@ -67,6 +67,6 @@
     key($1, $2),next if /^(\w*)\s*(.+?)?\s*$/;
 }
 
-print "\t ", join("\n\t,", @hs), "\n";
+print "         ", join("\n        ,", @hs), "\n";
 
-print "\t]\n" if $o{h};                            
+print "        ]\n" if $o{h};
diff --git a/ginsu.cabal b/ginsu.cabal
--- a/ginsu.cabal
+++ b/ginsu.cabal
@@ -1,10 +1,10 @@
 -- vim:et
 Name:		ginsu
-Version:	0.8.1.1
+Version:	0.8.2
 Copyright:	2002-2009 John Meacham <john@repetae.net>
-		2011-2012 Dylan Simon <dylan@dylex.net>
+                2011-2012 Dylan Simon <dylan@dylex.net>
 Author:		John Meacham <john@foo.net>
-		Dylan Simon <dylan@dylex.net>
+                Dylan Simon <dylan@dylex.net>
 Maintainer:     dylan@dylex.net
 License:	MIT
 License-File:	LICENSE
@@ -14,22 +14,22 @@
 Category:	Network,Console
 Build-Type:	Custom
 Cabal-Version:	>= 1.6
-tested-with:    GHC == 7.8.4, GHC == 7.10.1
-extra-source-files: 
+tested-with:    GHC == 7.8.4, GHC == 7.10.2
+extra-source-files:
                 configure
                 ginsu.buildinfo.in
                 config.h.in
-		gen_keyhelp.prl
-		actions.def
-		ginsu-mdk 
-		my_curses.h
-		my_rsa.h
-		Boolean/README
-		docs/Changelog.old
-		docs/ginsu-manual.html
-		docs/wiki.css
-		docs/ginsu-manual.txt
-		docs/ginsu.1
+                gen_keyhelp.prl
+                actions.def
+                ginsu-mdk
+                my_curses.h
+                my_rsa.h
+                Boolean/README
+                docs/Changelog.old
+                docs/ginsu-manual.html
+                docs/wiki.css
+                docs/ginsu-manual.txt
+                docs/ginsu.1
 data-files:     ginsu.config.sample
 
 Source-Repository head
@@ -79,8 +79,9 @@
                 my_curses.c
                 my_rsa.c
     Include-Dirs: .
-    Build-Depends:	
+    Build-Depends:
                 base >= 4 && < 5,
+                async,
                 bytestring,
                 old-locale,
                 containers,
diff --git a/ginsu.config.sample b/ginsu.config.sample
--- a/ginsu.config.sample
+++ b/ginsu.config.sample
@@ -51,7 +51,7 @@
 # MARK_[123..] sets the initial filter for a given marked screen. use the
 # number keys to quickly switch to a mark.
 #
-# the default has '7' show puffs to or from you, 
+# the default has '7' show puffs to or from you,
 # '8' shows traffic about gale and '9' filters spoilers away.
 
 MARK_7 ~c:$GALE_ID ; ~a:$GALE_ID
@@ -60,7 +60,7 @@
 
 
 # beep on any puffs matching the following filters
-BEEP ~c:$GALE_ID   
+BEEP ~c:$GALE_ID
 
 
 # number of puffs to store in pufflog on exit.
@@ -70,7 +70,7 @@
 SCROLLBACK_SIZE 0
 
 # charset, one of utf8, latin1, or ascii. if not set, will try to determine it from $LANG
-# this only affects what format it assumes you are editing puffs in. locale settings are 
+# this only affects what format it assumes you are editing puffs in. locale settings are
 # always used for the UI
 #CHARSET latin1
 
@@ -88,8 +88,8 @@
 
 BROWSER links
 
-apphook WikiWord 
-        '([[:space:]]|^)(([[:upper:]][[:lower:]]+){2,})([[:space:]]|$)' 
+apphook WikiWord
+        '([[:space:]]|^)(([[:upper:]][[:lower:]]+){2,})([[:space:]]|$)'
         '$BROWSER http://wiki.ofb.net/?$2'
         '$2'
 
@@ -99,10 +99,10 @@
 
 #default key bindings
 
-bind <F1> show_help_screen 
+bind <F1> show_help_screen
 bind ? show_help_screen
-#bind <F2> show_main_index 
-bind <F3> show_presence_status 
+#bind <F2> show_main_index
+bind <F3> show_presence_status
 
 bind j next_puff
 bind k previous_puff
@@ -165,4 +165,4 @@
 bind <C-l> redraw_screen
 bind v goto_match
 bind S show_status_screen
- 
+
diff --git a/my_curses.h b/my_curses.h
--- a/my_curses.h
+++ b/my_curses.h
@@ -10,7 +10,7 @@
 #include "config.h"
 #endif
 
-#if HAVE_NCURSESW_NCURSES_H 
+#if HAVE_NCURSESW_NCURSES_H
 #include <ncursesw/ncurses.h>
 #elif HAVE_NCURSES_NCURSES_H
 #include <ncurses/ncurses.h>
