Showing posts with label Pop. Show all posts
Showing posts with label Pop. Show all posts

Sunday, November 16, 2008

Get mail from gmail using pop of javamail

javamail官方论坛中一个用pop协议从gmail中获取邮件的例子,需要在gmail中开启pop功能。

import java.util.Date;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.internet.ContentType;
import javax.mail.internet.ParseException;

import com.sun.mail.pop3.POP3SSLStore;

public class GmailUtilities {

private Session session = null;
private Store store = null;
private String username, password;
private Folder folder;

public GmailUtilities() {

}

public void setUserPass(String username, String password) {
this.username = username;
this.password = password;
}

public void connect() throws Exception {

String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

Properties pop3Props = new Properties();

pop3Props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
pop3Props.setProperty("mail.pop3.socketFactory.fallback", "false");
pop3Props.setProperty("mail.pop3.port", "995");
pop3Props.setProperty("mail.pop3.socketFactory.port", "995");

URLName url = new URLName("pop3", "pop.gmail.com", 995, "", username, password);

session = Session.getInstance(pop3Props, null);
store = new POP3SSLStore(session, url);
store.connect();

}

public void openFolder(String folderName) throws Exception {

// Open the Folder
folder = store.getDefaultFolder();

folder = folder.getFolder(folderName);

if (folder == null) {
throw new Exception("Invalid folder");
}

// try to open read/write and if that fails try read-only
try {

folder.open(Folder.READ_WRITE);

} catch (MessagingException ex) {

folder.open(Folder.READ_ONLY);

}
}

public void closeFolder() throws Exception {
folder.close(false);
}

public int getMessageCount() throws Exception {
return folder.getMessageCount();
}

public int getNewMessageCount() throws Exception {
return folder.getNewMessageCount();
}

public void disconnect() throws Exception {
store.close();
}

public void printMessage(int messageNo) throws Exception {
System.out.println("Getting message number: " + messageNo);

Message m = null;

try {
m = folder.getMessage(messageNo);
dumpPart(m);
} catch (IndexOutOfBoundsException iex) {
System.out.println("Message number out of range");
}
}

public void printAllMessageEnvelopes() throws Exception {

// Attributes & Flags for all messages ..
Message[] msgs = folder.getMessages();

// Use a suitable FetchProfile
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);

for (int i = 0; i < msgs.length; i++) {
System.out.println("--------------------------");
System.out.println("MESSAGE #" + (i + 1) + ":");
dumpEnvelope(msgs[i]);
}

}

public void printAllMessages() throws Exception {

// Attributes & Flags for all messages ..
Message[] msgs = folder.getMessages();

// Use a suitable FetchProfile
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
folder.fetch(msgs, fp);

for (int i = 0; i < msgs.length; i++) {
System.out.println("--------------------------");
System.out.println("MESSAGE #" + (i + 1) + ":");
dumpPart(msgs[i]);
}

}

public static void dumpPart(Part p) throws Exception {
if (p instanceof Message)
dumpEnvelope((Message) p);

String ct = p.getContentType();
try {
pr("CONTENT-TYPE: " + (new ContentType(ct)).toString());
} catch (ParseException pex) {
pr("BAD CONTENT-TYPE: " + ct);
}

/*
* Using isMimeType to determine the content type avoids fetching the
* actual content data until we need it.
*/
if (p.isMimeType("text/plain")) {
pr("This is plain text");
pr("---------------------------");
System.out.println((String) p.getContent());
} else {

// just a separator
pr("---------------------------");

}
}

public static void dumpEnvelope(Message m) throws Exception {
pr(" ");
Address[] a;
// FROM
if ((a = m.getFrom()) != null) {
for (int j = 0; j < a.length; j++)
pr("FROM: " + a[j].toString());
}

// TO
if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
for (int j = 0; j < a.length; j++) {
pr("TO: " + a[j].toString());
}
}

// SUBJECT
pr("SUBJECT: " + m.getSubject());

// DATE
Date d = m.getSentDate();
pr("SendDate: " + (d != null ? d.toString() : "UNKNOWN"));

}

static String indentStr = " ";
static int level = 0;

/**
* Print a, possibly indented, string.
*/
public static void pr(String s) {

System.out.print(indentStr.substring(0, level * 2));
System.out.println(s);
}

}
Usage:

