Reading Mail with POP3

Credit: John Wells

Problem

You want to connect to an POP server in order to read and download the messages stored there.

Solution

The net/pop.rb package, written by Minero Aoki, is part of Rubys standard library, and provides a foundation on which to build a POP (Post Office Protocol)-oriented email application. As with the previous recipe on IMAP, well walk through some common ways of accessing a mail server with the POP API.

For this recipe, we assume you have access to a POP3 server running at mail.myhost. com on the standard POP3 port 110. Just as in the previous IMAP example, your username is "username", and password is (yep) "password".

To make the initial connection to the server, its as simple as:

	require 
et/pop

	conn = Net::POP3.new(mail.myhost.com)
	conn.start(username, password)

If you receive no errors, youve got an open session to your POP3 server, and can use the conn object to communicate with the server.

The following code acts like a typical POP3 client: having connected to the server, it downloads all the new messages, and then deletes them from the server. The deletion is commented out so you don lose mail accidentally while testing this code:

	require 
et/pop

	conn = Net::POP3.new(mail.myhost.com)
	conn.start(username, password)

	conn.mails.each do |msg|
	 File.open(msg.uidl, w) { |f| f.write msg.pop }
	 # msg.delete
	end

	conn.finish

Discussion

POP3 is a much simpler protocol than IMAP, and arguably a less powerful one. It doesn support the concept of folders, so theres no need to start off by selecting a particular folder (like we did in the IMAP recipe). Once you start a session, you have immediate access to all messages currently retained on the server.

IMAP stores your folders and your messages on the server itself. This way you can access the same messages and the same folders from different clients on different machines. For example, you might go to work and access an IMAP folder with Mozilla Thunderbird, then go home and access the same folder with a web-based mail client.

With POP3, there are no server-side folders. You e supposed to archive your messages on the client side. If you use a POP3 client to download messages at work, when you get home you won be able to access those messages. They e on your work computer, not on the POP3 server.

IMAP assigns a unique, unchanging ID to each message in the mailbox. By contrast, when you start a POP3 session, POP3 gives each message a "sequence number" reflecting its position in the mailbox at that time. The next time you connect to the POP3 server, the same message may have a different sequence number, as new, incoming messages can affect the sequencing. This is why POP3 clients typically download messages immediately and delete them from the server.

If we want to go outside this basic pattern, and leave the messages on the server, how can we keep track of messages from one connection to another? POP3 does provide a unique string ID for each message: a Unique Identification Listing, or UIDL. You can use a UIDL (which persists across POP3 sessions) to get a sequence number (which doesn ) and retrieve a message across separate connections.

This code finds the IDs of email messages from a particular source:

	conn = Net::POP3.new(mail.myhost.com)
	conn.start(username, password)
	ids = conn.mails.collect {|msg| msg.uidl if msg.pop.match(jabba)}
	conn.finish
	# => ["UID2-1141260595", "UID3-1141260595"]

Now we have unique identifiers for each of our matching messages. Given these, we can start a new POP3 session and use these UIDLs to retrieve each message individually:

	conn2 = Net::POP3.new(mail.myhost.com)
	conn.start(username, password)

	conn.each_mail {|msg| puts msg.pop if msg.uidl==UID3-1141260595}

	conn.finish
	# Return-Path: 
	# X-Original-To: username@my.mailhost.com
	# Delivered-To: username@localhost
	# …

Here we call the method Net::POP3#each_mail to iterate over all the messages in the mailbox. Each message is passed into the code block as a Net::POPMail message. We look at each messages UIDL and, when we find the message we want, we call Net::POPMail#pop to print it out.

Forwarding mail to a cell phone

Lets revisit our example from the IMAP recipe. You e waiting for a very important email, and you want to have it forwarded to your cell phone as soon as it comes in. You e able to send mail through a SMTP server hosted on port 25 of the same machine as your POP3 server. The email address of your cell phone is 5555555555@mycellphoneprovider.com.

This program checks your POP3 server for new email every five minutes. If a new message from anyone at huttfoundation.org is found, it forwards the message to your cell phone via SMS.

	#!/usr/bin/env ruby
	# forward_important_messages.rb

	require 
et/pop
	require 
et/smtp

	$address = huttfoundation.org
	$from = myhomeemail@my.mailhost.com
	$to = 5555555555@mycellphoneprovider.com
	smtp_server = my.mailhost.com
	pop_server = my.mailhost.com
	username = username
	password = password

	$found = Hash.new

	def send_msg (text)
	 count = 1
	 while(text.size > 0) do
	 # SMS messages limited to 160 characters
	 msg = text.slice!(0, 159)
	 full_msg = "From: #{$from}
"
	 full_msg += "To: #{$to}
"
	 full_msg += "Subject: Found message from #{$address} (#{count})!
"
	 full_msg += "Date: #{Time.now}
"
	 full_msg += msg + "
"
	 Net::SMTP.start(smtp_server, 25) do |smtp|
	 smtp.send_message full_msg, $from, $to
	 end
	 count += 1
	 end
	end

	loop do
	 conn = Net:: 
POP3.new(pop_server)
	 conn.start(username, password)

	 uidls = conn.mails.collect do |msg|
	 msg.uidl if msg.pop.match(/#{$address}/)
	 end

	 uidls.each do |one_id|
	 if ! $found.has_key? one_id
	 $found[one_id] = true
	 conn.each_mail do |msg|
	 send_msg(msg.uidl) if msg.uidl==one_id
	 end
	 end
	 end
	 conn.finish
	 # Sleep for 5 minutes.
	 sleep (60*60*5)
	end

See Also

  • Recipe 14.6, "Reading Mail with IMAP"
  • RFC1939 describes the POP3 protocol


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net