PageRenderTime 169ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/Ruby/StdLib/ruby/1.9.1/net/pop.rb

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