Enviar correos son cURL y SMTP

Hace unos días tuve una tarea que consistía en enviar, mediante SMTP, unos correos a los clientes de mi cliente, algo así como un newsletter, pero al ser pocos correos y sin archivos adjuntos podíamos hacerlo con la configuración del SMTP, que este caso fue del servicio de email que ofrece GoDaddy. Aquí veremos cómo enviar correos son cURL y SMTP.

Como uno es curioso, puedo tardarme diez horas automatizando algo que haciéndolo a mano te llevaría una hora.

Manos a la obra: comencé a diseñar el correo en Stripo y ya con el HTML listo, solo faltaba la parte divertida.

El proceso fue el siguiente:

  1. Tener el archivo separado por comas (CSV) de los correos de destino
  2. Afinar el HTML exportado de Stripo
  3. Obtener la configuración SMTP de GoDaddy
  4. Conectar con cURL al SMPTP y enviar los parámetros, headers necesarios y enviar el HTML
  5. Ejecutar el script
  6. Estar feliz con el resultado.

El último siempre es el más complicado.

Voy a documentar todos los errores que se me fueron presentando y la manera en que los resolví.

Error en cURL: Protocol not supported or disabled en libcurl

Todavía ni empezaba a enviar un simple correo con un “hello mundo” y ya tenía un error al ejecutar el comando básico 🤬🤬🤬:

curl --ssl-reqd --url "smtps://$SERVER:$PORT" \
    --user "$USER:$PASS" \
    --mail-from "$SENDER_ADDRESS" \
    --mail-rcpt "$RECIPIENT_ADDRESS" \
    --upload-file "$MAIL_CONTENT_FILE"

El error:

curl: (1) Protocol "smtps" not supported or disabled in libcurl

# o

curl: (1) Protocol "smtp" not supported or disabled in libcurl

Básicamente es porque la versión de cURL que nos bajamos de los repositorios como dnf en mi caso que estoy en Fedora, está medio limitado en sus funcionalidades.

¿Cómo lo solucionamos el error curl: (1) Protocol “smtps” not supported or disabled in libcurl?

Primero bebemos bajarnos cURL con todas sus features, por decirlo de alguna manera.

wget https://curl.se/download/curl-8.2.1.tar.gz

Una vez descargado, lo vamos a compilar.

Nota: primero puedes intentar descargar las herramientas para poder compilar en caso de que no las tengas.

sudo dnf install make automake gcc gcc-c++ kernel-devel

Compilar cURL

Una vez instaladas, hacemos lo siguiente:

# Descargar cURL
wget https://curl.se/download/curl-8.2.1.tar.gz

# Extraemos los archivos y entramos al directorio
tar zxf curl-8.2.1.tar.gz
cd curl-8.2.1/

# Le damos el directorio en el que hará todo el proceso de
# configuración y compilación
./configure --prefix=/home/<TU_USUARIO>/curl_full

Otro error 🫣:

configure: error: select TLS backend(s) or disable TLS with --without-ssl.

Agregamos la opción para que nos compile con OpenSSL support, que debemos tenerlo instalado:

# Configure
/configure --prefix=/home/manuelhdez/curl_smtp --with-openssl

# Compilar
make
make install

Ahora si, una vez finalizado el proceso ejecutaremos /home/<TU_USUARIO>/curl_full/bin/curl --version podemos ver que está funcionando.

enviar correos son cURL y SMTP

En comparación con nuestra versión global instalada vemos que ahora los protocolos soportados incluyen a smtp y smtps.

Preparando todo para enviar correos son cURL y SMTP

Con cURL completo instalado ya podemos hacer un script en bash para enviar correos son cURL y SMTP.

Para más claridad, vamos a separar los scripts en dos archivos:

  1. send.sh: para solo enviar el correo y otro para procesar el archivo de los emails.
  2. process.sh: para leer el archivo de emails y enviarle uno a uno a send.sh.

Archivo de emails

Vamos a generar un ejemplo de emails. Se vería algo así:

fulano xxx,[email protected]
mengano yyy,[email protected]
perengano wwww,[email protected]

Script de envío

Nombremos al archivo send.sh:

#!/usr/bin/env bash

SERVER='smtpout.secureserver.net'
PORT='465'

USER='<USUARIO_SMTP>'
PASS='<PASS_USUARIO_SMTP>'

SENDER_ADDRESS="[email protected]"
SENDER_NAME='<NOMBRE>'

RECIPIENT_NAME=$2
RECIPIENT_ADDRESS=$3

SUBJECT='Transportation Service in Mazatlan'

/home/<TU_USUARIO>/curl_full/bin/curl \
--no-progress-meter -vvv --ssl-reqd \
--url "smtps://$SERVER:$PORT" \
--user "$USER:$PASS" \
--mail-from "$SENDER_ADDRESS" \
--mail-rcpt "$RECIPIENT_ADDRESS" \
-H "Subject: $SUBJECT" \
-H "From: $SENDER_NAME <$SENDER_ADDRESS>" \
-H "To: $RECIPIENT_NAME <$RECIPIENT_ADDRESS>" \
--upload-file "$1"

Como vemos aquí hacemos uso de las siguientes variables (además de las obvias):

  • $1: es la ruta y el nombre del template HTML que usaremos
  • $2: el nombre de la persona que recibe el correo
  • $3: el email de la persona que recibe el correo

Este script se puede usar de la siguiente manera:

# Damos permisos de ejecución
chmod +x send.sh

