Initial commit
This commit is contained in:
commit
24e8f1c7f9
34
HaskellNet-SSL.cabal
Normal file
34
HaskellNet-SSL.cabal
Normal file
@ -0,0 +1,34 @@
|
||||
name: HaskellNet-SSL
|
||||
synopsis: Helpers to connect to SSL/TLS mail servers with HaskellNet
|
||||
version: 0.1.0.0
|
||||
description: This package ties together the HaskellNet and connection
|
||||
packages to make it easy to open IMAP and SMTP connections
|
||||
over SSL.
|
||||
homepage: https://github.com/dpwright/HaskellNet-SSL
|
||||
license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Daniel P. Wright
|
||||
maintainer: dani@dpwright.com
|
||||
copyright: (c) 2013 Daniel P. Wright
|
||||
category: Network
|
||||
build-type: Simple
|
||||
cabal-version: >=1.8
|
||||
data-files: README.md
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: git://github.com/dpwright/HaskellNet-SSL.git
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
ghc-options: -Wall
|
||||
exposed-modules: Network.HaskellNet.IMAP.SSL
|
||||
Network.HaskellNet.POP3.SSL
|
||||
Network.HaskellNet.SMTP.SSL
|
||||
other-modules: Network.HaskellNet.SSL
|
||||
build-depends: base ==4.5.*,
|
||||
HaskellNet >= 0.3.1,
|
||||
connection >= 0.1.3,
|
||||
network >= 2.3,
|
||||
bytestring,
|
||||
data-default
|
||||
30
LICENSE
Normal file
30
LICENSE
Normal file
@ -0,0 +1,30 @@
|
||||
Copyright (c) 2013, Daniel P. Wright
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Daniel P. Wright nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
HaskellNet-SSL
|
||||
--------------
|
||||
|
||||
This package ties together the excellent [HaskellNet][HaskellNet] and
|
||||
[connection][connection] packages to make it easy to open IMAP and SMTP
|
||||
connections over SSL. This is a simple "glue" library; all credit for a)
|
||||
connecting to IMAP/SMTP servers and b) making an SSL connection goes to the
|
||||
aforementioned libraries.
|
||||
|
||||
[HaskellNet]: https://github.com/jtdaugherty/HaskellNet
|
||||
[connection]: https://github.com/vincenthz/hs-connection
|
||||
15
src/Network/HaskellNet/IMAP/SSL.hs
Normal file
15
src/Network/HaskellNet/IMAP/SSL.hs
Normal file
@ -0,0 +1,15 @@
|
||||
module Network.HaskellNet.IMAP.SSL
|
||||
( connectIMAPSSL
|
||||
, connectIMAPSSLPort
|
||||
) where
|
||||
|
||||
import Network.Socket.Internal (PortNumber)
|
||||
import Network.HaskellNet.IMAP.Connection
|
||||
import Network.HaskellNet.IMAP
|
||||
import Network.HaskellNet.SSL
|
||||
|
||||
connectIMAPSSL :: String -> IO IMAPConnection
|
||||
connectIMAPSSL hostname = connectIMAPSSLPort hostname 993
|
||||
|
||||
connectIMAPSSLPort :: String -> PortNumber -> IO IMAPConnection
|
||||
connectIMAPSSLPort hostname port = connectSSL hostname port >>= connectStream
|
||||
15
src/Network/HaskellNet/POP3/SSL.hs
Normal file
15
src/Network/HaskellNet/POP3/SSL.hs
Normal file
@ -0,0 +1,15 @@
|
||||
module Network.HaskellNet.POP3.SSL
|
||||
( connectPop3SSL
|
||||
, connectPop3SSLPort
|
||||
) where
|
||||
|
||||
import Network.Socket.Internal (PortNumber)
|
||||
import Network.HaskellNet.POP3.Connection
|
||||
import Network.HaskellNet.POP3
|
||||
import Network.HaskellNet.SSL
|
||||
|
||||
connectPop3SSL :: String -> IO POP3Connection
|
||||
connectPop3SSL hostname = connectPop3SSLPort hostname 995
|
||||
|
||||
connectPop3SSLPort :: String -> PortNumber -> IO POP3Connection
|
||||
connectPop3SSLPort hostname port = connectSSL hostname port >>= connectStream
|
||||
64
src/Network/HaskellNet/SMTP/SSL.hs
Normal file
64
src/Network/HaskellNet/SMTP/SSL.hs
Normal file
@ -0,0 +1,64 @@
|
||||
module Network.HaskellNet.SMTP.SSL
|
||||
( connectSMTPSSL
|
||||
, connectSMTPSSLPort
|
||||
, connectSMTPSTARTTLS
|
||||
, connectSMTPSTARTTLSPort
|
||||
) where
|
||||
|
||||
import Network.Socket.Internal (PortNumber)
|
||||
import Network.HaskellNet.SMTP
|
||||
import Network.HaskellNet.SSL
|
||||
|
||||
import Network.HaskellNet.BSStream
|
||||
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
|
||||
import Control.Monad
|
||||
import Data.IORef
|
||||
|
||||
connectSMTPSSL :: String -> IO SMTPConnection
|
||||
connectSMTPSSL hostname = connectSMTPSSLPort hostname 465
|
||||
|
||||
connectSMTPSSLPort :: String -> PortNumber -> IO SMTPConnection
|
||||
connectSMTPSSLPort hostname port = connectSSL hostname port >>= connectStream
|
||||
|
||||
connectSMTPSTARTTLS :: String -> IO SMTPConnection
|
||||
connectSMTPSTARTTLS hostname = connectSMTPSTARTTLSPort hostname 587
|
||||
|
||||
connectSMTPSTARTTLSPort :: String -> PortNumber -> IO SMTPConnection
|
||||
connectSMTPSTARTTLSPort hostname port = connectSTARTTLS hostname port >>= connectStream
|
||||
|
||||
connectSTARTTLS :: String -> PortNumber -> IO BSStream
|
||||
connectSTARTTLS hostname port = do
|
||||
(bs, startTLS) <- connectPlain hostname port
|
||||
|
||||
greeting <- bsGetLine bs
|
||||
failIfNot bs 220 $ parseResponse greeting
|
||||
|
||||
bsPut bs $ B.pack "HELO\r\n"
|
||||
getResponse bs >>= failIfNot bs 250
|
||||
bsPut bs $ B.pack "STARTTLS\r\n"
|
||||
getResponse bs >>= failIfNot bs 220
|
||||
|
||||
startTLS
|
||||
|
||||
prefixRef <- newIORef [greeting]
|
||||
return $ bs {bsGetLine = prefixedGetLine prefixRef (bsGetLine bs)}
|
||||
where parseResponse = parse . B.unpack
|
||||
parse s = (getCode s, s)
|
||||
getCode = read . head . words
|
||||
getResponse bs = liftM parseResponse $ bsGetLine bs
|
||||
|
||||
failIfNot :: BSStream -> Integer -> (Integer, String) -> IO ()
|
||||
failIfNot bs code (rc, rs) = when (code /= rc) closeAndFail
|
||||
where closeAndFail = bsClose bs >> fail ("cannot connect to server: " ++ rs)
|
||||
|
||||
-- This is a bit of a nasty hack. Network.HaskellNet.SMTP.connectStream
|
||||
-- expects to receive a status 220 from the server as soon as it connects,
|
||||
-- but we've intercepted it in order to establish a STARTTLS connection.
|
||||
-- This allows us to keep hold of the original greeting and pass it back to
|
||||
-- HaskellNet.
|
||||
prefixedGetLine :: IORef [B.ByteString] -> IO B.ByteString -> IO B.ByteString
|
||||
prefixedGetLine prefix rawGetLine = readIORef prefix >>= deliverLine
|
||||
where deliverLine [] = rawGetLine
|
||||
deliverLine (l:ls) = writeIORef prefix ls >> return l
|
||||
44
src/Network/HaskellNet/SSL.hs
Normal file
44
src/Network/HaskellNet/SSL.hs
Normal file
@ -0,0 +1,44 @@
|
||||
module Network.HaskellNet.SSL ( connectSSL
|
||||
, connectPlain
|
||||
) where
|
||||
|
||||
import Network.Connection
|
||||
import Network.HaskellNet.BSStream
|
||||
import Network.Socket.Internal (PortNumber)
|
||||
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.Default
|
||||
|
||||
type STARTTLS = IO ()
|
||||
|
||||
maxLineLength :: Int
|
||||
maxLineLength = 10000
|
||||
|
||||
connectionGetBytes :: Connection -> Int -> IO B.ByteString
|
||||
connectionGetBytes = loop B.empty where
|
||||
loop buf _ 0 = return buf
|
||||
loop buf c l = connectionGet c l >>= nextIteration
|
||||
where nextIteration b = loop (buf `B.append` b) c $ l - B.length b
|
||||
|
||||
connectionToStream :: Connection -> BSStream
|
||||
connectionToStream c = BSStream
|
||||
{ bsGet = connectionGetBytes c
|
||||
, bsPut = connectionPut c
|
||||
, bsFlush = return ()
|
||||
, bsClose = connectionClose c
|
||||
, bsIsOpen = return True
|
||||
, bsGetLine = connectionGetLine maxLineLength c
|
||||
}
|
||||
|
||||
connectSSL :: String -> PortNumber -> IO BSStream
|
||||
connectSSL hostname port = do
|
||||
c <- initConnectionContext >>= flip connectTo params
|
||||
return $ connectionToStream c
|
||||
where params = ConnectionParams hostname port (Just def) Nothing
|
||||
|
||||
connectPlain :: String -> PortNumber -> IO (BSStream, STARTTLS)
|
||||
connectPlain hostname port = do
|
||||
ctx <- initConnectionContext
|
||||
c <- connectTo ctx params
|
||||
return (connectionToStream c, connectionSetSecure ctx c def)
|
||||
where params = ConnectionParams hostname port Nothing Nothing
|
||||
Loading…
Reference in New Issue
Block a user