Archivo

Entradas Etiquetadas ‘gmail’

OSX Postfix Relay – envio de correos usando OSX y Postfix

Lunes, 15 de Febrero de 2010

Todo este lío comenzó cuando me tocó configurar a mi Mac Mini (la cual es mi servidor headless estos días) para que poder enviar correos desde PHP:

< ?php
$to = "josevnz@mydomain.com";
$subject = "Test from tepuyes server!";
$body = "Hi,\n\nHow are you?";
if (mail($to, $subject, $body)) {
echo("

Message successfully sent!");
} else {
echo("

Message delivery failed...

");
}
?>

Odio a PHP. En Windows se puede especificar un servidor SMTP pero en UNIX este lenguaje utiliza Sendmail o lo que haya por debajo. Se puede utilizar Pear PHP mail pero eso implica que tengo que cambiar mi código.

Ahhh, nada como JavaMail para estos casos :D

Bueno, volviendo al tema que nos interesa, los correos nunca salían de la cola. Y es que mi proveedor de Internet, Cablevision, bloquea cualquier conexión a servidores de correo en los puertos 25 en un intento por detener el SPAM.

Intenté utilizar el servidor de correo que ellos sugieren (mail.optonline.net) pero este se quejaba que mi maquina no tenia un registro A en DNS que pudiera ser resuelto (sorpresa, es una máquina detrás del firewall). Traté de utilizar el servidor de correo de la compañia que hospeda a mi dominio en DNS usando autenticación, y para ello configuré lo siguiente en el archivo /etc/postfix/main.conf:

relayhost = smtpout.secureserver.net:3535
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
smtp_tls_CApath = /etc/postfix/certs
smtp_tls_session_cache_database = btree:/etc/postfix/smtp_scache
smtp_tls_session_cache_timeout = 3600s
smtp_tls_loglevel = 1
tls_random_source = dev:/dev/urandom

Y en el archivo /etc/postfix/sasl_passwd pusé mi usuario y clave con el cual voy a hacer el relay:


smtpout.secureserver.net: josevnz@mydomain.com:mycoolpassword

Necesitamos usar un certificado digital. La gente de Verisign ofrece uno el cual podemos bajar desde aqui: https://www.verisign.com/support/roots.html (en este caso yo lo copié en /private/tmp/roots.zip)

Luego un poco de carpintería ya que hay que decirle a Postfix que use los nuevos parámetros:


sudo mv /private/tmp/roots.zip /etc/postfix/certs
sudo unzip -j roots.zip
sudo openssl x509 -inform der -in thawte\ Primary\ Root\ CA\ -\ G2_ECC.cer -out thawte\ Primary\ Root\ CA\ -\ G2_ECC.pem
sudo c_rehash /etc/postfix/certs
sudo postmap /etc/postfix/sasl_passwd
sudo postfix stop
sudo postfix start

Sin embargo el servidor de ‘relay’ que decidí utilizar tiene problemas de DNS:

Tepuyes-Server:certs josevnz$ sudo mailq
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
41D3C1201BB 348 Mon Feb 15 18:12:43 josevnz@Tepuyes-Server.local
(Host or domain name not found. Name service error for name=smtpout.secureserver.net type=MX: Host not found, try again)
josevnz@mydomain.com

¡Que vaina!. Sin darme por vencido decidí utilizar a Gmail como relay, cambiando lo siguiente en /etc/postfix/main.cf:

relayhost = smtp.gmail.com:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
smtp_tls_CApath = /etc/postfix/certs
smtp_tls_session_cache_database = btree:/etc/postfix/smtp_scache
smtp_tls_session_cache_timeout = 3600s
smtp_tls_loglevel = 1
tls_random_source = dev:/dev/urandom

Y además en etc/postfix/sasl_passwd:

smtp.gmail.com micuentaengmail@gmail.com:UltraSuperDuperPassword

Reiniciamos a Postfix una vez mas:

sudo postmap /etc/postfix/sasl_passwd
sudo postfix stop
sudo postfix start

Esto me permitió resolver el problema. Comparto este truco el cual funciona al menos en OSX Leopard y ahora me permite mandar correos desde PHP :)

php , , ,

Borrando correos masivamente usando Javamail

Martes, 30 de Diciembre de 2008

Este articulo es una mejora al código que escribí anteriormente. Jugando un poco con Gmail me di cuenta que los correos aún pueden ser recuperados usando POP3, pese a que fueron borrados con IMAP. Realmente bizarro. Así que modifiqué el programa para que manejara un poco mejor el caso de POP3 o IMAP según las propiedades.

Aquí está el código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package com.kodegeek.blog.mail;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.mail.Session;
import javax.mail.Flags.Flag;
import javax.mail.internet.MimeMessage;
import javax.mail.Message;
 
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Store;
 
import com.sun.mail.imap.IMAPFolder;
 
import java.io.FileInputStream;
 
/**
 * I wrote this program to erase ALL the email on my GMAIL account. I have over 40,000 email so manually doing so was not an option :)
 * Please check the following resources:
 * <ul>
 * <li> http://java.sun.com/products/javamail/javadocs/index.html</li>
 * <li> http://java.sun.com/developer/onlineTraining/JavaMail/contents.html</li>
 * <li> http://java.sun.com/products/javamail/javadocs/com/sun/mail/imap/package-summary.html</li>
 * <li> http://java.sun.com/products/javamail/downloads/index.html</li>
 * </ul>
 * Also define a properties file with the following (adjust it to your configuration):
 * # As defined by the Javadoc
 * <ul>
 * <li> mail.user=name.lastname@gmail.com</li>
 * <li> mail.password=zzz</li>
 * <li> mail.store.host=pop.imap.gmail.com</li>
 * <li> mail.store.protocol=imap</li>
 * <li> mail.imap.port=993</li>
 * <li> mail.imap.socketFactory.port=993</li>
 * <li> mail.imap.socketFactory.class=javax.net.ssl.SSLSocketFactory</li>
 * </ul>
 * More examples:
 * <ul>
 * <li>POP3: -Dmail.user=name.lastname@gmail.com  -Dmail.password=XXXX -Dmail.store.host=pop.gmail.com -Dmail.store.protocol=pop3 -Dmail.store.folder=INBOX -Dmail.pop3.port=995 -Dmail.pop3.socketFactory.port=995 -Dmail.pop3.socketFactory.class=javax.net.ssl.SSLSocketFactory</li>
 * <li>IMAP: -Dmail.user=name.lastname@gmail.com  -Dmail.password=XXXX -Dmail.store.host=imap.gmail.com -Dmail.store.protocol=imap -Dmail.store.folder=INBOX -Dmail.imap.port=993 -Dmail.imap.socketFactory.port=993 -Dmail.imap.socketFactory.class=javax.net.ssl.SSLSocketFactory</li>
 * </ul>
 * License: GPL
 * http://kodegeek.com
 * @author Jose Vicente Nunez Zuleta
 * @version 12/28/2008
 */
public final class MailBulkEraser {
 
	/**
	 * Command line processing
	 * @param args [0] Location of the properties configuration file, If not provided then will use system defaults (defined with -D)
	 * @throws Exception
	 * @since 0.1
	 */
	public static void main(String [] args) throws Exception {
 
		Logger logger = Logger.getLogger(MailBulkEraser.class.getName());
 
		Properties props = System.getProperties();
		if (args.length == 1) {
			props.load(new FileInputStream(args[0]));
		}
		Session session = Session.getInstance(props, null);
		session.setDebug(Boolean.parseBoolean(props.getProperty("debug")));
		Store store = session.getStore(props.getProperty("mail.store.protocol"));
		logger.info(String.format("Connecting to: %s@%s", props.getProperty("mail.user"), props.getProperty("mail.store.host")));
		store.connect(props.getProperty("mail.store.host"), props.getProperty("mail.user"), props.getProperty("mail.password"));
		Folder folder = store.getDefaultFolder();
		logger.info("Got default folder. Erasing emails from ALL the folders");
		Folder [] folders = folder.list();
		long messgCnt = 0;
		for(Folder currFolder: folders) {
 
			try {
 
				if ((currFolder.getType() & Folder.HOLDS_MESSAGES) == 0) {
					continue; // Skip this folder type
				}
				logger.info(String.format("Opening folder: %s, num messages; %d", currFolder.getFullName(), currFolder.getMessageCount()));
				currFolder.open(Folder.READ_WRITE);
				Message [] messages = currFolder.getMessages();
 
				// Mark all the messages for delete
				for (Message message: messages) {
					try {
						if (! currFolder.isOpen()) {
							logger.warning(String.format("Folder %s is closed. Moving to next folder", currFolder.getFullName()));
							break;
						}
						message.setFlag(Flag.DELETED, true);
						MimeMessage currMessage = (MimeMessage) message;
						logger.info(String.format("Deleting mail subject: '%s', folder: '%s', date: '%s'", currMessage.getSubject(), currMessage.getFolder().getFullName(), currMessage.getSentDate()));
						if (currFolder instanceof IMAPFolder) {
							messages = currFolder.expunge(); // Confirm delete for all the messages on the current folder
						}
						messgCnt += 1;
					}  catch (Throwable thrbl) {
						logger.log(Level.WARNING, "There was a problem erasing the message, ignoring", thrbl);
						continue;
					}
				}
 
				logger.info(String.format("Messages erased on folder %s: %d", currFolder.getFullName(), messages.length));
				currFolder.close(true);
 
			} catch (MessagingException messgExp) {
				logger.log(Level.WARNING, "There was a problem, ignoring", messgExp);
			}
 
		}
		logger.info(String.format("Total number of erased messsages: %d", messgCnt));
		store.close();
		System.exit(0);
	}
 
}

Nota de los cambios:

Que lo disfruten :) .