# Ejecutar
./send.sh <mi_archivo.html> "<nombre de la persona>" <correo@dominio>

Archivo HTML

Es casi seguro que hasta acá tengas un error entregando el correo por una falla en las cabeceras. Esto es lo que puedes obtener (yo lo obtuve de Gmail):

Reason: There was an error while attempting to deliver your message with [Subject: "??"] to [<[email protected]>](mailto:<[email protected]>). MTA p1plsmtpa20-12.prod.pzx3.secureserver.net received this response from the destination host IP - 142.251.2.26 - &nbsp;550 , 550-5.7.1 [<UNA_IP>] Gmail has detected that this message is not RFC 5322

550-5.7.1 compliant:
550-5.7.1 'From' header is missing.
550-5.7.1 To reduce the amount of spam sent to Gmail, this message has been
550-5.7.1 blocked. For more information, go to
550-5.7.1 &nbsp;[https://support.google.com/mail/?p=RfcMessageNonCompliant](https://support.google.com/mail/?p=RfcMessageNonCompliant)&nbsp;and review
550 5.7.1 RFC 5322 specifications. p14-20020a056a000b4e12b006e6f6dd1735si2339065pfo.371 - gsmtp

¿Y ahora qué 🤔?

El error nos dice que que no tiene Subject (“??”) y que no le hemos enviado el From pero claramente lo estamos enviando en nuestro request de cURL. Si revisamos el comando para enviar el correo, vemos que usamos el switch -H para enviarla.

Pues resulta que también nuestro archivo HTML debe tener indicados dentro del contenido esos datos. ¿Cómo lo hacemos? Debemos agregar esto al inicio del archivo

From: Mi Empresa <[email protected]>
To: Pepe Pecas <[email protected]>
Subject: Nuestro subject

Ahora intentamos enviarlo de nuevo y… 😮‍💨

NADA, que ahora tenemos otro error (espero que a ustedes no les pase):

552 Message contains bare LF and is violating 822.bis section 2.3 from subject

¡¡¡Jesucristo redentor!!!

Pues también resulta que el archivo HTML debe tener bien configurados los saltos de línea. Para no hacerla tan complicada, podemos ir a VS Code y cambiar esto:

Por default no estaría en CRLF, sino en LF, así que damos clic en LF:

lf en visual studio code

Cambiamos a CRLF, guardamos y listo:

enviar correos son cURL y SMTP cambiar saltos de linea

Una prueba más. El correo se ha enviado. 🤩🤩🤩

Vamos a nuestra bandeja y… Oh que la chin… 🥴🥴🥴

html plano en correo

Me muestra el HTML no lo interpreta.

Aquí sí no le hemos indicado nada a nuestro comando, por lo que estaría enviando el archivo en texto plano, así que nuevamente vamos a nuestro archivo HTML y agregamos una línea justo abajo del Subject:

Content-Type: text/html; charset="utf-8"

Guardamos (hay que asegurarnos que los saltos de línea siguen en CRLF) y listo:

html en correo

Por fin lo podemos ver en HTML.

BONUS: enviando los correos en batch con cURL y SMTP

Archivo HTML

Preparamos el archivo HTML index.html, modificando la línea de To::

From: Mi Empresa <[email protected]>
To: CUSTOMER_NAME <CUSTOMER_EMAIL> <-------------------------ESTE
Subject: Servicio de transporte en Mazatlán
Content-Type: text/html; charset="utf-8"

CUSTOMER_NAME y CUSTOMER_EMAIL son los valores que vamos a reemplazar por los datos de nuestro cliente.

Script de proceso de correos

Ahora vamos crear otro script llamado process.sh para leer el archivo de emails y enviarlo uno a uno:

#!/bin/bash

input=$1
counter=0

while IFS="," read -r name email
do
    counter=$((counter+1))

    file_to_use="index_tpl_$counter.html"
    cp "index.html" $file_to_use

    sed -i "s/CUSTOMER_NAME/$name/g" "$file_to_use"
    sed -i "s/CUSTOMER_EMAIL/$email/g" "$file_to_use"

    echo "Enviando a $name $email..."

    ./send.sh "$file_to_use" "$name" $email

    sleep 5
done < "$input"

El proceso es el siguiente:

  1. Leemos el nombre del archivo y lo recogemos con $1 en la variable input.
  2. Hacemos un while leyendo el archivo emails.txt y asignamos el nombre de la persona en name y el email en su respectiva variable email.
  3. En cada iteración aumentamos nuestro counter
  4. Asignamos un nombre de archivo en file_to_use. Este lo vamos a usar como plantilla.
  5. Luego copiamos nuestro index.html con el nombre de file_to_use.
  6. Con el comando sed reemplazamos los dos valores en el template copiado index_X.html: CUSTOMER_NAME y CUSTOMER_EMAIL.
  7. Ejecutamos el comando enviando los valores a send.sh:
    1. El template copiado y con los datos reemplazados
    2. Nombre del cliente
    3. Email del cliente
  8. Duermo 5 segundos para no darle tanta chamba al server.

La manera de llamarlo es el siguiente:

chmod +x process.sh

./process.sh emails.txt

Con esto ya podrás automatizar el envío de emails con cURL y SMTP. Hemos logrado ser felices 🥳 🥳 🥳.

Seguro se puede hacer sin crear archivo de copia, pero ando apurado y con ir borrando los archivos que creemos podemos evitarnos muchas copias del mismo archivo.

Gracias por leer.


Posted

in

, , , , , ,

by