public class GmailPop {

public static void main(String[] args) {

try {

GmailUtilities gmail = new GmailUtilities();
gmail.setUserPass("test.yu@gmail.com", "****password****");
gmail.connect();
gmail.openFolder("INBOX");

int i = 0;
int totalMessages = gmail.getMessageCount();
int newMessages = gmail.getNewMessageCount();

System.out.println("Total messages = " + totalMessages);
System.out.println("New messages = " + newMessages);
System.out.println("-------------------------------");

do {
i++;
gmail.printMessage(i);
} while (i < 10);

// Uncomment the below line to print the body of the message.
// Remember it will eat-up your bandwidth if you have 100's of
// messages.
// gmail.printAllMessageEnvelopes();
// gmail.printAllMessages();

} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
}

Wednesday, May 16, 2007

POP3 SSL on Ruby

http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/net/pop.rb?revision=12083&pathrev=12083


# = net/pop.rb
#
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
#
# Copyright (c) 1999-2007 Minero Aoki.
#
# Written & maintained by Minero Aoki .
#
# Documented by William Webber and Minero Aoki.
#
# This program is free software. You can re-distribute and/or
# modify this program under the same terms as Ruby itself,
# Ruby Distribute License.
#
# NOTE: You can find Japanese version of this document at:
# http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=net%2Fpop.rb
#
# $Id$
#
# See Net::POP3 for documentation.
#

require 'net/protocol'
require 'digest/md5'
require 'timeout'

begin
require "openssl"
rescue LoadError
end

module Net

# Non-authentication POP3 protocol error
# (reply code "-ERR", except authentication).
class POPError < ProtocolError; end

# POP3 authentication error.
class POPAuthenticationError < ProtoAuthError; end

# Unexpected response from the server.
class POPBadResponse < POPError; end

#
# = Net::POP3
#
# == What is This Library?
#
# This library provides functionality for retrieving
# email via POP3, the Post Office Protocol version 3. For details
# of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt).
#
# == Examples
#
# === Retrieving Messages
#
# This example retrieves messages from the server and deletes them
# on the server.
#
# Messages are written to files named 'inbox/1', 'inbox/2', ....
# Replace 'pop.example.com' with your POP3 server address, and
# 'YourAccount' and 'YourPassword' with the appropriate account
# details.
#
# require 'net/pop'
#
# pop = Net::POP3.new('pop.example.com')
# pop.start('YourAccount', 'YourPassword') # (1)
# if pop.mails.empty?
# puts 'No mail.'
# else
# i = 0
# pop.each_mail do |m| # or "pop.mails.each ..." # (2)
# File.open("inbox/#{i}", 'w') do |f|
# f.write m.pop
# end
# m.delete
# i += 1
# end
# puts "#{pop.mails.size} mails popped."
# end
# pop.finish # (3)
#
# 1. Call Net::POP3#start and start POP session.
# 2. Access messages by using POP3#each_mail and/or POP3#mails.
# 3. Close POP session by calling POP3#finish or use the block form of #start.
#
# === Shortened Code
#
# The example above is very verbose. You can shorten the code by using
# some utility methods. First, the block form of Net::POP3.start can
# be used instead of POP3.new, POP3#start and POP3#finish.
#
# require 'net/pop'
#
# Net::POP3.start('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |pop|
# if pop.mails.empty?
# puts 'No mail.'
# else
# i = 0
# pop.each_mail do |m| # or "pop.mails.each ..."
# File.open("inbox/#{i}", 'w') do |f|
# f.write m.pop
# end
# m.delete
# i += 1
# end
# puts "#{pop.mails.size} mails popped."
# end
# end
#
# POP3#delete_all is an alternative for #each_mail and #delete.
#
# require 'net/pop'
#
# Net::POP3.start('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |pop|
# if pop.mails.empty?
# puts 'No mail.'
# else
# i = 1
# pop.delete_all do |m|
# File.open("inbox/#{i}", 'w') do |f|
# f.write m.pop
# end
# i += 1
# end
# end
# end
#
# And here is an even shorter example.
#
# require 'net/pop'
#
# i = 0
# Net::POP3.delete_all('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |m|
# File.open("inbox/#{i}", 'w') do |f|
# f.write m.pop
# end
# i += 1
# end
#
# === Memory Space Issues
#
# All the examples above get each message as one big string.
# This example avoids this.
#
# require 'net/pop'
#
# i = 1
# Net::POP3.delete_all('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |m|
# File.open("inbox/#{i}", 'w') do |f|
# m.pop do |chunk| # get a message little by little.
# f.write chunk
# end
# i += 1
# end
# end
#
# === Using APOP
#
# The net/pop library supports APOP authentication.
# To use APOP, use the Net::APOP class instead of the Net::POP3 class.
# You can use the utility method, Net::POP3.APOP(). For example:
#
# require 'net/pop'
#
# # Use APOP authentication if $isapop == true
# pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
# pop.start(YourAccount', 'YourPassword') do |pop|
# # Rest of the code is the same.
# end
#
# === Fetch Only Selected Mail Using 'UIDL' POP Command
#
# If your POP server provides UIDL functionality,
# you can grab only selected mails from the POP server.
# e.g.
#
# def need_pop?( id )
# # determine if we need pop this mail...
# end
#
# Net::POP3.start('pop.example.com', 110,
# 'Your account', 'Your password') do |pop|
# pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m|
# do_something(m.pop)
# end
# end
#
# The POPMail#unique_id() method returns the unique-id of the message as a
# String. Normally the unique-id is a hash of the message.
#
class POP3 < Protocol

Revision = %q$Revision$.split[1]

#
# Class Parameters
#

def POP3.default_port
default_pop3_port()
end

# The default port for POP3 connections, port 110
def POP3.default_pop3_port
110
end

# The default port for POP3S connections, port 995
def POP3.default_pop3s_port
995
end

def POP3.socket_type #:nodoc: obsolete
Net::InternetMessageIO
end

#
# Utilities
#

# Returns the APOP class if +isapop+ is true; otherwise, returns
# the POP class. For example:
#
# # Example 1
# pop = Net::POP3::APOP($is_apop).new(addr, port)
#
# # Example 2
# Net::POP3::APOP($is_apop).start(addr, port) do |pop|
# ....
# end
#
def POP3.APOP(isapop)
isapop ? APOP : POP3
end

# Starts a POP3 session and iterates over each POPMail object,
# yielding it to the +block+.
# This method is equivalent to:
#
# Net::POP3.start(address, port, account, password) do |pop|
# pop.each_mail do |m|
# yield m
# end
# end
#
# This method raises a POPAuthenticationError if authentication fails.
#
# === Example
#
# Net::POP3.foreach('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |m|
# file.write m.pop
# m.delete if $DELETE
# end
#
def POP3.foreach(address, port = nil,
account = nil, password = nil,
isapop = false, &block) # :yields: message
start(address, port, account, password, isapop) {|pop|
pop.each_mail(&block)
}
end

# Starts a POP3 session and deletes all messages on the server.
# If a block is given, each POPMail object is yielded to it before
# being deleted.
#
# This method raises a POPAuthenticationError if authentication fails.
#
# === Example
#
# Net::POP3.delete_all('pop.example.com', 110,
# 'YourAccount', 'YourPassword') do |m|
# file.write m.pop
# end
#
def POP3.delete_all(address, port = nil,
account = nil, password = nil,
isapop = false, &block)
start(address, port, account, password, isapop) {|pop|
pop.delete_all(&block)
}
end

# Opens a POP3 session, attempts authentication, and quits.
#
# This method raises POPAuthenticationError if authentication fails.
#
# === Example: normal POP3
#
# Net::POP3.auth_only('pop.example.com', 110,
# 'YourAccount', 'YourPassword')
#
# === Example: APOP
#
# Net::POP3.auth_only('pop.example.com', 110,
# 'YourAccount', 'YourPassword', true)
#
def POP3.auth_only(address, port = nil,
account = nil, password = nil,
isapop = false)
new(address, port, isapop).auth_only account, password
end

# Starts a pop3 session, attempts authentication, and quits.
# This method must not be called while POP3 session is opened.
# This method raises POPAuthenticationError if authentication fails.
def auth_only(account, password)
raise IOError, 'opening previously opened POP session' if started?
start(account, password) {
;
}
end

#
# SSL
#

@use_ssl = false
@verify = nil
@certs = nil

# Enable SSL for all new instances.
# +verify+ is the type of verification to do on the Server Cert; Defaults
# to OpenSSL::SSL::VERIFY_NONE.
# +certs+ is a file or directory holding CA certs to use to verify the
# server cert; Defaults to nil.
def POP3.enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE, certs = nil)
@use_ssl = true
@verify = verify
@certs = certs
end

