2012-07-03 21 views
9

Estoy tratando de escribir un script simple para enviar un correo a través de mi cuenta de gmail. Pero soy un principiante, así que no es tan simple. Probé google pero no hice hackage, no hay ayuda ni ejemplos.Uso de tls-extra para smtp simple

El problema es que no encontré la manera de usar tls-extra (o tls) para iniciar el intercambio STARTTLS.

Ok, aquí está el código:

import Network 
import Network.TLS.Extra 
import System.IO 
import Text.Printf 

server = "smtp.gmail.com" 
port = 25 --that has to chage, I think 

forever a = a >> forever a 

main = test1 

write :: Handle -> String -> IO() 
write h s = do 
    hPrintf h "%s\r\n" s 
    printf "> %s\n" s 

listen :: Handle -> IO() 
listen h = forever $ hGetLine h >>= putStrLn 

test1 = do h <- connectTo server (PortNumber (fromIntegral port)) 
      hSetBuffering h NoBuffering 
      write h "EHLO" 
      write h "STARTTLS" 
      listen h 

Otra cosa es que yo uso la función de escucha para saber lo que está pasando. Pero no puedo entender cómo usarlo junto con escribir. Es decir, me gustaría poder ver qué está sucediendo en el servidor mientras envío algunos datos.

me encontré con dos funciones que pueden resolver mis problemas:

connectionClient :: CryptoRandomGen g => String -> String -> TLSParams -> g -> IO (TLSCtx Handle) 
forkIO :: IO() -> IO ThreadId 

La primera para TLS y el segundo para enviar y recibir al mismo tiempo. Pero parece que no puede hacer que funcionen.

Espero no estar sucio aquí, cualquier ayuda sería apreciada.

PD: Inglés no es mi nativo.

EDITAR: Por lo tanto, he hecho algunos progresos.

import Network 
import Network.TLS 
import Network.TLS.Extra 
import System.IO 
import Text.Printf 
import Control.Monad (forever) 
import Control.Concurrent (threadDelay) 
import qualified Data.ByteString.Char8 as B 
import Crypto.Random 

serv :: String 
serv = "smtp.gmail.com" 
port :: Int 
port = 25 --that has to chage, I think 

main :: IO() 
main = test1 

write :: Handle -> String -> IO() 
write h s = do 
    hPrintf h "%s\r\n" s 
    printf "> %s\n" s 

listen :: Handle -> IO() 
listen h = forever $ hGetLine h >>= putStrLn 

printSock :: Handle -> String -> IO() 
printSock h s = do write h s 
        hGetLine h >>= putStrLn 
        threadDelay 25 

params :: TLSParams 
params=defaultParams {pConnectVersion=TLS12 
        ,pAllowedVersions=[TLS10, TLS11, TLS12] 
        ,pCiphers=ciphersuite_all} 

test1 = do h <- connectTo serv (PortNumber (fromIntegral port)) 
      hSetBuffering h NoBuffering 
      printSock h "EHLO" 
      printSock h "STARTTLS" 
      --google waits for tls handshake 
      --the problem is from here 
      g <- newGenIO :: IO SystemRandom 
      tlsH <- client params g h 
      handshake tlsH --the handshake is failling 

Aún así, no puedo encontrar la manera de negociar ese tls handshake.

Tengo que decir que estoy bastante sorprendido por la falta de respuesta. Las otras pocas veces tuve un problema, la comunidad fue bastante rápida para ayudar. Pero esto no parece interesarse (solo una constatación). Aquí en SO o incluso en #haskell o developpez.com.

De todos modos algunos consejos sobre google y tls serían bienvenidos. He echado un vistazo al código del cliente msmtp pero francamente no tengo el nivel requerido.

+0

Tengo exactamente el mismo problema. ¿Logramos averiguarlo? – Salil

+0

No es realmente relevante para su pregunta, pero permítame señalar que 'forever' se define en las bibliotecas base. Ver [Hackage] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Monad.html#v:forever). –

Respuesta

5

Puede usar el paquete de conexión que simplifica la conexión del cliente y proporciona todos los bits necesarios para esto. El siguiente código funcionará para conectarse a smtp.gmail.com:

{-# LANGUAGE OverloadedStrings #-} 
import qualified Data.ByteString as B 
import Data.ByteString.Char8() 
import Network.Connection 
import Data.Default 

readHeader con = do 
    l <- connectionGetLine 1024 con 
    putStrLn $ show l 
    if B.isPrefixOf "250 " l 
     then return() 
     else readHeader con 

main = do 
    ctx <- initConnectionContext 
    con <- connectTo ctx $ ConnectionParams 
          { connectionHostname = "smtp.gmail.com" 
          , connectionPort  = 25 
          , connectionUseSecure = Nothing 
          , connectionUseSocks = Nothing 
          } 

    -- read the server banner 
    connectionGetLine 1024 con >>= putStrLn . show 
    -- say ehlo to the smtp server 
    connectionPut con "EHLO\n" 
    -- wait for a reply and print 
    readHeader con 
    -- Tell the server to start a TLS context. 
    connectionPut con "STARTTLS\n" 
    -- wait for a reply and print 
    connectionGetLine 1024 con >>= putStrLn . show 

    -- negociate the TLS context 
    connectionSetSecure ctx con def 

    ------- connection is secure now 
    -- send credential, passwords, ask question, etc. 

    -- then quit politely. 
    connectionPut con "QUIT\n" 
    connectionClose con 
+0

Ya tenía código de trabajo cuando publicó pero su código está más limpio.De todos modos, solo una actualización para decir que desde entonces descarté cualquier solución de código nativo y estoy usando un contenedor personalizado alrededor del comando msmtp que está disponible en dos y nix y que ya trata con todo lo que quieras. – Sarfraz

1

No sé exactamente qué está causando el problema que está teniendo (Mi SMTP está un poco oxidado), pero creo que parte del problema es que se está conectando en el puerto incorrecto. Creo que estos son los parámetros correctos para usar SMTP con Google.

Gmail SMTP server address: smtp.gmail.com 
Gmail SMTP user name: Your full Gmail address (e.g. [email protected]) 
Gmail SMTP password: Your Gmail password 
Gmail SMTP port: 465 
Gmail SMTP TLS/SSL required: yes 

Editar: Hice una búsqueda rápida de Hackage para ver si había un paquete SMTP que le permitiría iniciar sesión con un nombre de usuario y contraseña, usando TLS/SSL, pero no vi nada. (¡Sin embargo, podría haber pasado por alto algo!) Lo más cercano que encontré fue simplemtpclient. No creo que sea compatible con SSL, pero quizás podría ampliarlo o usarlo como guía para escribir el suyo.