java, kodegeek, opensource , , , , , ,

¿Como borrar 40000 correos en Gmail? Fácil, usando Javamail e IMAP

Domingo, 28 de Diciembre de 2008

Este fin de semana me decidí a poner un poco de orden en mis cuentas de correo; Una de las cosas que me obligó a ello es que mi cuenta paga de Yahoo expiró y aún ando debatiendo si debo pagar $20 por una cuenta de correo que no ofrece nada por encima de Gmail. Al momento de revizar mi cuenta de Gmail me encuentro con que estoy usando por encima de 1 GB en correo con más de 40000 correos.

Si, es increíble la cantidad de basura que se acumula en dos años :)

Definitivamente no estaba dispuesto a perder mi tiempo borrando tal cantidad de correos a mano, en vez de eso me decicí a automatizar la tarea usando Java y el hecho de que Gmail soporta POP / IMAP (Javamail cuenta con accesso a IMAP comp arte de su API). Una vez leido un poco abrí mi Eclipse y el resultado es el código que viene a continuación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.kodegeek.blog.mail;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.mail.Session;
import javax.mail.Flags.Flag;
import javax.mail.internet.MimeMessage;
import javax.mail.Message;
 
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Store;
 
import java.io.FileInputStream;
 
/**
 * I wrote this program to erase ALL the email on my GMAIL account. I have over 40,000 email so manually doing so was not an option :)
 * Please check the following resources:
 * <ul>
 * <li> http://java.sun.com/products/javamail/javadocs/index.html</li>
 * <li> http://java.sun.com/developer/onlineTraining/JavaMail/contents.html</li>
 * <li> http://java.sun.com/products/javamail/javadocs/com/sun/mail/imap/package-summary.html</li>
 * <li> http://java.sun.com/products/javamail/downloads/index.html</li>
 * </ul>
 * Also define a properties file with the following (adjust it to your configuration):
 * # As defined by the Javadoc
 * <ul>
 * <li> mail.user=name.lastname@gmail.com</li>
 * <li> mail.password=zzz</li>
 * <li> mail.store.host=pop.imap.gmail.com</li>
 * <li> mail.store.protocol=imap</li>
 * <li> mail.imap.port=993</li>
 * <li> mail.imap.socketFactory.port=993</li>
 * <li> mail.imap.socketFactory.class=javax.net.ssl.SSLSocketFactory</li>
 * </ul>
 * License: GPL
 * http://kodegeek.com
 * @author Jose Vicente Nunez Zuleta
 * @version 12/28/2008
 */