# Disable SSL for all new instances.
def POP3.disable_ssl
@use_ssl = nil
@verify = nil
@certs = nil
end

def POP3.use_ssl?
@use_ssl
end

def POP3.verify
@verify
end

def POP3.certs
@certs
end

#
# Session management
#

# Creates a new POP3 object and open the connection. Equivalent to
#
# Net::POP3.new(address, port, isapop).start(account, password)
#
# If +block+ is provided, yields the newly-opened POP3 object to it,
# and automatically closes it at the end of the session.
#
# === Example
#
# Net::POP3.start(addr, port, account, password) do |pop|
# pop.each_mail do |m|
# file.write m.pop
# m.delete
# end
# end
#
def POP3.start(address, port = nil,
account = nil, password = nil,
isapop = false, &block) # :yield: pop
new(address, port, isapop).start(account, password, &block)
end

# Creates a new POP3 object.
#
# +address+ is the hostname or ip address of your POP3 server.
#
# The optional +port+ is the port to connect to.
#
# The optional +isapop+ specifies whether this connection is going
# to use APOP authentication; it defaults to +false+.
#
# This method does *not* open the TCP connection.
def initialize(addr, port = nil, isapop = false)
@address = addr
@use_ssl = POP3.use_ssl?
@port = port || (POP3.use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port)
@apop = isapop
@certs = POP3.certs
@verify = POP3.verify

