{-# LANGUAGE OverloadedStrings #-}

{- |
Description: terms for sending mail.
-}

module Network.Mail.SMTP.Send (

    send

  ) where

import Control.Monad.IO.Class

import Network.Mail.SMTP.Types
import Network.Mail.SMTP.SMTP
import Network.Mail.Mime

import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Data.ByteString.Char8 (pack)
import Data.Text.Encoding (encodeUtf8)
import Data.ByteString.Lazy.Search (replace)

-- | Attempt to send an email.
--   This involves sending MAIL, RCPT, and DATA commands.
send :: Mail -> SMTP ()
send mail = do
    content <- liftIO $ renderMail' mail
    sendRendered from to content
  where
    from = enc $ mailFrom mail
    to = map enc $ mailTo mail
    enc = encodeUtf8 . addressEmail

-- | Attempt to send "rendered" mail. First argument is a coding of the
--   sender address, second is a coding of each recipient address, and third
--   is the message body.
--   This function will escape any "\r\n.\r\n" pattern, which would otherwise
--   result in a premature ending to the message.
sendRendered :: B.ByteString -> [B.ByteString] -> BL.ByteString -> SMTP ()
sendRendered from to content = do
    command (MAIL from)
    expectCode 250
    -- TODO TBD expect a 250 for each recipient? Isn't it OK to have some
    -- of them fail?
    mapM_ (\r -> command (RCPT r) >> expectCode 250) to
    command DATA
    expectCode 354
    bytes (BL.toStrict escapedContent)
    -- We have to manually put in the .
    bytes (pack ".")
    expectCode 250

  where

    escapedContent :: BL.ByteString
    escapedContent = replace pattern substitution content

    pattern :: B.ByteString
    pattern = "\r\n.\r\n"

    substitution :: BL.ByteString
    substitution = "\r\n..\r\n"