wgetTransport :: ConfiguredProgram -> HttpTransport
wgetTransport prog =
    HttpTransport gethttp posthttp posthttpfile True False
  where
    gethttp verbosity uri etag destPath = do
        resp <- runWGet verbosity args
        (code, _err, etag') <- parseResponse uri resp
        return (code, etag')
      where
        args = [ show uri
               , "--output-document=" ++ destPath
               , "--user-agent=" ++ userAgent
               , "--tries=5"
               , "--timeout=15"
               , "--server-response" ]
            ++ concat
               [ ["--header", "If-None-Match: " ++ t]
               | t <- maybeToList etag ]

    posthttp = noPostYet

    posthttpfile verbosity  uri path auth =
        withTempFile (takeDirectory path)
                     (takeFileName path) $ \tmpFile tmpHandle -> do
          (body, boundary) <- generateMultipartBody path
          BS.hPut tmpHandle body
          BS.writeFile "wget.in" body
          hClose tmpHandle
          let args = [ show uri
                     , "--post-file=" ++ tmpFile
                     , "--user-agent=" ++ userAgent
                     , "--server-response"
                     , "--header=Content-type: multipart/form-data; " ++ 
                                              "boundary=" ++ boundary ]
                  ++ concat
                     [ [ "--http-user=" ++ uname
                       , "--http-password=" ++ passwd ]
                     | (uname,passwd) <- maybeToList auth ]

          resp <- runWGet verbosity args
          (code, err, _etag) <- parseResponse uri resp
          return (code, err)

    runWGet verbosity args = do
        -- wget returns its output on stderr rather than stdout
        (_, resp, exitCode) <- getProgramInvocationOutputAndErrors verbosity
                                 (programInvocation prog args)
        -- wget returns exit code 8 for server "errors" like "304 not modified"
        if exitCode == ExitSuccess || exitCode == ExitFailure 8
          then return resp
          else die $ "'" ++ programPath prog
                  ++ "' exited with an error:\n" ++ resp

    -- With the --server-response flag, wget produces output with the full
    -- http server response with all headers, we want to find a line like
    -- "HTTP/1.1 200 OK", but only the last one, since we can have multiple
    -- requests due to redirects.
    --
    -- Unfortunately wget apparently cannot be persuaded to give us the body
    -- of error responses, so we just return the human readable status message
    -- like "Forbidden" etc.
    parseResponse uri resp =
      let codeerr = listToMaybe
                    [ (code, unwords err)
                    | (protocol:codestr:err) <- map words (reverse (lines resp))
                    , "HTTP/" `isPrefixOf` protocol
                    , code <- maybeToList (readMaybe codestr) ]
          mb_etag :: Maybe ETag
          mb_etag  = listToMaybe
                    [ etag
                    | ["ETag:", etag] <- map words (reverse (lines resp)) ]
       in case codeerr of
            Just (i, err) -> return (i, err, mb_etag)
            _             -> statusParseFail uri resp
wgetTransport :: ConfiguredProgram -> HttpTransport
wgetTransport prog =
    HttpTransport gethttp posthttp posthttpfile True False
  where
    gethttp verbosity uri etag destPath = do
        resp <- runWGet verbosity args
        (code, _err, etag') <- parseResponse uri resp
        return (code, etag')
      where
        args = [ show uri
               , "--output-document=" ++ destPath
               , "--user-agent=" ++ userAgent
               , "--tries=5"
               , "--timeout=15"
               , "--server-response" ]
            ++ concat
               [ ["--header", "If-None-Match: " ++ t]
               | t <- maybeToList etag ]

    posthttp = noPostYet

    posthttpfile verbosity  uri path auth =
        withTempFile (takeDirectory path)
                     (takeFileName path) $ \tmpFile tmpHandle -> do
          (body, boundary) <- generateMultipartBody path
          BS.hPut tmpHandle body
          BS.writeFile "wget.in" body
          hClose tmpHandle
          let args = [ show uri
                     , "--post-file=" ++ tmpFile
                     , "--user-agent=" ++ userAgent
                     , "--server-response"
                     , "--header=Content-type: multipart/form-data; " ++ 
                                              "boundary=" ++ boundary ]
                  ++ concat
                     [ [ "--http-user=" ++ uname
                       , "--http-password=" ++ passwd ]
                     | (uname,passwd) <- maybeToList auth ]

          resp <- runWGet verbosity args
          (code, err, _etag) <- parseResponse uri resp
          return (code, err)

    runWGet verbosity args = do
        -- wget returns its output on stderr rather than stdout
        (_, resp, exitCode) <- getProgramInvocationOutputAndErrors verbosity
                                 (programInvocation prog args)
        -- wget returns exit code 8 for server "errors" like "304 not modified"
        if exitCode == ExitSuccess || exitCode == ExitFailure 8
          then return resp
          else die $ "'" ++ programPath prog
                  ++ "' exited with an error:\n" ++ resp

    -- With the --server-response flag, wget produces output with the full
    -- http server response with all headers, we want to find a line like
    -- "HTTP/1.1 200 OK", but only the last one, since we can have multiple
    -- requests due to redirects.
    --
    -- Unfortunately wget apparently cannot be persuaded to give us the body
    -- of error responses, so we just return the human readable status message
    -- like "Forbidden" etc.
    parseResponse uri resp =
      let codeerr = listToMaybe
                    [ (code, unwords err)
                    | (protocol:codestr:err) <- map words (reverse (lines resp))
                    , "HTTP/" `isPrefixOf` protocol
                    , code <- maybeToList (readMaybe codestr) ]
          mb_etag :: Maybe ETag
          mb_etag  = listToMaybe
                    [ etag
                    | ["ETag:", etag] <- map words (reverse (lines resp)) ]
       in case codeerr of
            Just (i, err) -> return (i, err, mb_etag)
            _             -> statusParseFail uri resp
wgetTransport prog =
    HttpTransport gethttp posthttp posthttpfile True False
  where
    gethttp verbosity uri etag destPath = do
        resp <- runWGet verbosity args
        (code, _err, etag') <- parseResponse uri resp
        return (code, etag')
      where
        args = [ show uri
               , "--output-document=" ++ destPath
               , "--user-agent=" ++ userAgent
               , "--tries=5"
               , "--timeout=15"
               , "--server-response" ]
            ++ concat
               [ ["--header", "If-None-Match: " ++ t]
               | t <- maybeToList etag ]

    posthttp = noPostYet

    posthttpfile verbosity  uri path auth =
        withTempFile (takeDirectory path)
                     (takeFileName path) $ \tmpFile tmpHandle -> do
          (body, boundary) <- generateMultipartBody path
          BS.hPut tmpHandle body
          BS.writeFile "wget.in" body
          hClose tmpHandle
          let args = [ show uri
                     , "--post-file=" ++ tmpFile
                     , "--user-agent=" ++ userAgent
                     , "--server-response"
                     , "--header=Content-type: multipart/form-data; " ++ 
                                              "boundary=" ++ boundary ]
                  ++ concat
                     [ [ "--http-user=" ++ uname
                       , "--http-password=" ++ passwd ]
                     | (uname,passwd) <- maybeToList auth ]

          resp <- runWGet verbosity args
          (code, err, _etag) <- parseResponse uri resp
          return (code, err)

    runWGet verbosity args = do
        -- wget returns its output on stderr rather than stdout
        (_, resp, exitCode) <- getProgramInvocationOutputAndErrors verbosity
                                 (programInvocation prog args)
        -- wget returns exit code 8 for server "errors" like "304 not modified"
        if exitCode == ExitSuccess || exitCode == ExitFailure 8
          then return resp
          else die $ "'" ++ programPath prog
                  ++ "' exited with an error:\n" ++ resp

    -- With the --server-response flag, wget produces output with the full
    -- http server response with all headers, we want to find a line like
    -- "HTTP/1.1 200 OK", but only the last one, since we can have multiple
    -- requests due to redirects.
    --
    -- Unfortunately wget apparently cannot be persuaded to give us the body
    -- of error responses, so we just return the human readable status message
    -- like "Forbidden" etc.
    parseResponse uri resp =
      let codeerr = listToMaybe
                    [ (code, unwords err)
                    | (protocol:codestr:err) <- map words (reverse (lines resp))
                    , "HTTP/" `isPrefixOf` protocol
                    , code <- maybeToList (readMaybe codestr) ]
          mb_etag :: Maybe ETag
          mb_etag  = listToMaybe
                    [ etag
                    | ["ETag:", etag] <- map words (reverse (lines resp)) ]
       in case codeerr of
            Just (i, err) -> return (i, err, mb_etag)
            _             -> statusParseFail uri resp