@command = nil
@socket = nil
@started = false
@open_timeout = 30
@read_timeout = 60
@debug_output = nil

@mails = nil
@n_mails = nil
@n_bytes = nil
end

# Does this instance use APOP authentication?
def apop?
@apop
end

# does this instance use SSL?
def use_ssl?
@use_ssl
end

# Enables SSL for this instance. Must be called before the connection is
# established to have any effect.
# +verify+ is the type of verification to do on the Server Cert; Defaults
# to OpenSSL::SSL::VERIFY_NONE.
# +certs+ is a file or directory holding CA certs to use to verify the
# server cert; Defaults to nil.
# +port+ is port to establish the SSL connection on; Defaults to 995.
def enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE, certs = nil,
port = POP3.default_pop3s_port)
@use_ssl = true
@verify = verify
@certs = certs
@port = port
end

def disable_ssl
@use_ssl = false
@verify = nil
@certs = nil
end

# Provide human-readable stringification of class state.
def inspect
"#<#{self.class} #{@address}:#{@port} open=#{@started}>"
end

# *WARNING*: This method causes a serious security hole.
# Use this method only for debugging.
#
# Set an output stream for debugging.
#
# === Example
#
# pop = Net::POP.new(addr, port)
# pop.set_debug_output $stderr
# pop.start(account, passwd) do |pop|
# ....
# end
#
def set_debug_output(arg)
@debug_output = arg
end

# The address to connect to.
attr_reader :address

# The port number to connect to.
attr_reader :port

# Seconds to wait until a connection is opened.
# If the POP3 object cannot open a connection within this time,
# it raises a TimeoutError exception.
attr_accessor :open_timeout

# Seconds to wait until reading one block (by one read(1) call).
# If the POP3 object cannot complete a read() within this time,
# it raises a TimeoutError exception.
attr_reader :read_timeout

# Set the read timeout.
def read_timeout=(sec)
@command.socket.read_timeout = sec if @command
@read_timeout = sec
end

# +true+ if the POP3 session has started.
def started?
@started
end

alias active? started? #:nodoc: obsolete

# Starts a POP3 session.
#
# When called with block, gives a POP3 object to the block and
# closes the session after block call finishes.
#
# This method raises a POPAuthenticationError if authentication fails.
def start(account, password) # :yield: pop
raise IOError, 'POP session already started' if @started
if block_given?
begin
do_start account, password
return yield(self)
ensure
do_finish
end
else
do_start account, password
return self
end
end