public final class ImapBulkEraser {
 
	/**
	 * Command line processing
	 * @param args [0] Location of the properties configuration file, If not provided then will use system defaults (defined with -D)
	 * @throws Exception
	 * @since 0.1
	 */
	public static void main(String [] args) throws Exception {
 
		Logger logger = Logger.getLogger(ImapBulkEraser.class.getName());
 
		Properties props = System.getProperties();
		if (args.length == 1) {
			props.load(new FileInputStream(args[0]));
		}
		Session session = Session.getInstance(props, null);
		session.setDebug(Boolean.parseBoolean(props.getProperty("debug")));
		Store store = session.getStore(props.getProperty("mail.store.protocol"));
		logger.info(String.format("Connecting to: %s@%s:%s", props.getProperty("mail.user"), props.getProperty("mail.store.host"), props.getProperty("mail.imap.port")));
		store.connect(props.getProperty("mail.store.host"), props.getProperty("mail.user"), props.getProperty("mail.password"));
		Folder folder = store.getDefaultFolder();
		logger.info("Got default folder. Erasing emails from ALL the folders");
		Folder [] folders = folder.list();
		long messgCnt = 0;
		for(Folder currFolder: folders) {
 
			try {
 
				if ((currFolder.getType() & Folder.HOLDS_MESSAGES) == 0) {
					continue; // Skip this folder type
				}
				logger.info(String.format("Opening folder: %s, num messages; %d", currFolder.getFullName(), currFolder.getMessageCount()));
				currFolder.open(Folder.READ_WRITE);
				Message [] messages = currFolder.getMessages();
 
				// Mark all the messages for delete
				for (Message message: messages) {
					try {
						message.setFlag(Flag.DELETED, true);
						MimeMessage currMessage = (MimeMessage) message;
						logger.info(String.format("Set delete flag for mail subject: '%s', folder: '%s'", currMessage.getSubject(), currMessage.getFolder().getFullName()));
						messages = currFolder.expunge(); // Confirm delete for all the messages on the current folder
						messgCnt += messages.length;
					}  catch (MessagingException messgExp) {
						logger.log(Level.WARNING, "There was a problem erasing the message, ignoring", messgExp);
					}
				}
 
				logger.info(String.format("Messages erased on folder %s: %d", currFolder.getFullName(), messages.length));
				currFolder.close(true);
 
			} catch (MessagingException messgExp) {
				logger.log(Level.WARNING, "There was a problem, ignoring", messgExp);
			}
 
		}
		logger.info(String.format("Total number of erased messsages: %d", messgCnt));
		store.close();
	}
 
}

