Reworked SSL for POP patch
From:
Daniel Hobe <daniel@...>
Date:
2004-04-04 23:57:49 UTC
List:
ruby-core #2745
I've reworked the patch to implement a few new methods and not add any extra arguments to existing methods. 3 new class vars: @@usessl @@certs @@verify New methods: Toggle SSL connections for new instances. Sets the above class variables. POP3.enable_ssl( verify, certs ) POP3.disable_ssl 2 equivalent instance methods to enable / disable SSL on a per instance basis: pop.enable_ssl(verify, certs, port) pop.disable_ssl Example usage: Quick 'n dirty all connections are SSL: Net::POP3.enable_ssl Net::POP3.start(server,port,account,password) do |pop| ... end Use SSL on a case by case basis: pop = Net::POP3.new(server) pop.enable_ssl if use_ssl_on_this_server pop.start(user,password) do |pop| ... end Do people like this better? This way of handling SSL could be applied to SMTP and IMAP as well to get a consistant SSL API across all the mail services. FYI, I'll be on vacation for the next week and away from a computer until I get back. -- Daniel Hobe <daniel@nightrunner.com> http://www.nightrunner.com
Attachments (1)
pop.patch_v4
(6.3 KB, text/x-diff)
--- /home/hobe/downloads/ruby/ruby/lib/net/pop.rb 2004-04-04 16:37:09.000000000 -0700
+++ pop.rb 2004-04-04 16:56:33.000000000 -0700
@@ -36,12 +36,13 @@
# require 'net/pop'
#
# pop = Net::POP3.new('pop.example.com')
-# pop.start('YourAccount', 'YourPassword') # (1)
+# pop = pop.enable_ssl(verify, certs) if $use_ssl # (1)
+# pop.start('YourAccount', 'YourPassword') # (2)
# if pop.mails.empty?
# puts 'no mail.'
# else
# i = 0
-# pop.each_mail do |m| # or "pop.mails.each ..." # (2)
+# pop.each_mail do |m| # or "pop.mails.each ..." # (3)
# File.open("inbox/#{i}", 'w') {|f|
# f.write m.pop
# }
@@ -50,11 +51,12 @@
# end
# puts "#{pop.mails.size} mails popped."
# end
-# pop.finish # (3)
+# pop.finish # (4)
#
-# 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.
+# 1. optionally enable SSL for this POP connection
+# 2. call Net::POP3#start and start POP session
+# 3. access messages by using POP3#each_mail and/or POP3#mails
+# 4. close POP session by calling POP3#finish or use the block form of #start.
#
# === Shortened Code
#
@@ -141,10 +143,31 @@
#
# # Use APOP authentication if $isapop == true
# pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
-# pop.start(YourAccount', 'YourPassword') {|pop|
+# pop.start('YourAccount', 'YourPassword') {|pop|
# # Rest code is same.
# }
+#
+# === Using SSL
+# The net/pop library supports POP3 over SSL.
+# To use SSL:
+#
+# Example 1:
+# require 'net/pop'
+#
+# pop = Net::POP3.APOP($is_apop)
+# pop = pop.enable_ssl if $use_ssl
+# pop.start(server, port, account, password) do |pop|
+# ...
+# end
#
+# Example 2:
+# require 'net/pop'
+# pop = Net::POP3.new('pop.example.com').enable_ssl
+# pop.start(username, password) do |pop|
+# ...
+# end
+#
+#
# === Fetch Only Selected Mail Using `UIDL' POP Command
#
# If your POP server provides UIDL functionality,
@@ -170,6 +193,11 @@
require 'digest/md5'
require 'timeout'
+begin
+ require "openssl"
+rescue LoadError
+end
+
module Net
# Non-authentication POP3 protocol error
@@ -196,9 +224,19 @@
# Class Parameters
#
+ @@usessl = nil
+ @@verify = nil
+ @@certs = nil
+ PORT = 110
+ SSL_PORT = 995
# The default port for POP3 connections, port 110
def POP3.default_port
- 110
+ PORT
+ end
+
+ # The default port for POP3S connections, port 995
+ def POP3.default_ssl_port
+ SSL_PORT
end
def POP3.socket_type #:nodoc: obsolete
@@ -324,17 +362,43 @@
new(address, port, isapop).start(account, password, &block)
end
+ # Enable SSL for all new instances.
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
+ # to OpenSSL::SSL::VERIFY_PEER.
+ # +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_PEER, certs = nil )
+ @@usessl = true
+ @@verify = verify
+ @@certs = certs
+ end
+
+ # Disable SSL for all new instances.
+ def POP3.disable_ssl
+ @@usessl = nil
+ @@verify = nil
+ @@certs = nil
+ 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; it defaults to 110.
+ # +addr+ 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
- @port = port || self.class.default_port
+ @usessl = @@usessl
+ if @usessl
+ @port = port || POP3::SSL_PORT
+ else
+ @port = port || POP3::PORT
+ end
@apop = isapop
-
+
+ @certs = @@certs
+ @verify = @@verify
+
@command = nil
@socket = nil
@started = false
@@ -352,6 +416,32 @@
@apop
end
+ # does this instance use SSL?
+ def usessl?
+ @usessl
+ 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_PEER.
+ # +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 conection on; Defaults to 995.
+ def enable_ssl(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil,
+ port = POP3::SSL_PORT)
+ @usessl = true
+ @verify = verify
+ @certs = certs
+ @port = port
+ end
+
+ def disable_ssl
+ @usessl = nil
+ @verify = nil
+ @certs = nil
+ end
+
# Provide human-readable stringification of class state.
def inspect
"#<#{self.class} #{@address}:#{@port} open=#{@started}>"
@@ -424,9 +514,22 @@
end
def do_start(account, password)
- @socket = InternetMessageIO.new(timeout(@open_timeout) {
- TCPSocket.open(@address, @port)
- })
+ s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
+ if @usessl
+ unless defined?(OpenSSL)
+ raise "SSL extension not installed"
+ end
+ sslctx = OpenSSL::SSL::SSLContext.new
+ sslctx.verify_mode = @verify
+ sslctx.ca_file = @certs if @certs && FileTest::file?(@certs)
+ sslctx.ca_path = @certs if @certs && FileTest::directory?(@certs)
+ s = OpenSSL::SSL::SSLSocket.new(s, sslctx)
+ 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