def do_start(account, password)
s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
if use_ssl?
raise 'openssl library not installed' unless defined?(OpenSSL)
context = OpenSSL::SSL::SSLContext.new
context.verify_mode = @verify
if @certs
if File.file?(@certs)
context.ca_file = @certs
elsif File.directory?(@certs)
context.ca_path = @certs
else
raise ArgumentError, "certs path is not file/directory: #{@certs}"
end
end
s = OpenSSL::SSL::SSLSocket.new(s, context)
s.sync_close = true
s.connect
end
@socket = InternetMessageIO.new(s)
logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})"
@socket.read_timeout = @read_timeout
@socket.debug_output = @debug_output
on_connect
@command = POP3Command.new(@socket)
if apop?
@command.apop account, password
else
@command.auth account, password
end
@started = true
ensure
# Authentication failed, clean up connection.
unless @started
s.close if s and not s.closed?
@socket = nil
@command = nil
end
end
private :do_start

def on_connect
end
private :on_connect

# Finishes a POP3 session and closes TCP connection.
def finish
raise IOError, 'POP session not yet started' unless started?
do_finish
end

def do_finish
@mails = nil
@command.quit if @command
ensure
@started = false
@command = nil
@socket.close if @socket and not @socket.closed?
@socket = nil
end
private :do_finish

def command
raise IOError, 'POP session not opened yet' \
if not @socket or @socket.closed?
@command
end
private :command

#
# POP protocol wrapper
#

# Returns the number of messages on the POP server.
def n_mails
return @n_mails if @n_mails
@n_mails, @n_bytes = command().stat
@n_mails
end

# Returns the total size in bytes of all the messages on the POP server.
def n_bytes
return @n_bytes if @n_bytes
@n_mails, @n_bytes = command().stat
@n_bytes
end

# Returns an array of Net::POPMail objects, representing all the
# messages on the server. This array is renewed when the session
# restarts; otherwise, it is fetched from the server the first time
# this method is called (directly or indirectly) and cached.
#
# This method raises a POPError if an error occurs.
def mails
return @mails.dup if @mails
if n_mails() == 0
# some popd raises error for LIST on the empty mailbox.
@mails = []
return []
end

@mails = command().list.map {|num, size|
POPMail.new(num, size, self, command())
}
@mails.dup
end

# Yields each message to the passed-in block in turn.
# Equivalent to:
#
# pop3.mails.each do |popmail|
# ....
# end
#
# This method raises a POPError if an error occurs.
def each_mail(&block) # :yield: message
mails().each(&block)
end

alias each each_mail

# Deletes all messages on the server.
#
# If called with a block, yields each message in turn before deleting it.
#
# === Example
#
# n = 1
# pop.delete_all do |m|
# File.open("inbox/#{n}") do |f|
# f.write m.pop
# end
# n += 1
# end
#
# This method raises a POPError if an error occurs.
#
def delete_all # :yield: message
mails().each do |m|
yield m if block_given?
m.delete unless m.deleted?
end
end

# Resets the session. This clears all "deleted" marks from messages.
#
# This method raises a POPError if an error occurs.
def reset
command().rset
mails().each do |m|
m.instance_eval {
@deleted = false
}
end
end

def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
command().uidl.each do |num, uid|
@mails.find {|m| m.number == num }.uid = uid
end
end

def logging(msg)
@debug_output << msg + "\n" if @debug_output
end

end # class POP3

# class aliases
POP = POP3
POPSession = POP3
POP3Session = POP3

#
# This class is equivalent to POP3, except that it uses APOP authentication.
#
class APOP < POP3
# Always returns true.
def apop?
true
end
end

# class aliases
APOPSession = APOP

#
# This class represents a message which exists on the POP server.
# Instances of this class are created by the POP3 class; they should
# not be directly created by the user.
#
class POPMail

def initialize(num, len, pop, cmd) #:nodoc:
@number = num
@length = len
@pop = pop
@command = cmd
@deleted = false
@uid = nil
end

# The sequence number of the message on the server.
attr_reader :number

# The length of the message in octets.
attr_reader :length
alias size length

# Provide human-readable stringification of class state.
def inspect
"#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
end

