From fd29910a7383fd541595d9196aeffd7b9b16915d Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Fri, 24 May 2019 11:47:24 +0800 Subject: [PATCH 1/5] Add network-bsd --- smtp-mail.cabal | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smtp-mail.cabal b/smtp-mail.cabal index 503586ca..f56e2980 100644 --- a/smtp-mail.cabal +++ b/smtp-mail.cabal @@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: smtp-mail -version: 0.1.4.6 +version: 0.1.4.7 synopsis: Simple email sending via SMTP -- description: homepage: http://github.com/jhickner/smtp-mail @@ -36,6 +36,7 @@ library , filepath , mime-mail , network + , network-bsd , text ghc-options: -Wall -fwarn-tabs From 31a4fd7a76e4345a0587f28705e49c8178bfd518 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Fri, 24 May 2019 14:15:53 +0800 Subject: [PATCH 2/5] Replace deprecated Network usage --- Network/Mail/SMTP.hs | 75 ++++++++++++++++++++------------------------ smtp-mail.cabal | 3 +- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Network/Mail/SMTP.hs b/Network/Mail/SMTP.hs index 77d5cd73..0409b729 100644 --- a/Network/Mail/SMTP.hs +++ b/Network/Mail/SMTP.hs @@ -40,16 +40,13 @@ module Network.Mail.SMTP import Network.Mail.SMTP.Auth import Network.Mail.SMTP.Types -import System.IO -import System.FilePath (takeFileName) - import Control.Monad (unless) -import Data.Monoid import Data.Char (isDigit) -import Network +import Network.Socket import Network.BSD (getHostName) import Network.Mail.Mime hiding (htmlPart, simpleMail) +import qualified Network.Connection as Conn import Data.ByteString (ByteString) import qualified Data.ByteString as B @@ -60,10 +57,10 @@ import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TL import Data.Text.Encoding -data SMTPConnection = SMTPC !Handle ![ByteString] +data SMTPConnection = SMTPC !Conn.Connection ![ByteString] instance Eq SMTPConnection where - (==) (SMTPC a _) (SMTPC b _) = a == b + (==) (SMTPC a _) (SMTPC b _) = Conn.connectionID a == Conn.connectionID b -- | Connect to an SMTP server with the specified host and default port (25) connectSMTP :: HostName -- ^ name of the server @@ -74,8 +71,11 @@ connectSMTP hostname = connectSMTP' hostname 25 connectSMTP' :: HostName -- ^ name of the server -> PortNumber -- ^ port number -> IO SMTPConnection -connectSMTP' hostname port = - connectTo hostname (PortNumber port) >>= connectStream getHostName +connectSMTP' hostname port = do + context <- Conn.initConnectionContext + Conn.connectTo context connParams >>= connectStream getHostName + where + connParams = Conn.ConnectionParams hostname port Nothing Nothing -- | Connect to an SMTP server with the specified host and port @@ -83,8 +83,11 @@ connectSMTPWithHostName :: HostName -- ^ name of the server -> PortNumber -- ^ port number -> IO String -- ^ Returns the host name to use to send from -> IO SMTPConnection -connectSMTPWithHostName hostname port getMailHostName = - connectTo hostname (PortNumber port) >>= connectStream getMailHostName +connectSMTPWithHostName hostname port getMailHostName = do + context <- Conn.initConnectionContext + Conn.connectTo context connParams >>= connectStream getMailHostName + where + connParams = Conn.ConnectionParams hostname port Nothing Nothing -- | Attemp to send a 'Command' to the SMTP server once @@ -115,11 +118,11 @@ tryCommandNoFail tries st cmd expectedReply = do else return (code, msg) -- | Create an 'SMTPConnection' from an already connected Handle -connectStream :: IO String -> Handle -> IO SMTPConnection +connectStream :: IO String -> Conn.Connection -> IO SMTPConnection connectStream getMailHostName st = do (code1, _) <- parseResponse st unless (code1 == 220) $ do - hClose st + Conn.connectionClose st fail "cannot connect to the server" senderHost <- getMailHostName (code, initialMsg) <- tryCommandNoFail 3 (SMTPC st []) (EHLO $ B8.pack senderHost) 250 @@ -129,18 +132,19 @@ connectStream getMailHostName st = do msg <- tryCommand 3 (SMTPC st []) (HELO $ B8.pack senderHost) 250 return (SMTPC st (tail $ B8.lines msg)) -parseResponse :: Handle -> IO (ReplyCode, ByteString) -parseResponse st = do - (code, bdy) <- readLines - return (read $ B8.unpack code, B8.unlines bdy) +parseResponse :: Conn.Connection -> IO (ReplyCode, ByteString) +parseResponse conn = do + (code, bdy) <- readLines + return (read $ B8.unpack code, B8.unlines bdy) where readLines = do - l <- B8.hGetLine st - let (c, bdy) = B8.span isDigit l - if not (B8.null bdy) && B8.head bdy == '-' - then do (c2, ls) <- readLines - return (c2, B8.tail bdy:ls) - else return (c, [B8.tail bdy]) + -- TODO: catch exception if line limit is reached + l <- Conn.connectionGetLine (16 * 1024) conn + let (c, bdy) = B8.span isDigit l + if not (B8.null bdy) && B8.head bdy == '-' + then do (c2, ls) <- readLines + return (c2, B8.tail bdy:ls) + else return (c, [B8.tail bdy]) -- | Send a 'Command' to the SMTP server @@ -210,7 +214,7 @@ sendCommand (SMTPC conn _) meth = do -- | Send 'QUIT' and close the connection. closeSMTP :: SMTPConnection -> IO () -closeSMTP c@(SMTPC conn _) = sendCommand c QUIT >> hClose conn +closeSMTP c@(SMTPC conn _) = sendCommand c QUIT >> Conn.connectionClose conn -- | Sends a rendered mail to the server. sendRenderedMail :: ByteString -- ^ sender mail @@ -309,27 +313,16 @@ simpleMail from to cc bcc subject parts = -- | Construct a plain text 'Part' plainTextPart :: TL.Text -> Part -plainTextPart = Part "text/plain; charset=utf-8" - QuotedPrintableText Nothing [] . TL.encodeUtf8 +plainTextPart body = Part "text/plain; charset=utf-8" + QuotedPrintableText DefaultDisposition [] (PartContent $ TL.encodeUtf8 body) -- | Construct an html 'Part' htmlPart :: TL.Text -> Part -htmlPart = Part "text/html; charset=utf-8" - QuotedPrintableText Nothing [] . TL.encodeUtf8 - --- | Construct a file attachment 'Part' -filePart :: T.Text -- ^ content type - -> FilePath -- ^ path to file - -> IO Part -filePart ct fp = do - content <- BL.readFile fp - return $ Part ct Base64 (Just $ T.pack (takeFileName fp)) [] content +htmlPart body = Part "text/html; charset=utf-8" + QuotedPrintableText DefaultDisposition [] (PartContent $ TL.encodeUtf8 body) lazyToStrict :: BL.ByteString -> B.ByteString lazyToStrict = B.concat . BL.toChunks -crlf :: B8.ByteString -crlf = B8.pack "\r\n" - -bsPutCrLf :: Handle -> ByteString -> IO () -bsPutCrLf h s = B8.hPut h s >> B8.hPut h crlf >> hFlush h +bsPutCrLf :: Conn.Connection -> ByteString -> IO () +bsPutCrLf = Conn.connectionPut diff --git a/smtp-mail.cabal b/smtp-mail.cabal index f56e2980..66b74bac 100644 --- a/smtp-mail.cabal +++ b/smtp-mail.cabal @@ -32,10 +32,11 @@ library , base16-bytestring , base64-bytestring , bytestring + , connection , cryptohash , filepath , mime-mail - , network + , network == 3.0.1.1 , network-bsd , text From ab400d8557e65e40a579facdf827c5f936af1844 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sat, 25 May 2019 05:32:36 +0800 Subject: [PATCH 3/5] Fix crlf and indentations --- Network/Mail/SMTP.hs | 18 ++++++++++-------- Network/Mail/SMTP/Auth.hs | 1 - smtp-mail.cabal | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Network/Mail/SMTP.hs b/Network/Mail/SMTP.hs index 0409b729..95522231 100644 --- a/Network/Mail/SMTP.hs +++ b/Network/Mail/SMTP.hs @@ -72,8 +72,8 @@ connectSMTP' :: HostName -- ^ name of the server -> PortNumber -- ^ port number -> IO SMTPConnection connectSMTP' hostname port = do - context <- Conn.initConnectionContext - Conn.connectTo context connParams >>= connectStream getHostName + context <- Conn.initConnectionContext + Conn.connectTo context connParams >>= connectStream getHostName where connParams = Conn.ConnectionParams hostname port Nothing Nothing @@ -84,8 +84,8 @@ connectSMTPWithHostName :: HostName -- ^ name of the server -> IO String -- ^ Returns the host name to use to send from -> IO SMTPConnection connectSMTPWithHostName hostname port getMailHostName = do - context <- Conn.initConnectionContext - Conn.connectTo context connParams >>= connectStream getMailHostName + context <- Conn.initConnectionContext + Conn.connectTo context connParams >>= connectStream getMailHostName where connParams = Conn.ConnectionParams hostname port Nothing Nothing @@ -134,11 +134,10 @@ connectStream getMailHostName st = do parseResponse :: Conn.Connection -> IO (ReplyCode, ByteString) parseResponse conn = do - (code, bdy) <- readLines - return (read $ B8.unpack code, B8.unlines bdy) + (code, bdy) <- readLines + return (read $ B8.unpack code, B8.unlines bdy) where readLines = do - -- TODO: catch exception if line limit is reached l <- Conn.connectionGetLine (16 * 1024) conn let (c, bdy) = B8.span isDigit l if not (B8.null bdy) && B8.head bdy == '-' @@ -324,5 +323,8 @@ htmlPart body = Part "text/html; charset=utf-8" lazyToStrict :: BL.ByteString -> B.ByteString lazyToStrict = B.concat . BL.toChunks +crlf :: B8.ByteString +crlf = B8.pack "\r\n" + bsPutCrLf :: Conn.Connection -> ByteString -> IO () -bsPutCrLf = Conn.connectionPut +bsPutCrLf conn = Conn.connectionPut conn . flip B.append crlf diff --git a/Network/Mail/SMTP/Auth.hs b/Network/Mail/SMTP/Auth.hs index 1b55c1a5..7d42499f 100644 --- a/Network/Mail/SMTP/Auth.hs +++ b/Network/Mail/SMTP/Auth.hs @@ -13,7 +13,6 @@ import qualified Data.ByteString.Base64 as B64 (encode) import Data.ByteString (ByteString) import Data.List import Data.Bits -import Data.Monoid import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 (unwords) diff --git a/smtp-mail.cabal b/smtp-mail.cabal index 66b74bac..3ae2f269 100644 --- a/smtp-mail.cabal +++ b/smtp-mail.cabal @@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: smtp-mail -version: 0.1.4.7 +version: 0.2.0.0 synopsis: Simple email sending via SMTP -- description: homepage: http://github.com/jhickner/smtp-mail @@ -36,7 +36,7 @@ library , cryptohash , filepath , mime-mail - , network == 3.0.1.1 + , network , network-bsd , text From cb3da1f94991213828b36a5177acce3738c22ad0 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sat, 25 May 2019 06:01:54 +0800 Subject: [PATCH 4/5] Read buffer till empty instead of reading lines with max bytes --- Network/Mail/SMTP.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Network/Mail/SMTP.hs b/Network/Mail/SMTP.hs index 95522231..18c26eaf 100644 --- a/Network/Mail/SMTP.hs +++ b/Network/Mail/SMTP.hs @@ -137,8 +137,14 @@ parseResponse conn = do (code, bdy) <- readLines return (read $ B8.unpack code, B8.unlines bdy) where + getLines c acc = do + res <- Conn.connectionGetChunk c + if B8.null res + then return acc + else getLines c $ B8.append acc res + readLines = do - l <- Conn.connectionGetLine (16 * 1024) conn + l <- getLines conn B8.empty let (c, bdy) = B8.span isDigit l if not (B8.null bdy) && B8.head bdy == '-' then do (c2, ls) <- readLines From 91eed69d2e60a2394ba9ab3b7d26fa91b5374207 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Wed, 19 Jun 2019 11:51:10 +0800 Subject: [PATCH 5/5] Use connectionGetLine and deprecate redundant functions --- Network/Mail/SMTP.hs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Network/Mail/SMTP.hs b/Network/Mail/SMTP.hs index 18c26eaf..83c73e72 100644 --- a/Network/Mail/SMTP.hs +++ b/Network/Mail/SMTP.hs @@ -40,12 +40,14 @@ module Network.Mail.SMTP import Network.Mail.SMTP.Auth import Network.Mail.SMTP.Types +import System.FilePath (takeFileName) + import Control.Monad (unless) import Data.Char (isDigit) import Network.Socket import Network.BSD (getHostName) -import Network.Mail.Mime hiding (htmlPart, simpleMail) +import Network.Mail.Mime hiding (filePart, htmlPart, simpleMail) import qualified Network.Connection as Conn import Data.ByteString (ByteString) @@ -137,14 +139,8 @@ parseResponse conn = do (code, bdy) <- readLines return (read $ B8.unpack code, B8.unlines bdy) where - getLines c acc = do - res <- Conn.connectionGetChunk c - if B8.null res - then return acc - else getLines c $ B8.append acc res - readLines = do - l <- getLines conn B8.empty + l <- Conn.connectionGetLine 1000 conn let (c, bdy) = B8.span isDigit l if not (B8.null bdy) && B8.head bdy == '-' then do (c2, ls) <- readLines @@ -320,11 +316,22 @@ simpleMail from to cc bcc subject parts = plainTextPart :: TL.Text -> Part plainTextPart body = Part "text/plain; charset=utf-8" QuotedPrintableText DefaultDisposition [] (PartContent $ TL.encodeUtf8 body) +{-# DEPRECATED plainTextPart "Use plainPart from mime-mail package" #-} -- | Construct an html 'Part' htmlPart :: TL.Text -> Part htmlPart body = Part "text/html; charset=utf-8" QuotedPrintableText DefaultDisposition [] (PartContent $ TL.encodeUtf8 body) +{-# DEPRECATED htmlPart "Use htmlPart from mime-mail package" #-} + +-- | Construct a file attachment 'Part' +filePart :: T.Text -- ^ content type + -> FilePath -- ^ path to file + -> IO Part +filePart ct fp = do + content <- BL.readFile fp + return $ Part ct Base64 (AttachmentDisposition $ T.pack (takeFileName fp)) [] (PartContent content) +{-# DEPRECATED filePart "Use filePart from mime-mail package" #-} lazyToStrict :: BL.ByteString -> B.ByteString lazyToStrict = B.concat . BL.toChunks