packages feed

pubsub-0.10: examples/Feeder.hs

--------------------------------------------------------------------
-- |
-- Module    : Feeder
-- Copyright : (c) Sigbjorn Finne, 2009
-- License   : BSD3
--
-- Maintainer: Sigbjorn Finne <sof@forkIO.com>
-- Stability : provisional
-- Portability:
--
--------------------------------------------------------------------
module Main(main) where

import Network.Connection
import Web.Utils.HTTP

import Web.Types
import Web.PubSub.Types
import Web.PubSub

import System.IO
import System.Environment
import System.Exit
import Control.Concurrent

import Web.Utils.Fetch
import Text.Atom.Feed

-- where we will be listening for incoming pubsub events; sub and
-- feed updates.
-- [If you are using the fastcgi proxy script pubsub.fcgi,
--  it needs to be changed also if you modify the location below.]
servOptions :: ServerOptions
servOptions =
  Network.Connection.serverOpts{servInterface=Just "localhost"
                               ,servPort=Just 8080
			       }


-- where the PubSubHub protocol will callback:
endpoint="http://hs-pubsub.example.com/pubsub.fcgi"

-- I _think_ you can leave out the 'method' bit...
sub_callback = endpoint++"?method=notify"

-- if you need to authenticate with the hub, config this
-- (superfeedr requires it.)
authUser :: Maybe AuthUser
authUser = Nothing
--authUser = Just nullAuthUser{authUserName="bobo", authUserPass="alice"}



-- URL of hub to use
sub_hub :: URLString
--sub_hub = "http://pubsubhubbub.appspot.com/"
sub_hub = "http://superfeedr.com/hubbub"


type Handler = Request -> IO Response

type MethodTable = [(String, Handler)]

createServer :: IO Server
createServer = do
  serv <- newServer servOptions
  return serv

startServer :: Bool -> MethodTable -> Server -> IO ()
startServer oneShot tab serv 
 | oneShot   = forkIO (acceptor serv) >> return ()
 | otherwise = acceptor serv
 where
   acceptor s = do
     (c,_) <- acceptConnection s
     forkIO (handleConnection tab c)
     if oneShot
      then return ()
      else acceptor s

handleConnection :: MethodTable -> Connection -> IO ()
handleConnection tab c = do
  ls <- hGetLine (coHandle c)
  case jsonRequest ls of
    Nothing -> do
      hPutStrLn (coHandle c) (jsonResponse errorResponse)
    Just req ->
      case lookup "method" (reqVars req) >>= \ m -> lookup m tab of
        Nothing -> do
	  case tab of
	    ((_,r):_) -> do
	      rsp <- r req
    	      hPutStrLn (coHandle c) (jsonResponse rsp)
	    _ -> hPutStrLn (coHandle c) (jsonResponse errorResponse)
	Just hdlr -> do
	  rsp <- hdlr req
	  hPutStrLn (coHandle c) (jsonResponse rsp)
  finishSession c

finishSession :: Connection -> IO ()
finishSession c = Prelude.catch (closeConnection c) (\ _ -> return ())

main :: IO ()
main = do
  ls <- getArgs
  top <-
    case ls of
      (t:_) -> return t
      _ -> do
        putStrLn "Usage: feeder <topic URL>"
	putStrLn "(foo$ feeder 'http://search.twitter.com/search.atom?q=opera' )"
	putStrLn "[Hence, not adding new subs but listening to existing ones..]"
	return ""
  serv <- createServer
  (subStart, tab) <- setupSubscription top
  startServer True tab serv
  Prelude.catch subStart (\ e -> print e >> hFlush stdout >> return ())
  startServer False tab serv

setupSubscription :: String -> IO (IO (), MethodTable)
setupSubscription top = do
  let sub = Subscribe{ subMode     = True
                     , subCallback = sub_callback
		     , subTopic    = top
		     , subVerify   = [VerifySync True]
		     , subVerifyToken = Nothing
		     , subLeaseSecs  = Nothing
		     }
  return (if null top then return () else subscribe authUser sub_hub sub, 
    [ ("notify", handleCalls sub)
    ])

handleCalls :: Subscribe -> Handler
handleCalls s req = do
   case lookup "hub.mode" (reqVars req) of
    Just "subscribe" -> do
--       hPutStrLn stdout "subscribe-verify.."
--       hPutStrLn stdout (show $ reqVars req)
       let r = handleVerify s req
--       hPutStrLn stdout (respBody r)
       return r
    Just "publish"   -> handleNewContent req
    _ -> do
      case lookup "method" (reqVars req) of
        Just "notify" | reqMethod req == "POST" -> handleNewContent req
	_ -> do
	  case lookup "Referer" (reqHeaders req) of
	    Just "superfeedr.com" -> return superfeedrResp
	    _ -> handleNewContent req
 where
  handleNewContent req = do
    (r, mbf) <- notifyContent req
    case mbf of
      Just f -> do
       putStrLn ("new entries - " ++ feedId f)
       mapM_ (\ e -> putStrLn (txtToString $ entryTitle e)) (feedEntries f)
      _ -> return ()
    return r

superfeedrResp :: Response
superfeedrResp = errorResponse{respStatus=200,respBody="736f66333639"}

errorResponse :: Response
errorResponse =
  Response{  respStatus  = 404
          ,  respHeaders = []
	  ,  respBody    = ""
	  }