#
# This method fetches the message. If called with a block, the
# message is yielded to the block one chunk at a time. If called
# without a block, the message is returned as a String. The optional
# +dest+ argument will be prepended to the returned String; this
# argument is essentially obsolete.
#
# === Example without block
#
# POP3.start('pop.example.com', 110,
# 'YourAccount, 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
# f.write popmail.pop
# end
# popmail.delete
# n += 1
# end
# end
#
# === Example with block
#
# POP3.start('pop.example.com', 110,
# 'YourAccount, 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
# popmail.pop do |chunk| ####
# f.write chunk
# end
# end
# n += 1
# end
# end
#
# This method raises a POPError if an error occurs.
#
def pop( dest = '', &block ) # :yield: message_chunk
if block_given?
@command.retr(@number, &block)
nil
else
@command.retr(@number) do |chunk|
dest << chunk
end
dest
end
end

alias all pop #:nodoc: obsolete
alias mail pop #:nodoc: obsolete

# Fetches the message header and +lines+ lines of body.
#
# The optional +dest+ argument is obsolete.
#
# This method raises a POPError if an error occurs.
def top(lines, dest = '')
@command.top(@number, lines) do |chunk|
dest << chunk
end
dest
end

# Fetches the message header.
#
# The optional +dest+ argument is obsolete.
#
# This method raises a POPError if an error occurs.
def header(dest = '')
top(0, dest)
end

# Marks a message for deletion on the server. Deletion does not
# actually occur until the end of the session; deletion may be
# cancelled for _all_ marked messages by calling POP3#reset().
#
# This method raises a POPError if an error occurs.
#
# === Example
#
# POP3.start('pop.example.com', 110,
# 'YourAccount, 'YourPassword') do |pop|
# n = 1
# pop.mails.each do |popmail|
# File.open("inbox/#{n}", 'w') do |f|
# f.write popmail.pop
# end
# popmail.delete ####
# n += 1
# end
# end
#
def delete
@command.dele @number
@deleted = true
end

alias delete! delete #:nodoc: obsolete

# True if the mail has been deleted.
def deleted?
@deleted
end

# Returns the unique-id of the message.
# Normally the unique-id is a hash string of the message.
#
# This method raises a POPError if an error occurs.
def unique_id
return @uid if @uid
@pop.set_all_uids
@uid
end

alias uidl unique_id

def uid=(uid) #:nodoc: internal use only
@uid = uid
end

end # class POPMail


class POP3Command #:nodoc: internal use only

def initialize(sock)
@socket = sock
@error_occured = false
res = check_response(critical { recv_response() })
@apop_stamp = res.slice(/<.+>/)
end

def inspect
"#<#{self.class} socket=#{@socket}>"
end

def auth(account, password)
check_response_auth(critical {
check_response_auth(get_response('USER %s', account))
get_response('PASS %s', password)
})
end

def apop(account, password)
raise POPAuthenticationError, 'not APOP server; cannot login' \
unless @apop_stamp
check_response_auth(critical {
get_response('APOP %s %s',
account,
Digest::MD5.hexdigest(@apop_stamp + password))
})
end

def list
critical {
getok 'LIST'
list = []
@socket.each_list_item do |line|
m = /\A(\d+)[ \t]+(\d+)/.match(line) or
raise POPBadResponse, "bad response: #{line}"
list.push [m[1].to_i, m[2].to_i]
end
return list
}
end

def stat
res = check_response(critical { get_response('STAT') })
m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
raise POPBadResponse, "wrong response format: #{res}"
[m[1].to_i, m[2].to_i]
end

def rset
check_response(critical { get_response('RSET') })
end

def top(num, lines = 0, &block)
critical {
getok('TOP %d %d', num, lines)
@socket.each_message_chunk(&block)
}
end

def retr(num, &block)
critical {
getok('RETR %d', num)
@socket.each_message_chunk(&block)
}
end

def dele(num)
check_response(critical { get_response('DELE %d', num) })
end

def uidl(num = nil)
if num
res = check_response(critical { get_response('UIDL %d', num) })
return res.split(/ /)[1]
else
critical {
getok('UIDL')
table = {}
@socket.each_list_item do |line|
num, uid = line.split
table[num.to_i] = uid
end
return table
}
end
end

def quit
check_response(critical { get_response('QUIT') })
end

private

def getok(fmt, *fargs)
@socket.writeline sprintf(fmt, *fargs)
check_response(recv_response())
end

def get_response(fmt, *fargs)
@socket.writeline sprintf(fmt, *fargs)
recv_response()
end