Correr el código es trivial, asegurese de tener el JAR de Javamail en el $CLASSPATH y haga algo como esto:


java com.kodegeek.blog.mail.ImapBulkEraser -Dmail.user=name.lastname@gmail.com -Dmail.password=XXXX -Dmail.store.host=imap.gmail.com -Dmail.store.protocol=imap -Dmail.store.folder=INBOX -Dmail.imap.port=993 -Dmail.imap.socketFactory.port=993 -Dmail.imap.socketFactory.class=javax.net.ssl.SSLSocketFactory

El programa no es perfecto, ya que de vez en cuando me conseguí con la siguiente error:


NFO: Set delete flag for mail subject: 'Deploying webapps in embedded environment', folder: 'tomcat-user'
Dec 28, 2008 5:40:18 PM com.kodegeek.blog.mail.ImapBulkEraser main
WARNING: There was a problem erasing the message, ignoring
javax.mail.MessagingException: Failed to load IMAP envelope
at com.sun.mail.imap.IMAPMessage.loadEnvelope(IMAPMessage.java:1069)
at com.sun.mail.imap.IMAPMessage.getSubject(IMAPMessage.java:256)
at com.kodegeek.blog.mail.ImapBulkEraser.main(ImapBulkEraser.java:83)
Dec 28, 2008 5:40:18 PM com.kodegeek.blog.mail.ImapBulkEraser main

Sin embargo esos no son todos los correos :)

Como siempre el código fuente de este programa es GPL y se puede bajar desde CVS.

java , , , ,