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:
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
Категории