From: Daniel Hobe Date: 2004-04-12T03:36:56+09:00 Subject: Re: Reworked SSL for POP patch Anyone have a chance to look at this yet? Daniel Hobe wrote: > 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. > > > ------------------------------------------------------------------------ > > --- /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