net-pop 0.1.0

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