diff --git a/Network/Mail/SMTP.hs b/Network/Mail/SMTP.hs
--- a/Network/Mail/SMTP.hs
+++ b/Network/Mail/SMTP.hs
@@ -4,6 +4,9 @@
       sendMail
     , sendMailWithLogin
     , simpleMail
+    , plainTextPart
+    , htmlPart
+    , filePart
     -- * Types
     , Command(..)
     , Response(..)
@@ -25,12 +28,14 @@
     , connectSMTP
       -- * Operation to a Connection
     , sendCommand
+    , login
     , closeSMTP
     , renderAndSend
     )
     where
 
 import System.IO
+import System.FilePath (takeFileName)
 
 import Control.Monad (unless)
 import Data.Monoid
@@ -111,15 +116,17 @@
 -- | Repeatedly attempt to send a 'Command' to the SMTP server
 tryCommand :: Int -> SMTPConnection -> Command -> ReplyCode
            -> IO ByteString
-tryCommand tries st cmd expectedReply | tries <= 0 = do
-  closeSMTP st
-  fail $ "cannot execute command " ++ show cmd ++
-           ", expected reply code " ++ show expectedReply
 tryCommand tries st cmd expectedReply = do
-  (code, msg) <- sendCommand st cmd
-  if code == expectedReply then
-      return msg else
-      tryCommand (tries - 1) st cmd expectedReply
+    (code, msg) <- sendCommand st cmd
+    if code == expectedReply 
+      then return msg
+      else if tries > 1
+        then tryCommand (tries - 1) st cmd expectedReply
+        else do
+          closeSMTP st
+          fail $ "Unexpected reply to: " ++ show cmd ++ 
+            ", Expected reply code: " ++ show expectedReply ++
+            ", Got this instead: " ++ show code ++ " " ++ show msg
 
 -- | Create an 'SMTPConnection' from an already connected Handle
 connectStream :: Handle -> IO SMTPConnection
@@ -164,7 +171,7 @@
        unless (code == 235) $ fail "authentication failed."
        return (code, msg)
     where command = "AUTH LOGIN"
-          (userB64, passB64) = login username password
+          (userB64, passB64) = encodeLogin username password
 sendCommand (SMTPC conn _) (AUTH at username password) =
     do bsPutCrLf conn command
        (code, msg) <- parseResponse conn
@@ -234,6 +241,10 @@
   renderAndSend con mail
   closeSMTP con
 
+-- | A convenience function that sends 'AUTH' 'LOGIN' to the server
+login :: SMTPConnection -> UserName -> Password -> IO (ReplyCode, ByteString)
+login con user pass = sendCommand con (AUTH LOGIN user pass)
+
 -- | A simple interface for generating a 'Mail' with a plantext body and
 -- an optional HTML body.
 simpleMail :: Address   -- ^ from
@@ -241,24 +252,34 @@
            -> [Address] -- ^ CC
            -> [Address] -- ^ BCC
            -> T.Text -- ^ subject
-           -> TL.Text -- ^ plain body
-           -> Maybe TL.Text -- ^ optional HTML body
+           -> [Part] -- ^ list of parts (list your preferred part last)
            -> Mail
-simpleMail from to cc bcc subject plainBody htmlBody =
+simpleMail from to cc bcc subject parts =
     Mail { mailFrom = from
          , mailTo   = to
          , mailCc   = cc
          , mailBcc  = bcc
          , mailHeaders = [ ("Subject", subject) ]
-         , mailParts =   [ parts plainBody htmlBody ]
+         , mailParts = [parts]
          }
-  where 
-    plainPart plain' = Part "text/plain; charset=utf-8" 
-      QuotedPrintableText Nothing [] $ TL.encodeUtf8 plain'
-    htmlPart html' = Part "text/html; charset=utf-8" 
-      QuotedPrintableText Nothing [] $ TL.encodeUtf8 html'
-    parts plain' Nothing      = [ plainPart plain' ]
-    parts plain' (Just html') = [ plainPart plain', htmlPart html' ]
+
+-- | Construct a plain text 'Part'
+plainTextPart :: TL.Text -> Part
+plainTextPart = Part "text/plain; charset=utf-8" 
+              QuotedPrintableText Nothing [] . TL.encodeUtf8
+
+-- | 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 <- B.readFile fp
+    return $ Part ct Base64 (Just $ T.pack (takeFileName fp)) [] content 
 
 lazyToStrict :: B.ByteString -> S.ByteString
 lazyToStrict = S.concat . B.toChunks
diff --git a/Network/Mail/SMTP/Auth.hs b/Network/Mail/SMTP/Auth.hs
--- a/Network/Mail/SMTP/Auth.hs
+++ b/Network/Mail/SMTP/Auth.hs
@@ -2,7 +2,7 @@
     UserName,
     Password,
     AuthType(..),
-    login,
+    encodeLogin,
     auth,
 ) where
 
@@ -48,11 +48,11 @@
           ikey = B.pack $ B.zipWith xor key' ipad
           okey = B.pack $ B.zipWith xor key' opad
 
-plain :: UserName -> Password -> ByteString
-plain user pass = b64Encode $ intercalate "\0" [user, user, pass]
+encodePlain :: UserName -> Password -> ByteString
+encodePlain user pass = b64Encode $ intercalate "\0" [user, user, pass]
 
-login :: UserName -> Password -> (ByteString, ByteString)
-login user pass = (b64Encode user, b64Encode pass)
+encodeLogin :: UserName -> Password -> (ByteString, ByteString)
+encodeLogin user pass = (b64Encode user, b64Encode pass)
 
 cramMD5 :: String -> UserName -> Password -> ByteString
 cramMD5 challenge user pass =
@@ -63,6 +63,6 @@
     pass'      = toAscii pass
 
 auth :: AuthType -> String -> UserName -> Password -> ByteString
-auth PLAIN    _ u p = plain u p
-auth LOGIN    _ u p = let (u', p') = login u p in B8.unwords [u', p']
+auth PLAIN    _ u p = encodePlain u p
+auth LOGIN    _ u p = let (u', p') = encodeLogin u p in B8.unwords [u', p']
 auth CRAM_MD5 c u p = cramMD5 c u p
diff --git a/smtp-mail.cabal b/smtp-mail.cabal
--- 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.3.0
+version:             0.1.4.0
 synopsis:            Simple email sending via SMTP
 -- description:         
 homepage:            http://github.com/jhickner/smtp-mail
@@ -25,6 +25,7 @@
   build-depends:       
     base >= 4.5 && < 5,
     network,
+    filepath,
     mime-mail,
     bytestring,
     text,
