1# = net/pop.rb 2# 3# Copyright (c) 1999-2007 Yukihiro Matsumoto. 4# 5# Copyright (c) 1999-2007 Minero Aoki. 6# 7# Written & maintained by Minero Aoki <aamine@loveruby.net>. 8# 9# Documented by William Webber and Minero Aoki. 10# 11# This program is free software. You can re-distribute and/or 12# modify this program under the same terms as Ruby itself, 13# Ruby Distribute License. 14# 15# NOTE: You can find Japanese version of this document at: 16# http://www.ruby-lang.org/ja/man/html/net_pop.html 17# 18# $Id: pop.rb 44391 2013-12-24 15:46:01Z nagachika $ 19# 20# See Net::POP3 for documentation. 21# 22 23require 'net/protocol' 24require 'digest/md5' 25require 'timeout' 26 27begin 28 require "openssl" 29rescue LoadError 30end 31 32module Net 33 34 # Non-authentication POP3 protocol error 35 # (reply code "-ERR", except authentication). 36 class POPError < ProtocolError; end 37 38 # POP3 authentication error. 39 class POPAuthenticationError < ProtoAuthError; end 40 41 # Unexpected response from the server. 42 class POPBadResponse < POPError; end 43 44 # 45 # == What is This Library? 46 # 47 # This library provides functionality for retrieving 48 # email via POP3, the Post Office Protocol version 3. For details 49 # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt). 50 # 51 # == Examples 52 # 53 # === Retrieving Messages 54 # 55 # This example retrieves messages from the server and deletes them 56 # on the server. 57 # 58 # Messages are written to files named 'inbox/1', 'inbox/2', .... 59 # Replace 'pop.example.com' with your POP3 server address, and 60 # 'YourAccount' and 'YourPassword' with the appropriate account 61 # details. 62 # 63 # require 'net/pop' 64 # 65 # pop = Net::POP3.new('pop.example.com') 66 # pop.start('YourAccount', 'YourPassword') # (1) 67 # if pop.mails.empty? 68 # puts 'No mail.' 69 # else 70 # i = 0 71 # pop.each_mail do |m| # or "pop.mails.each ..." # (2) 72 # File.open("inbox/#{i}", 'w') do |f| 73 # f.write m.pop 74 # end 75 # m.delete 76 # i += 1 77 # end 78 # puts "#{pop.mails.size} mails popped." 79 # end 80 # pop.finish # (3) 81 # 82 # 1. Call Net::POP3#start and start POP session. 83 # 2. Access messages by using POP3#each_mail and/or POP3#mails. 84 # 3. Close POP session by calling POP3#finish or use the block form of #start. 85 # 86 # === Shortened Code 87 # 88 # The example above is very verbose. You can shorten the code by using 89 # some utility methods. First, the block form of Net::POP3.start can 90 # be used instead of POP3.new, POP3#start and POP3#finish. 91 # 92 # require 'net/pop' 93 # 94 # Net::POP3.start('pop.example.com', 110, 95 # 'YourAccount', 'YourPassword') do |pop| 96 # if pop.mails.empty? 97 # puts 'No mail.' 98 # else 99 # i = 0 100 # pop.each_mail do |m| # or "pop.mails.each ..." 101 # File.open("inbox/#{i}", 'w') do |f| 102 # f.write m.pop 103 # end 104 # m.delete 105 # i += 1 106 # end 107 # puts "#{pop.mails.size} mails popped." 108 # end 109 # end 110 # 111 # POP3#delete_all is an alternative for #each_mail and #delete. 112 # 113 # require 'net/pop' 114 # 115 # Net::POP3.start('pop.example.com', 110, 116 # 'YourAccount', 'YourPassword') do |pop| 117 # if pop.mails.empty? 118 # puts 'No mail.' 119 # else 120 # i = 1 121 # pop.delete_all do |m| 122 # File.open("inbox/#{i}", 'w') do |f| 123 # f.write m.pop 124 # end 125 # i += 1 126 # end 127 # end 128 # end 129 # 130 # And here is an even shorter example. 131 # 132 # require 'net/pop' 133 # 134 # i = 0 135 # Net::POP3.delete_all('pop.example.com', 110, 136 # 'YourAccount', 'YourPassword') do |m| 137 # File.open("inbox/#{i}", 'w') do |f| 138 # f.write m.pop 139 # end 140 # i += 1 141 # end 142 # 143 # === Memory Space Issues 144 # 145 # All the examples above get each message as one big string. 146 # This example avoids this. 147 # 148 # require 'net/pop' 149 # 150 # i = 1 151 # Net::POP3.delete_all('pop.example.com', 110, 152 # 'YourAccount', 'YourPassword') do |m| 153 # File.open("inbox/#{i}", 'w') do |f| 154 # m.pop do |chunk| # get a message little by little. 155 # f.write chunk 156 # end 157 # i += 1 158 # end 159 # end 160 # 161 # === Using APOP 162 # 163 # The net/pop library supports APOP authentication. 164 # To use APOP, use the Net::APOP class instead of the Net::POP3 class. 165 # You can use the utility method, Net::POP3.APOP(). For example: 166 # 167 # require 'net/pop' 168 # 169 # # Use APOP authentication if $isapop == true 170 # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110) 171 # pop.start(YourAccount', 'YourPassword') do |pop| 172 # # Rest of the code is the same. 173 # end 174 # 175 # === Fetch Only Selected Mail Using 'UIDL' POP Command 176 # 177 # If your POP server provides UIDL functionality, 178 # you can grab only selected mails from the POP server. 179 # e.g. 180 # 181 # def need_pop?( id ) 182 # # determine if we need pop this mail... 183 # end 184 # 185 # Net::POP3.start('pop.example.com', 110, 186 # 'Your account', 'Your password') do |pop| 187 # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m| 188 # do_something(m.pop) 189 # end 190 # end 191 # 192 # The POPMail#unique_id() method returns the unique-id of the message as a 193 # String. Normally the unique-id is a hash of the message. 194 # 195 class POP3 < Protocol 196 197 # svn revision of this library 198 Revision = %q$Revision: 44391 $.split[1] 199 200 # 201 # Class Parameters 202 # 203 204 # returns the port for POP3 205 def POP3.default_port 206 default_pop3_port() 207 end 208 209 # The default port for POP3 connections, port 110 210 def POP3.default_pop3_port 211 110 212 end 213 214 # The default port for POP3S connections, port 995 215 def POP3.default_pop3s_port 216 995 217 end 218 219 def POP3.socket_type #:nodoc: obsolete 220 Net::InternetMessageIO 221 end 222 223 # 224 # Utilities 225 # 226 227 # Returns the APOP class if +isapop+ is true; otherwise, returns 228 # the POP class. For example: 229 # 230 # # Example 1 231 # pop = Net::POP3::APOP($is_apop).new(addr, port) 232 # 233 # # Example 2 234 # Net::POP3::APOP($is_apop).start(addr, port) do |pop| 235 # .... 236 # end 237 # 238 def POP3.APOP(isapop) 239 isapop ? APOP : POP3 240 end 241 242 # Starts a POP3 session and iterates over each POPMail object, 243 # yielding it to the +block+. 244 # This method is equivalent to: 245 # 246 # Net::POP3.start(address, port, account, password) do |pop| 247 # pop.each_mail do |m| 248 # yield m 249 # end 250 # end 251 # 252 # This method raises a POPAuthenticationError if authentication fails. 253 # 254 # === Example 255 # 256 # Net::POP3.foreach('pop.example.com', 110, 257 # 'YourAccount', 'YourPassword') do |m| 258 # file.write m.pop 259 # m.delete if $DELETE 260 # end 261 # 262 def POP3.foreach(address, port = nil, 263 account = nil, password = nil, 264 isapop = false, &block) # :yields: message 265 start(address, port, account, password, isapop) {|pop| 266 pop.each_mail(&block) 267 } 268 end 269 270 # Starts a POP3 session and deletes all messages on the server. 271 # If a block is given, each POPMail object is yielded to it before 272 # being deleted. 273 # 274 # This method raises a POPAuthenticationError if authentication fails. 275 # 276 # === Example 277 # 278 # Net::POP3.delete_all('pop.example.com', 110, 279 # 'YourAccount', 'YourPassword') do |m| 280 # file.write m.pop 281 # end 282 # 283 def POP3.delete_all(address, port = nil, 284 account = nil, password = nil, 285 isapop = false, &block) 286 start(address, port, account, password, isapop) {|pop| 287 pop.delete_all(&block) 288 } 289 end 290 291 # Opens a POP3 session, attempts authentication, and quits. 292 # 293 # This method raises POPAuthenticationError if authentication fails. 294 # 295 # === Example: normal POP3 296 # 297 # Net::POP3.auth_only('pop.example.com', 110, 298 # 'YourAccount', 'YourPassword') 299 # 300 # === Example: APOP 301 # 302 # Net::POP3.auth_only('pop.example.com', 110, 303 # 'YourAccount', 'YourPassword', true) 304 # 305 def POP3.auth_only(address, port = nil, 306 account = nil, password = nil, 307 isapop = false) 308 new(address, port, isapop).auth_only account, password 309 end 310 311 # Starts a pop3 session, attempts authentication, and quits. 312 # This method must not be called while POP3 session is opened. 313 # This method raises POPAuthenticationError if authentication fails. 314 def auth_only(account, password) 315 raise IOError, 'opening previously opened POP session' if started? 316 start(account, password) { 317 ; 318 } 319 end 320 321 # 322 # SSL 323 # 324 325 @ssl_params = nil 326 327 # :call-seq: 328 # Net::POP.enable_ssl(params = {}) 329 # 330 # Enable SSL for all new instances. 331 # +params+ is passed to OpenSSL::SSLContext#set_params. 332 def POP3.enable_ssl(*args) 333 @ssl_params = create_ssl_params(*args) 334 end 335 336 # Constructs proper parameters from arguments 337 def POP3.create_ssl_params(verify_or_params = {}, certs = nil) 338 begin 339 params = verify_or_params.to_hash 340 rescue NoMethodError 341 params = {} 342 params[:verify_mode] = verify_or_params 343 if certs 344 if File.file?(certs) 345 params[:ca_file] = certs 346 elsif File.directory?(certs) 347 params[:ca_path] = certs 348 end 349 end 350 end 351 return params 352 end 353 354 # Disable SSL for all new instances. 355 def POP3.disable_ssl 356 @ssl_params = nil 357 end 358 359 # returns the SSL Parameters 360 # 361 # see also POP3.enable_ssl 362 def POP3.ssl_params 363 return @ssl_params 364 end 365 366 # returns +true+ if POP3.ssl_params is set 367 def POP3.use_ssl? 368 return !@ssl_params.nil? 369 end 370 371 # returns whether verify_mode is enable from POP3.ssl_params 372 def POP3.verify 373 return @ssl_params[:verify_mode] 374 end 375 376 # returns the :ca_file or :ca_path from POP3.ssl_params 377 def POP3.certs 378 return @ssl_params[:ca_file] || @ssl_params[:ca_path] 379 end 380 381 # 382 # Session management 383 # 384 385 # Creates a new POP3 object and open the connection. Equivalent to 386 # 387 # Net::POP3.new(address, port, isapop).start(account, password) 388 # 389 # If +block+ is provided, yields the newly-opened POP3 object to it, 390 # and automatically closes it at the end of the session. 391 # 392 # === Example 393 # 394 # Net::POP3.start(addr, port, account, password) do |pop| 395 # pop.each_mail do |m| 396 # file.write m.pop 397 # m.delete 398 # end 399 # end 400 # 401 def POP3.start(address, port = nil, 402 account = nil, password = nil, 403 isapop = false, &block) # :yield: pop 404 new(address, port, isapop).start(account, password, &block) 405 end 406 407 # Creates a new POP3 object. 408 # 409 # +address+ is the hostname or ip address of your POP3 server. 410 # 411 # The optional +port+ is the port to connect to. 412 # 413 # The optional +isapop+ specifies whether this connection is going 414 # to use APOP authentication; it defaults to +false+. 415 # 416 # This method does *not* open the TCP connection. 417 def initialize(addr, port = nil, isapop = false) 418 @address = addr 419 @ssl_params = POP3.ssl_params 420 @port = port 421 @apop = isapop 422 423 @command = nil 424 @socket = nil 425 @started = false 426 @open_timeout = 30 427 @read_timeout = 60 428 @debug_output = nil 429 430 @mails = nil 431 @n_mails = nil 432 @n_bytes = nil 433 end 434 435 # Does this instance use APOP authentication? 436 def apop? 437 @apop 438 end 439 440 # does this instance use SSL? 441 def use_ssl? 442 return !@ssl_params.nil? 443 end 444 445 # :call-seq: 446 # Net::POP#enable_ssl(params = {}) 447 # 448 # Enables SSL for this instance. Must be called before the connection is 449 # established to have any effect. 450 # +params[:port]+ is port to establish the SSL connection on; Defaults to 995. 451 # +params+ (except :port) is passed to OpenSSL::SSLContext#set_params. 452 def enable_ssl(verify_or_params = {}, certs = nil, port = nil) 453 begin 454 @ssl_params = verify_or_params.to_hash.dup 455 @port = @ssl_params.delete(:port) || @port 456 rescue NoMethodError 457 @ssl_params = POP3.create_ssl_params(verify_or_params, certs) 458 @port = port || @port 459 end 460 end 461 462 # Disable SSL for all new instances. 463 def disable_ssl 464 @ssl_params = nil 465 end 466 467 # Provide human-readable stringification of class state. 468 def inspect 469 "#<#{self.class} #{@address}:#{@port} open=#{@started}>" 470 end 471 472 # *WARNING*: This method causes a serious security hole. 473 # Use this method only for debugging. 474 # 475 # Set an output stream for debugging. 476 # 477 # === Example 478 # 479 # pop = Net::POP.new(addr, port) 480 # pop.set_debug_output $stderr 481 # pop.start(account, passwd) do |pop| 482 # .... 483 # end 484 # 485 def set_debug_output(arg) 486 @debug_output = arg 487 end 488 489 # The address to connect to. 490 attr_reader :address 491 492 # The port number to connect to. 493 def port 494 return @port || (use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port) 495 end 496 497 # Seconds to wait until a connection is opened. 498 # If the POP3 object cannot open a connection within this time, 499 # it raises a Net::OpenTimeout exception. The default value is 30 seconds. 500 attr_accessor :open_timeout 501 502 # Seconds to wait until reading one block (by one read(1) call). 503 # If the POP3 object cannot complete a read() within this time, 504 # it raises a Net::ReadTimeout exception. The default value is 60 seconds. 505 attr_reader :read_timeout 506 507 # Set the read timeout. 508 def read_timeout=(sec) 509 @command.socket.read_timeout = sec if @command 510 @read_timeout = sec 511 end 512 513 # +true+ if the POP3 session has started. 514 def started? 515 @started 516 end 517 518 alias active? started? #:nodoc: obsolete 519 520 # Starts a POP3 session. 521 # 522 # When called with block, gives a POP3 object to the block and 523 # closes the session after block call finishes. 524 # 525 # This method raises a POPAuthenticationError if authentication fails. 526 def start(account, password) # :yield: pop 527 raise IOError, 'POP session already started' if @started 528 if block_given? 529 begin 530 do_start account, password 531 return yield(self) 532 ensure 533 do_finish 534 end 535 else 536 do_start account, password 537 return self 538 end 539 end 540 541 # internal method for Net::POP3.start 542 def do_start(account, password) # :nodoc: 543 s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do 544 TCPSocket.open(@address, port) 545 end 546 if use_ssl? 547 raise 'openssl library not installed' unless defined?(OpenSSL) 548 context = OpenSSL::SSL::SSLContext.new 549 context.set_params(@ssl_params) 550 s = OpenSSL::SSL::SSLSocket.new(s, context) 551 s.sync_close = true 552 s.connect 553 if context.verify_mode != OpenSSL::SSL::VERIFY_NONE 554 s.post_connection_check(@address) 555 end 556 end 557 @socket = InternetMessageIO.new(s) 558 logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})" 559 @socket.read_timeout = @read_timeout 560 @socket.debug_output = @debug_output 561 on_connect 562 @command = POP3Command.new(@socket) 563 if apop? 564 @command.apop account, password 565 else 566 @command.auth account, password 567 end 568 @started = true 569 ensure 570 # Authentication failed, clean up connection. 571 unless @started 572 s.close if s and not s.closed? 573 @socket = nil 574 @command = nil 575 end 576 end 577 private :do_start 578 579 # Does nothing 580 def on_connect # :nodoc: 581 end 582 private :on_connect 583 584 # Finishes a POP3 session and closes TCP connection. 585 def finish 586 raise IOError, 'POP session not yet started' unless started? 587 do_finish 588 end 589 590 # nil's out the: 591 # - mails 592 # - number counter for mails 593 # - number counter for bytes 594 # - quits the current command, if any 595 def do_finish # :nodoc: 596 @mails = nil 597 @n_mails = nil 598 @n_bytes = nil 599 @command.quit if @command 600 ensure 601 @started = false 602 @command = nil 603 @socket.close if @socket and not @socket.closed? 604 @socket = nil 605 end 606 private :do_finish 607 608 # Returns the current command. 609 # 610 # Raises IOError if there is no active socket 611 def command # :nodoc: 612 raise IOError, 'POP session not opened yet' \ 613 if not @socket or @socket.closed? 614 @command 615 end 616 private :command 617 618 # 619 # POP protocol wrapper 620 # 621 622 # Returns the number of messages on the POP server. 623 def n_mails 624 return @n_mails if @n_mails 625 @n_mails, @n_bytes = command().stat 626 @n_mails 627 end 628 629 # Returns the total size in bytes of all the messages on the POP server. 630 def n_bytes 631 return @n_bytes if @n_bytes 632 @n_mails, @n_bytes = command().stat 633 @n_bytes 634 end 635 636 # Returns an array of Net::POPMail objects, representing all the 637 # messages on the server. This array is renewed when the session 638 # restarts; otherwise, it is fetched from the server the first time 639 # this method is called (directly or indirectly) and cached. 640 # 641 # This method raises a POPError if an error occurs. 642 def mails 643 return @mails.dup if @mails 644 if n_mails() == 0 645 # some popd raises error for LIST on the empty mailbox. 646 @mails = [] 647 return [] 648 end 649 650 @mails = command().list.map {|num, size| 651 POPMail.new(num, size, self, command()) 652 } 653 @mails.dup 654 end 655 656 # Yields each message to the passed-in block in turn. 657 # Equivalent to: 658 # 659 # pop3.mails.each do |popmail| 660 # .... 661 # end 662 # 663 # This method raises a POPError if an error occurs. 664 def each_mail(&block) # :yield: message 665 mails().each(&block) 666 end 667 668 alias each each_mail 669 670 # Deletes all messages on the server. 671 # 672 # If called with a block, yields each message in turn before deleting it. 673 # 674 # === Example 675 # 676 # n = 1 677 # pop.delete_all do |m| 678 # File.open("inbox/#{n}") do |f| 679 # f.write m.pop 680 # end 681 # n += 1 682 # end 683 # 684 # This method raises a POPError if an error occurs. 685 # 686 def delete_all # :yield: message 687 mails().each do |m| 688 yield m if block_given? 689 m.delete unless m.deleted? 690 end 691 end 692 693 # Resets the session. This clears all "deleted" marks from messages. 694 # 695 # This method raises a POPError if an error occurs. 696 def reset 697 command().rset 698 mails().each do |m| 699 m.instance_eval { 700 @deleted = false 701 } 702 end 703 end 704 705 def set_all_uids #:nodoc: internal use only (called from POPMail#uidl) 706 uidl = command().uidl 707 @mails.each {|m| m.uid = uidl[m.number] } 708 end 709 710 # deguging output for +msg+ 711 def logging(msg) 712 @debug_output << msg + "\n" if @debug_output 713 end 714 715 end # class POP3 716 717 # class aliases 718 POP = POP3 # :nodoc: 719 POPSession = POP3 # :nodoc: 720 POP3Session = POP3 # :nodoc: 721 722 # 723 # This class is equivalent to POP3, except that it uses APOP authentication. 724 # 725 class APOP < POP3 726 # Always returns true. 727 def apop? 728 true 729 end 730 end 731 732 # class aliases 733 APOPSession = APOP 734 735 # 736 # This class represents a message which exists on the POP server. 737 # Instances of this class are created by the POP3 class; they should 738 # not be directly created by the user. 739 # 740 class POPMail 741 742 def initialize(num, len, pop, cmd) #:nodoc: 743 @number = num 744 @length = len 745 @pop = pop 746 @command = cmd 747 @deleted = false 748 @uid = nil 749 end 750 751 # The sequence number of the message on the server. 752 attr_reader :number 753 754 # The length of the message in octets. 755 attr_reader :length 756 alias size length 757 758 # Provide human-readable stringification of class state. 759 def inspect 760 "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>" 761 end 762 763 # 764 # This method fetches the message. If called with a block, the 765 # message is yielded to the block one chunk at a time. If called 766 # without a block, the message is returned as a String. The optional 767 # +dest+ argument will be prepended to the returned String; this 768 # argument is essentially obsolete. 769 # 770 # === Example without block 771 # 772 # POP3.start('pop.example.com', 110, 773 # 'YourAccount, 'YourPassword') do |pop| 774 # n = 1 775 # pop.mails.each do |popmail| 776 # File.open("inbox/#{n}", 'w') do |f| 777 # f.write popmail.pop 778 # end 779 # popmail.delete 780 # n += 1 781 # end 782 # end 783 # 784 # === Example with block 785 # 786 # POP3.start('pop.example.com', 110, 787 # 'YourAccount, 'YourPassword') do |pop| 788 # n = 1 789 # pop.mails.each do |popmail| 790 # File.open("inbox/#{n}", 'w') do |f| 791 # popmail.pop do |chunk| #### 792 # f.write chunk 793 # end 794 # end 795 # n += 1 796 # end 797 # end 798 # 799 # This method raises a POPError if an error occurs. 800 # 801 def pop( dest = '', &block ) # :yield: message_chunk 802 if block_given? 803 @command.retr(@number, &block) 804 nil 805 else 806 @command.retr(@number) do |chunk| 807 dest << chunk 808 end 809 dest 810 end 811 end 812 813 alias all pop #:nodoc: obsolete 814 alias mail pop #:nodoc: obsolete 815 816 # Fetches the message header and +lines+ lines of body. 817 # 818 # The optional +dest+ argument is obsolete. 819 # 820 # This method raises a POPError if an error occurs. 821 def top(lines, dest = '') 822 @command.top(@number, lines) do |chunk| 823 dest << chunk 824 end 825 dest 826 end 827 828 # Fetches the message header. 829 # 830 # The optional +dest+ argument is obsolete. 831 # 832 # This method raises a POPError if an error occurs. 833 def header(dest = '') 834 top(0, dest) 835 end 836 837 # Marks a message for deletion on the server. Deletion does not 838 # actually occur until the end of the session; deletion may be 839 # cancelled for _all_ marked messages by calling POP3#reset(). 840 # 841 # This method raises a POPError if an error occurs. 842 # 843 # === Example 844 # 845 # POP3.start('pop.example.com', 110, 846 # 'YourAccount, 'YourPassword') do |pop| 847 # n = 1 848 # pop.mails.each do |popmail| 849 # File.open("inbox/#{n}", 'w') do |f| 850 # f.write popmail.pop 851 # end 852 # popmail.delete #### 853 # n += 1 854 # end 855 # end 856 # 857 def delete 858 @command.dele @number 859 @deleted = true 860 end 861 862 alias delete! delete #:nodoc: obsolete 863 864 # True if the mail has been deleted. 865 def deleted? 866 @deleted 867 end 868 869 # Returns the unique-id of the message. 870 # Normally the unique-id is a hash string of the message. 871 # 872 # This method raises a POPError if an error occurs. 873 def unique_id 874 return @uid if @uid 875 @pop.set_all_uids 876 @uid 877 end 878 879 alias uidl unique_id 880 881 def uid=(uid) #:nodoc: internal use only 882 @uid = uid 883 end 884 885 end # class POPMail 886 887 888 class POP3Command #:nodoc: internal use only 889 890 def initialize(sock) 891 @socket = sock 892 @error_occurred = false 893 res = check_response(critical { recv_response() }) 894 @apop_stamp = res.slice(/<[!-~]+@[!-~]+>/) 895 end 896 897 attr_reader :socket 898 899 def inspect 900 "#<#{self.class} socket=#{@socket}>" 901 end 902 903 def auth(account, password) 904 check_response_auth(critical { 905 check_response_auth(get_response('USER %s', account)) 906 get_response('PASS %s', password) 907 }) 908 end 909 910 def apop(account, password) 911 raise POPAuthenticationError, 'not APOP server; cannot login' \ 912 unless @apop_stamp 913 check_response_auth(critical { 914 get_response('APOP %s %s', 915 account, 916 Digest::MD5.hexdigest(@apop_stamp + password)) 917 }) 918 end 919 920 def list 921 critical { 922 getok 'LIST' 923 list = [] 924 @socket.each_list_item do |line| 925 m = /\A(\d+)[ \t]+(\d+)/.match(line) or 926 raise POPBadResponse, "bad response: #{line}" 927 list.push [m[1].to_i, m[2].to_i] 928 end 929 return list 930 } 931 end 932 933 def stat 934 res = check_response(critical { get_response('STAT') }) 935 m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or 936 raise POPBadResponse, "wrong response format: #{res}" 937 [m[1].to_i, m[2].to_i] 938 end 939 940 def rset 941 check_response(critical { get_response('RSET') }) 942 end 943 944 def top(num, lines = 0, &block) 945 critical { 946 getok('TOP %d %d', num, lines) 947 @socket.each_message_chunk(&block) 948 } 949 end 950 951 def retr(num, &block) 952 critical { 953 getok('RETR %d', num) 954 @socket.each_message_chunk(&block) 955 } 956 end 957 958 def dele(num) 959 check_response(critical { get_response('DELE %d', num) }) 960 end 961 962 def uidl(num = nil) 963 if num 964 res = check_response(critical { get_response('UIDL %d', num) }) 965 return res.split(/ /)[1] 966 else 967 critical { 968 getok('UIDL') 969 table = {} 970 @socket.each_list_item do |line| 971 num, uid = line.split 972 table[num.to_i] = uid 973 end 974 return table 975 } 976 end 977 end 978 979 def quit 980 check_response(critical { get_response('QUIT') }) 981 end 982 983 private 984 985 def getok(fmt, *fargs) 986 @socket.writeline sprintf(fmt, *fargs) 987 check_response(recv_response()) 988 end 989 990 def get_response(fmt, *fargs) 991 @socket.writeline sprintf(fmt, *fargs) 992 recv_response() 993 end 994 995 def recv_response 996 @socket.readline 997 end 998 999 def check_response(res) 1000 raise POPError, res unless /\A\+OK/i =~ res 1001 res 1002 end 1003 1004 def check_response_auth(res) 1005 raise POPAuthenticationError, res unless /\A\+OK/i =~ res 1006 res 1007 end 1008 1009 def critical 1010 return '+OK dummy ok response' if @error_occurred 1011 begin 1012 return yield() 1013 rescue Exception 1014 @error_occurred = true 1015 raise 1016 end 1017 end 1018 1019 end # class POP3Command 1020 1021end # module Net 1022