-------------------------------------------------------------------- -- | -- Module : Web.Twitter.Fetch -- Copyright : (c) Sigbjorn Finne, 2008 -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: so-so -- -- Simple GET\/de-ref of URLs; abstracting out networking backend\/package. -- module Web.Twitter.Fetch ( readContentsURL , readUserContentsURL , postContentsURL , URLString , AuthUser(..) , nullAuthUser , Cookie ) where --import Network.Curl import Network.Browser import Network.HTTP import Network.URI type URLString = String data AuthUser = AuthUser { authUserName :: String , authUserPass :: String } nullAuthUser :: AuthUser nullAuthUser = AuthUser { authUserName = "" , authUserPass = "" } readContentsURL :: URLString -> IO String readContentsURL u = do req <- case parseURI u of Nothing -> fail ("ill-formed URL: " ++ u) Just ur -> return (defaultGETRequest ur) -- don't like doing this, but HTTP is awfully chatty re: cookie handling.. let nullHandler _ = return () (_u, resp) <- browse $ setOutHandler nullHandler >> request req case rspCode resp of (2,_,_) -> return (rspBody resp) _ -> fail ("Failed reading URL " ++ show u ++ " code: " ++ show (rspCode resp)) {- Curl version: readContentsURL :: URLString -> IO String readContentsURL u = do let opts = [ CurlFollowLocation True ] (_,xs) <- curlGetString u opts return xs -} readUserContentsURL :: Maybe AuthUser -> Bool -> Bool -> URLString -> [(String,String)] -> IO ([(String,String)], String) readUserContentsURL mbU doRedir isHead us hdrs = do -- readContentsURL u let hs = case parseHeaders $ map (\ (x,y) -> x++": " ++ y) (addDefaultHeaders 0 hdrs) of Left{} -> [] Right xs -> xs req0 <- case parseURI us of Nothing -> fail ("ill-formed URL: " ++ us) Just ur -> return (defaultGETRequest ur) -- don't like doing this, but HTTP is awfully chatty re: cookie handling.. let req = insertHeaderIfMissing HdrHost (authority (rqURI req0)) $ req0{ rqMethod=if isHead then HEAD else GET , rqHeaders= hs , rqURI = (rqURI req0){uriScheme="",uriAuthority=Nothing} } let nullHandler _ = return () (u, resp) <- browse $ do setOutHandler nullHandler case mbU of Nothing -> return () Just usr -> do setAllowRedirects doRedir setAllowBasicAuth True setAuthorityGen (\ _ _ -> return (Just (authUserName usr,authUserPass usr))) -- setAllowBasicAuth True -- setAuthorityGen (\ _ _ -> return (Just (authUserName usr,authUserPass usr))) {- addAuthority AuthBasic{ auUsername = userName usr , auPassword = userPass usr , auRealm = "" , auSite = nullURI{uriPath="/"} } -} request req case rspCode resp of (2,_,_) -> return (map toP (rspHeaders resp), rspBody resp) (3,_,_) | not doRedir -> return (map toP (rspHeaders resp), rspBody resp) _ -> fail ("Failed reading URL " ++ show u ++ " code: " ++ show (rspCode resp)) postContentsURL :: Maybe AuthUser -> URLString -> [(String,String)] -> [Cookie] -> String -> IO ([Cookie],[(String,String)], String) postContentsURL mbU u hdrs csIn body = do let hs = case parseHeaders $ map (\ (x,y) -> x++": " ++ y) (addDefaultHeaders (length body) hdrs) of Left{} -> [] Right xs -> xs req0 <- case parseURI u of Nothing -> fail ("ill-formed URL: " ++ u) Just ur -> return (defaultGETRequest ur) let req = insertHeaderIfMissing HdrHost (authority (rqURI req0)) $ req0{ rqMethod=POST , rqBody=body , rqHeaders= hs , rqURI = (rqURI req0){uriScheme="",uriAuthority=Nothing} } -- print req -- ,body) let nullHandler _ = return () ((_,rsp),cs) <- browse $ do setOutHandler nullHandler setAllowRedirects True setCookies csIn case mbU of Nothing -> return () Just usr -> do setAllowBasicAuth True setAuthorityGen (\ _ _ -> return (Just (authUserName usr,authUserPass usr))) v <- request req ls <- getCookies return (v,ls) case rspCode rsp of (2,_,_) -> return (cs,map toP (rspHeaders rsp), rspBody rsp) x -> fail ("POST failed - code: " ++ show x ++ ", URL: " ++ u ++ show (rspBody rsp)) toP (Header k v) = (show k, v) addDefaultHeaders :: Int -> [(String,String)] -> [(String,String)] addDefaultHeaders clen hs = addIfMiss "User-Agent" "hs-twitter" $ addIfMiss "Content-Length" (show clen) hs where addIfMiss f v xs = maybe ((f,v):xs) (const xs) (lookup f xs) {- Curl versions: readUserContentsURL :: User -> URLString -> IO String readUserContentsURL u url = do let opts = [ CurlHttpAuth [HttpAuthAny] , CurlUserPwd (authUserName u ++ case userPass u of {"" -> ""; p -> ':':p }) , CurlFollowLocation True ] (_,xs) <- curlGetString url opts return xs postContentsURL :: URLString -> [(String,String)] -> String -> IO String postContentsURL u hdrs body = do let opts = [ CurlCustomRequest "POST" , CurlFollowLocation True , CurlPost True , CurlPostFields [body] , CurlHttpTransferDecoding False ] ++ [CurlHttpHeaders (map ( \ (x,y) -> (x ++ ':':y)) hdrs)] rsp <- curlGetResponse u opts case respStatus rsp `div` 100 of 2 -> return (respBody rsp) x -> fail ("POST failed - code: " ++ show x ++ ", URL: " ++ u) -}