def recv_response
@socket.readline
end

def check_response(res)
raise POPError, res unless /\A\+OK/i =~ res
res
end

def check_response_auth(res)
raise POPAuthenticationError, res unless /\A\+OK/i =~ res
res
end

def critical
return '+OK dummy ok response' if @error_occured
begin
return yield()
rescue Exception
@error_occured = true
raise
end
end

end # class POP3Command

end # module Net

Tuesday, May 15, 2007

用Ruby程序接收邮件


require 'net/pop'

pop = Net::POP3.new('pop3.126.com')
pop.start('test', 'password') # (1)
if pop.mails.empty?
puts 'No mail.'
else
i = 0
# pop.each_mail do |m| # or "pop.mails.each ..." # (2)
# File.open("#{i}", 'w') do |f|
# f.write m.pop
# end
# m.delete
# i += 1
# end
pop.mails.each do |m|
File.open("mail", 'a') do |f|
f.write m.pop
end
end
puts "#{pop.mails.size} mails popped."
end
pop.finish

Linux fetchmail 使用实例

fetchmail 工具需要一个名为 .fetchmailrc 的配置文件才能正常工作。 这个文件中包含了服务器信息, 以及登录使用的凭据。 由于这个文件包含敏感内容, 建议将其设置为只有属主所有, 使用下面的命令:

$> chmod 600 .fetchmailrc

下面的 .fetchmailrc 提供了一个将某一用户的信箱通过 POP 下载到本地的例子。

poll pop3.163.com proto pop3:
username "test", with password "password", is "yu" here;

更多的资料可参考官方手册,另此软件作者Eric Steven Raymond是个开源项目牛人,出过一篇有影响力的文章《大教堂与集市》。

telnet 收发邮件

Initiative - est's Blog
收邮件 开始 运行 cmd 回车,OK, Let's go!

telnet 202.115.22.199 110
USER electronixtar
PASS 我的密码啦

可以看到统计了,接下来

STAT 可以查看统计

LIST 看所有信件

RETR 1 收第一封啦。可惜邮件是 Base64 编码的。不过习惯就对了,以后我们应聘的时候可以吹会第三门外语Base64呢,哇咔咔

TOP 1 10 收最前面的1封的前10行内容

DELE 1 删除第一封

QUIT 断开连接,闪人鸟~

发邮件 开始 运行 cmd 回车,OK, Let's go on!

telnet 202.115.22.199 25

EHLO Multiversal 偶的电脑名字,双方协商呢,看来蛮友好的嘛。服务器返回:
250-mail.dormforce.net
250-AUTH CRAM-MD5 PLAIN LOGIN DIGEST-MD5 NTLM
250-SIZE 10485760
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-PIPELINING
250-ETRN
250-DSN
250 HELP
可能列出了一些命令吧,继续,

AUTH LOGIN 用一般登陆模式。从上面的服务器返回可以看到居然支持 AUTH CRAM-MD5 PLAIN LOGIN DIGEST-MD5 NTLM,爽啊,支持这么多 ^_^
服务器返回,
AUTH LOGIN
334 VXNlcm5hbWU6
服务器问你用户名呢,不过是 Username: Base64编码后的字符
ZWxlY3Ryb25peHRhcg==
偶的用户名 Base64编码后,看来以后如果用 telnet 收邮件,得背用户名的Base64写法啊
334 UGFzc3dvcmQ6
回答是你的密码 Base64 加密后的字符串,这里偶就不敢写了。
235 2.0.0 Authentication successful

然后开始写邮件啦。注意准备好Base64翻译器:

MAIL FROM: 发件人吧
250 2.1.0 Sender ok

RCPT TO: 收件人吧
250 2.1.5 Recipient ok (remote)

DATA
然后就是正文了,符合 MIME 规范就可以了。贴一个例子:

From: electronixtar@dormforce.net
To: electronicstar@dormforce.net
Subject: Hello From telnet
Mime-Version: 1.0
Content-Type: text/plain

Hello!

.

最后结束,打一个回车,一个 句号 . 英文半角的,然后再一个回车,写信完毕!

QUIT 断开连接,闪人。

btw,今天抓包发现,DreamMail发邮件速度快的原因是采用了异步读取,真是NB啊~