rubysl-net-pop 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5c2c96967c03048355f1f17b405229c6553fec35
4
+ data.tar.gz: b85de853dc60e4e85b4ea79c9f49eb5d0f1a539f
5
+ SHA512:
6
+ metadata.gz: aecb1ca7aeb605c9b9cb1a692655819e02aa79e38bd4b91a6a03019db4e6eeb5f11c238dc9090dc07d7e4e19325c6fe38b68b5841bb892f65ade155292112e8c
7
+ data.tar.gz: 2f2df75754e68638ad4d75efc3357f67d4504d6af415ff9d138d9cbe5d38a75c73db877ac40c2b86fd3f3a4c0eb206bc86e7b0f1efd0f5ce6af69a8b5a67ccec
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-net-pop.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Rubysl::Net::Pop
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-net-pop'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-net-pop
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/net/pop.rb ADDED
@@ -0,0 +1 @@
1
+ require "rubysl/net/pop"
@@ -0,0 +1,2 @@
1
+ require "rubysl/net/pop/pop"
2
+ require "rubysl/net/pop/version"
@@ -0,0 +1,998 @@
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$
19
+ #
20
+ # See Net::POP3 for documentation.
21
+ #
22
+
23
+ require 'net/protocol'
24
+ require 'digest/md5'
25
+ require 'timeout'
26
+
27
+ begin
28
+ require "openssl/ssl"
29
+ rescue LoadError
30
+ end
31
+
32
+ module 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
+ # = Net::POP3
46
+ #
47
+ # == What is This Library?
48
+ #
49
+ # This library provides functionality for retrieving
50
+ # email via POP3, the Post Office Protocol version 3. For details
51
+ # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt).
52
+ #
53
+ # == Examples
54
+ #
55
+ # === Retrieving Messages
56
+ #
57
+ # This example retrieves messages from the server and deletes them
58
+ # on the server.
59
+ #
60
+ # Messages are written to files named 'inbox/1', 'inbox/2', ....
61
+ # Replace 'pop.example.com' with your POP3 server address, and
62
+ # 'YourAccount' and 'YourPassword' with the appropriate account
63
+ # details.
64
+ #
65
+ # require 'net/pop'
66
+ #
67
+ # pop = Net::POP3.new('pop.example.com')
68
+ # pop.start('YourAccount', 'YourPassword') # (1)
69
+ # if pop.mails.empty?
70
+ # puts 'No mail.'
71
+ # else
72
+ # i = 0
73
+ # pop.each_mail do |m| # or "pop.mails.each ..." # (2)
74
+ # File.open("inbox/#{i}", 'w') do |f|
75
+ # f.write m.pop
76
+ # end
77
+ # m.delete
78
+ # i += 1
79
+ # end
80
+ # puts "#{pop.mails.size} mails popped."
81
+ # end
82
+ # pop.finish # (3)
83
+ #
84
+ # 1. Call Net::POP3#start and start POP session.
85
+ # 2. Access messages by using POP3#each_mail and/or POP3#mails.
86
+ # 3. Close POP session by calling POP3#finish or use the block form of #start.
87
+ #
88
+ # === Shortened Code
89
+ #
90
+ # The example above is very verbose. You can shorten the code by using
91
+ # some utility methods. First, the block form of Net::POP3.start can
92
+ # be used instead of POP3.new, POP3#start and POP3#finish.
93
+ #
94
+ # require 'net/pop'
95
+ #
96
+ # Net::POP3.start('pop.example.com', 110,
97
+ # 'YourAccount', 'YourPassword') do |pop|
98
+ # if pop.mails.empty?
99
+ # puts 'No mail.'
100
+ # else
101
+ # i = 0
102
+ # pop.each_mail do |m| # or "pop.mails.each ..."
103
+ # File.open("inbox/#{i}", 'w') do |f|
104
+ # f.write m.pop
105
+ # end
106
+ # m.delete
107
+ # i += 1
108
+ # end
109
+ # puts "#{pop.mails.size} mails popped."
110
+ # end
111
+ # end
112
+ #
113
+ # POP3#delete_all is an alternative for #each_mail and #delete.
114
+ #
115
+ # require 'net/pop'
116
+ #
117
+ # Net::POP3.start('pop.example.com', 110,
118
+ # 'YourAccount', 'YourPassword') do |pop|
119
+ # if pop.mails.empty?
120
+ # puts 'No mail.'
121
+ # else
122
+ # i = 1
123
+ # pop.delete_all do |m|
124
+ # File.open("inbox/#{i}", 'w') do |f|
125
+ # f.write m.pop
126
+ # end
127
+ # i += 1
128
+ # end
129
+ # end
130
+ # end
131
+ #
132
+ # And here is an even shorter example.
133
+ #
134
+ # require 'net/pop'
135
+ #
136
+ # i = 0
137
+ # Net::POP3.delete_all('pop.example.com', 110,
138
+ # 'YourAccount', 'YourPassword') do |m|
139
+ # File.open("inbox/#{i}", 'w') do |f|
140
+ # f.write m.pop
141
+ # end
142
+ # i += 1
143
+ # end
144
+ #
145
+ # === Memory Space Issues
146
+ #
147
+ # All the examples above get each message as one big string.
148
+ # This example avoids this.
149
+ #
150
+ # require 'net/pop'
151
+ #
152
+ # i = 1
153
+ # Net::POP3.delete_all('pop.example.com', 110,
154
+ # 'YourAccount', 'YourPassword') do |m|
155
+ # File.open("inbox/#{i}", 'w') do |f|
156
+ # m.pop do |chunk| # get a message little by little.
157
+ # f.write chunk
158
+ # end
159
+ # i += 1
160
+ # end
161
+ # end
162
+ #
163
+ # === Using APOP
164
+ #
165
+ # The net/pop library supports APOP authentication.
166
+ # To use APOP, use the Net::APOP class instead of the Net::POP3 class.
167
+ # You can use the utility method, Net::POP3.APOP(). For example:
168
+ #
169
+ # require 'net/pop'
170
+ #
171
+ # # Use APOP authentication if $isapop == true
172
+ # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
173
+ # pop.start(YourAccount', 'YourPassword') do |pop|
174
+ # # Rest of the code is the same.
175
+ # end
176
+ #
177
+ # === Fetch Only Selected Mail Using 'UIDL' POP Command
178
+ #
179
+ # If your POP server provides UIDL functionality,
180
+ # you can grab only selected mails from the POP server.
181
+ # e.g.
182
+ #
183
+ # def need_pop?( id )
184
+ # # determine if we need pop this mail...
185
+ # end
186
+ #
187
+ # Net::POP3.start('pop.example.com', 110,
188
+ # 'Your account', 'Your password') do |pop|
189
+ # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m|
190
+ # do_something(m.pop)
191
+ # end
192
+ # end
193
+ #
194
+ # The POPMail#unique_id() method returns the unique-id of the message as a
195
+ # String. Normally the unique-id is a hash of the message.
196
+ #
197
+ class POP3 < Protocol
198
+
199
+ Revision = %q$Revision$.split[1]
200
+
201
+ #
202
+ # Class Parameters
203
+ #
204
+
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
+ def POP3.create_ssl_params(verify_or_params = {}, certs = nil)
337
+ begin
338
+ params = verify_or_params.to_hash
339
+ rescue NoMethodError
340
+ params = {}
341
+ params[:verify_mode] = verify_or_params
342
+ if certs
343
+ if File.file?(certs)
344
+ params[:ca_file] = certs
345
+ elsif File.directory?(certs)
346
+ params[:ca_path] = certs
347
+ end
348
+ end
349
+ end
350
+ return params
351
+ end
352
+
353
+ # Disable SSL for all new instances.
354
+ def POP3.disable_ssl
355
+ @ssl_params = nil
356
+ end
357
+
358
+ def POP3.ssl_params
359
+ return @ssl_params
360
+ end
361
+
362
+ def POP3.use_ssl?
363
+ return !@ssl_params.nil?
364
+ end
365
+
366
+ def POP3.verify
367
+ return @ssl_params[:verify_mode]
368
+ end
369
+
370
+ def POP3.certs
371
+ return @ssl_params[:ca_file] || @ssl_params[:ca_path]
372
+ end
373
+
374
+ #
375
+ # Session management
376
+ #
377
+
378
+ # Creates a new POP3 object and open the connection. Equivalent to
379
+ #
380
+ # Net::POP3.new(address, port, isapop).start(account, password)
381
+ #
382
+ # If +block+ is provided, yields the newly-opened POP3 object to it,
383
+ # and automatically closes it at the end of the session.
384
+ #
385
+ # === Example
386
+ #
387
+ # Net::POP3.start(addr, port, account, password) do |pop|
388
+ # pop.each_mail do |m|
389
+ # file.write m.pop
390
+ # m.delete
391
+ # end
392
+ # end
393
+ #
394
+ def POP3.start(address, port = nil,
395
+ account = nil, password = nil,
396
+ isapop = false, &block) # :yield: pop
397
+ new(address, port, isapop).start(account, password, &block)
398
+ end
399
+
400
+ # Creates a new POP3 object.
401
+ #
402
+ # +address+ is the hostname or ip address of your POP3 server.
403
+ #
404
+ # The optional +port+ is the port to connect to.
405
+ #
406
+ # The optional +isapop+ specifies whether this connection is going
407
+ # to use APOP authentication; it defaults to +false+.
408
+ #
409
+ # This method does *not* open the TCP connection.
410
+ def initialize(addr, port = nil, isapop = false)
411
+ @address = addr
412
+ @ssl_params = POP3.ssl_params
413
+ @port = port
414
+ @apop = isapop
415
+
416
+ @command = nil
417
+ @socket = nil
418
+ @started = false
419
+ @open_timeout = 30
420
+ @read_timeout = 60
421
+ @debug_output = nil
422
+
423
+ @mails = nil
424
+ @n_mails = nil
425
+ @n_bytes = nil
426
+ end
427
+
428
+ # Does this instance use APOP authentication?
429
+ def apop?
430
+ @apop
431
+ end
432
+
433
+ # does this instance use SSL?
434
+ def use_ssl?
435
+ return !@ssl_params.nil?
436
+ end
437
+
438
+ # call-seq:
439
+ # Net::POP#enable_ssl(params = {})
440
+ #
441
+ # Enables SSL for this instance. Must be called before the connection is
442
+ # established to have any effect.
443
+ # +params[:port]+ is port to establish the SSL connection on; Defaults to 995.
444
+ # +params+ (except :port) is passed to OpenSSL::SSLContext#set_params.
445
+ def enable_ssl(verify_or_params = {}, certs = nil, port = nil)
446
+ begin
447
+ @ssl_params = verify_or_params.to_hash.dup
448
+ @port = @ssl_params.delete(:port) || @port
449
+ rescue NoMethodError
450
+ @ssl_params = POP3.create_ssl_params(verify_or_params, certs)
451
+ @port = port || @port
452
+ end
453
+ end
454
+
455
+ def disable_ssl
456
+ @ssl_params = nil
457
+ end
458
+
459
+ # Provide human-readable stringification of class state.
460
+ def inspect
461
+ "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
462
+ end
463
+
464
+ # *WARNING*: This method causes a serious security hole.
465
+ # Use this method only for debugging.
466
+ #
467
+ # Set an output stream for debugging.
468
+ #
469
+ # === Example
470
+ #
471
+ # pop = Net::POP.new(addr, port)
472
+ # pop.set_debug_output $stderr
473
+ # pop.start(account, passwd) do |pop|
474
+ # ....
475
+ # end
476
+ #
477
+ def set_debug_output(arg)
478
+ @debug_output = arg
479
+ end
480
+
481
+ # The address to connect to.
482
+ attr_reader :address
483
+
484
+ # The port number to connect to.
485
+ def port
486
+ return @port || (use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port)
487
+ end
488
+
489
+ # Seconds to wait until a connection is opened.
490
+ # If the POP3 object cannot open a connection within this time,
491
+ # it raises a TimeoutError exception.
492
+ attr_accessor :open_timeout
493
+
494
+ # Seconds to wait until reading one block (by one read(1) call).
495
+ # If the POP3 object cannot complete a read() within this time,
496
+ # it raises a TimeoutError exception.
497
+ attr_reader :read_timeout
498
+
499
+ # Set the read timeout.
500
+ def read_timeout=(sec)
501
+ @command.socket.read_timeout = sec if @command
502
+ @read_timeout = sec
503
+ end
504
+
505
+ # +true+ if the POP3 session has started.
506
+ def started?
507
+ @started
508
+ end
509
+
510
+ alias active? started? #:nodoc: obsolete
511
+
512
+ # Starts a POP3 session.
513
+ #
514
+ # When called with block, gives a POP3 object to the block and
515
+ # closes the session after block call finishes.
516
+ #
517
+ # This method raises a POPAuthenticationError if authentication fails.
518
+ def start(account, password) # :yield: pop
519
+ raise IOError, 'POP session already started' if @started
520
+ if block_given?
521
+ begin
522
+ do_start account, password
523
+ return yield(self)
524
+ ensure
525
+ do_finish
526
+ end
527
+ else
528
+ do_start account, password
529
+ return self
530
+ end
531
+ end
532
+
533
+ def do_start(account, password)
534
+ s = timeout(@open_timeout) { TCPSocket.open(@address, port) }
535
+ if use_ssl?
536
+ raise 'openssl library not installed' unless defined?(OpenSSL)
537
+ context = OpenSSL::SSL::SSLContext.new
538
+ context.set_params(@ssl_params)
539
+ s = OpenSSL::SSL::SSLSocket.new(s, context)
540
+ s.sync_close = true
541
+ s.connect
542
+ if context.verify_mode != OpenSSL::SSL::VERIFY_NONE
543
+ s.post_connection_check(@address)
544
+ end
545
+ end
546
+ @socket = InternetMessageIO.new(s)
547
+ logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})"
548
+ @socket.read_timeout = @read_timeout
549
+ @socket.debug_output = @debug_output
550
+ on_connect
551
+ @command = POP3Command.new(@socket)
552
+ if apop?
553
+ @command.apop account, password
554
+ else
555
+ @command.auth account, password
556
+ end
557
+ @started = true
558
+ ensure
559
+ # Authentication failed, clean up connection.
560
+ unless @started
561
+ s.close if s and not s.closed?
562
+ @socket = nil
563
+ @command = nil
564
+ end
565
+ end
566
+ private :do_start
567
+
568
+ def on_connect
569
+ end
570
+ private :on_connect
571
+
572
+ # Finishes a POP3 session and closes TCP connection.
573
+ def finish
574
+ raise IOError, 'POP session not yet started' unless started?
575
+ do_finish
576
+ end
577
+
578
+ def do_finish
579
+ @mails = nil
580
+ @n_mails = nil
581
+ @n_bytes = nil
582
+ @command.quit if @command
583
+ ensure
584
+ @started = false
585
+ @command = nil
586
+ @socket.close if @socket and not @socket.closed?
587
+ @socket = nil
588
+ end
589
+ private :do_finish
590
+
591
+ def command
592
+ raise IOError, 'POP session not opened yet' \
593
+ if not @socket or @socket.closed?
594
+ @command
595
+ end
596
+ private :command
597
+
598
+ #
599
+ # POP protocol wrapper
600
+ #
601
+
602
+ # Returns the number of messages on the POP server.
603
+ def n_mails
604
+ return @n_mails if @n_mails
605
+ @n_mails, @n_bytes = command().stat
606
+ @n_mails
607
+ end
608
+
609
+ # Returns the total size in bytes of all the messages on the POP server.
610
+ def n_bytes
611
+ return @n_bytes if @n_bytes
612
+ @n_mails, @n_bytes = command().stat
613
+ @n_bytes
614
+ end
615
+
616
+ # Returns an array of Net::POPMail objects, representing all the
617
+ # messages on the server. This array is renewed when the session
618
+ # restarts; otherwise, it is fetched from the server the first time
619
+ # this method is called (directly or indirectly) and cached.
620
+ #
621
+ # This method raises a POPError if an error occurs.
622
+ def mails
623
+ return @mails.dup if @mails
624
+ if n_mails() == 0
625
+ # some popd raises error for LIST on the empty mailbox.
626
+ @mails = []
627
+ return []
628
+ end
629
+
630
+ @mails = command().list.map {|num, size|
631
+ POPMail.new(num, size, self, command())
632
+ }
633
+ @mails.dup
634
+ end
635
+
636
+ # Yields each message to the passed-in block in turn.
637
+ # Equivalent to:
638
+ #
639
+ # pop3.mails.each do |popmail|
640
+ # ....
641
+ # end
642
+ #
643
+ # This method raises a POPError if an error occurs.
644
+ def each_mail(&block) # :yield: message
645
+ mails().each(&block)
646
+ end
647
+
648
+ alias each each_mail
649
+
650
+ # Deletes all messages on the server.
651
+ #
652
+ # If called with a block, yields each message in turn before deleting it.
653
+ #
654
+ # === Example
655
+ #
656
+ # n = 1
657
+ # pop.delete_all do |m|
658
+ # File.open("inbox/#{n}") do |f|
659
+ # f.write m.pop
660
+ # end
661
+ # n += 1
662
+ # end
663
+ #
664
+ # This method raises a POPError if an error occurs.
665
+ #
666
+ def delete_all # :yield: message
667
+ mails().each do |m|
668
+ yield m if block_given?
669
+ m.delete unless m.deleted?
670
+ end
671
+ end
672
+
673
+ # Resets the session. This clears all "deleted" marks from messages.
674
+ #
675
+ # This method raises a POPError if an error occurs.
676
+ def reset
677
+ command().rset
678
+ mails().each do |m|
679
+ m.instance_eval {
680
+ @deleted = false
681
+ }
682
+ end
683
+ end
684
+
685
+ def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
686
+ uidl = command().uidl
687
+ @mails.each {|m| m.uid = uidl[m.number] }
688
+ end
689
+
690
+ def logging(msg)
691
+ @debug_output << msg + "\n" if @debug_output
692
+ end
693
+
694
+ end # class POP3
695
+
696
+ # class aliases
697
+ POP = POP3
698
+ POPSession = POP3
699
+ POP3Session = POP3
700
+
701
+ #
702
+ # This class is equivalent to POP3, except that it uses APOP authentication.
703
+ #
704
+ class APOP < POP3
705
+ # Always returns true.
706
+ def apop?
707
+ true
708
+ end
709
+ end
710
+
711
+ # class aliases
712
+ APOPSession = APOP
713
+
714
+ #
715
+ # This class represents a message which exists on the POP server.
716
+ # Instances of this class are created by the POP3 class; they should
717
+ # not be directly created by the user.
718
+ #
719
+ class POPMail
720
+
721
+ def initialize(num, len, pop, cmd) #:nodoc:
722
+ @number = num
723
+ @length = len
724
+ @pop = pop
725
+ @command = cmd
726
+ @deleted = false
727
+ @uid = nil
728
+ end
729
+
730
+ # The sequence number of the message on the server.
731
+ attr_reader :number
732
+
733
+ # The length of the message in octets.
734
+ attr_reader :length
735
+ alias size length
736
+
737
+ # Provide human-readable stringification of class state.
738
+ def inspect
739
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
740
+ end
741
+
742
+ #
743
+ # This method fetches the message. If called with a block, the
744
+ # message is yielded to the block one chunk at a time. If called
745
+ # without a block, the message is returned as a String. The optional
746
+ # +dest+ argument will be prepended to the returned String; this
747
+ # argument is essentially obsolete.
748
+ #
749
+ # === Example without block
750
+ #
751
+ # POP3.start('pop.example.com', 110,
752
+ # 'YourAccount, 'YourPassword') do |pop|
753
+ # n = 1
754
+ # pop.mails.each do |popmail|
755
+ # File.open("inbox/#{n}", 'w') do |f|
756
+ # f.write popmail.pop
757
+ # end
758
+ # popmail.delete
759
+ # n += 1
760
+ # end
761
+ # end
762
+ #
763
+ # === Example with block
764
+ #
765
+ # POP3.start('pop.example.com', 110,
766
+ # 'YourAccount, 'YourPassword') do |pop|
767
+ # n = 1
768
+ # pop.mails.each do |popmail|
769
+ # File.open("inbox/#{n}", 'w') do |f|
770
+ # popmail.pop do |chunk| ####
771
+ # f.write chunk
772
+ # end
773
+ # end
774
+ # n += 1
775
+ # end
776
+ # end
777
+ #
778
+ # This method raises a POPError if an error occurs.
779
+ #
780
+ def pop( dest = '', &block ) # :yield: message_chunk
781
+ if block_given?
782
+ @command.retr(@number, &block)
783
+ nil
784
+ else
785
+ @command.retr(@number) do |chunk|
786
+ dest << chunk
787
+ end
788
+ dest
789
+ end
790
+ end
791
+
792
+ alias all pop #:nodoc: obsolete
793
+ alias mail pop #:nodoc: obsolete
794
+
795
+ # Fetches the message header and +lines+ lines of body.
796
+ #
797
+ # The optional +dest+ argument is obsolete.
798
+ #
799
+ # This method raises a POPError if an error occurs.
800
+ def top(lines, dest = '')
801
+ @command.top(@number, lines) do |chunk|
802
+ dest << chunk
803
+ end
804
+ dest
805
+ end
806
+
807
+ # Fetches the message header.
808
+ #
809
+ # The optional +dest+ argument is obsolete.
810
+ #
811
+ # This method raises a POPError if an error occurs.
812
+ def header(dest = '')
813
+ top(0, dest)
814
+ end
815
+
816
+ # Marks a message for deletion on the server. Deletion does not
817
+ # actually occur until the end of the session; deletion may be
818
+ # cancelled for _all_ marked messages by calling POP3#reset().
819
+ #
820
+ # This method raises a POPError if an error occurs.
821
+ #
822
+ # === Example
823
+ #
824
+ # POP3.start('pop.example.com', 110,
825
+ # 'YourAccount, 'YourPassword') do |pop|
826
+ # n = 1
827
+ # pop.mails.each do |popmail|
828
+ # File.open("inbox/#{n}", 'w') do |f|
829
+ # f.write popmail.pop
830
+ # end
831
+ # popmail.delete ####
832
+ # n += 1
833
+ # end
834
+ # end
835
+ #
836
+ def delete
837
+ @command.dele @number
838
+ @deleted = true
839
+ end
840
+
841
+ alias delete! delete #:nodoc: obsolete
842
+
843
+ # True if the mail has been deleted.
844
+ def deleted?
845
+ @deleted
846
+ end
847
+
848
+ # Returns the unique-id of the message.
849
+ # Normally the unique-id is a hash string of the message.
850
+ #
851
+ # This method raises a POPError if an error occurs.
852
+ def unique_id
853
+ return @uid if @uid
854
+ @pop.set_all_uids
855
+ @uid
856
+ end
857
+
858
+ alias uidl unique_id
859
+
860
+ def uid=(uid) #:nodoc: internal use only
861
+ @uid = uid
862
+ end
863
+
864
+ end # class POPMail
865
+
866
+
867
+ class POP3Command #:nodoc: internal use only
868
+
869
+ def initialize(sock)
870
+ @socket = sock
871
+ @error_occured = false
872
+ res = check_response(critical { recv_response() })
873
+ @apop_stamp = res.slice(/<[!-~]+@[!-~]+>/)
874
+ end
875
+
876
+ def inspect
877
+ "#<#{self.class} socket=#{@socket}>"
878
+ end
879
+
880
+ def auth(account, password)
881
+ check_response_auth(critical {
882
+ check_response_auth(get_response('USER %s', account))
883
+ get_response('PASS %s', password)
884
+ })
885
+ end
886
+
887
+ def apop(account, password)
888
+ raise POPAuthenticationError, 'not APOP server; cannot login' \
889
+ unless @apop_stamp
890
+ check_response_auth(critical {
891
+ get_response('APOP %s %s',
892
+ account,
893
+ Digest::MD5.hexdigest(@apop_stamp + password))
894
+ })
895
+ end
896
+
897
+ def list
898
+ critical {
899
+ getok 'LIST'
900
+ list = []
901
+ @socket.each_list_item do |line|
902
+ m = /\A(\d+)[ \t]+(\d+)/.match(line) or
903
+ raise POPBadResponse, "bad response: #{line}"
904
+ list.push [m[1].to_i, m[2].to_i]
905
+ end
906
+ return list
907
+ }
908
+ end
909
+
910
+ def stat
911
+ res = check_response(critical { get_response('STAT') })
912
+ m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
913
+ raise POPBadResponse, "wrong response format: #{res}"
914
+ [m[1].to_i, m[2].to_i]
915
+ end
916
+
917
+ def rset
918
+ check_response(critical { get_response('RSET') })
919
+ end
920
+
921
+ def top(num, lines = 0, &block)
922
+ critical {
923
+ getok('TOP %d %d', num, lines)
924
+ @socket.each_message_chunk(&block)
925
+ }
926
+ end
927
+
928
+ def retr(num, &block)
929
+ critical {
930
+ getok('RETR %d', num)
931
+ @socket.each_message_chunk(&block)
932
+ }
933
+ end
934
+
935
+ def dele(num)
936
+ check_response(critical { get_response('DELE %d', num) })
937
+ end
938
+
939
+ def uidl(num = nil)
940
+ if num
941
+ res = check_response(critical { get_response('UIDL %d', num) })
942
+ return res.split(/ /)[1]
943
+ else
944
+ critical {
945
+ getok('UIDL')
946
+ table = {}
947
+ @socket.each_list_item do |line|
948
+ num, uid = line.split
949
+ table[num.to_i] = uid
950
+ end
951
+ return table
952
+ }
953
+ end
954
+ end
955
+
956
+ def quit
957
+ check_response(critical { get_response('QUIT') })
958
+ end
959
+
960
+ private
961
+
962
+ def getok(fmt, *fargs)
963
+ @socket.writeline sprintf(fmt, *fargs)
964
+ check_response(recv_response())
965
+ end
966
+
967
+ def get_response(fmt, *fargs)
968
+ @socket.writeline sprintf(fmt, *fargs)
969
+ recv_response()
970
+ end
971
+
972
+ def recv_response
973
+ @socket.readline
974
+ end
975
+
976
+ def check_response(res)
977
+ raise POPError, res unless /\A\+OK/i =~ res
978
+ res
979
+ end
980
+
981
+ def check_response_auth(res)
982
+ raise POPAuthenticationError, res unless /\A\+OK/i =~ res
983
+ res
984
+ end
985
+
986
+ def critical
987
+ return '+OK dummy ok response' if @error_occured
988
+ begin
989
+ return yield()
990
+ rescue Exception
991
+ @error_occured = true
992
+ raise
993
+ end
994
+ end
995
+
996
+ end # class POP3Command
997
+
998
+ end # module Net
@@ -0,0 +1,7 @@
1
+ module RubySL
2
+ module Net
3
+ module POP
4
+ VERSION = "1.0.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require './lib/rubysl/net/pop/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "rubysl-net-pop"
6
+ spec.version = RubySL::Net::POP::VERSION
7
+ spec.authors = ["Brian Shirai"]
8
+ spec.email = ["brixen@gmail.com"]
9
+ spec.description = %q{Ruby standard library pop.}
10
+ spec.summary = %q{Ruby standard library pop.}
11
+ spec.homepage = "https://github.com/rubysl/rubysl-net-pop"
12
+ spec.license = "BSD"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_development_dependency "mspec", "~> 1.5"
22
+ spec.add_development_dependency "rubysl-prettyprint", "~> 1.0"
23
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubysl-net-pop
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Shirai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubysl-prettyprint
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: Ruby standard library pop.
70
+ email:
71
+ - brixen@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .travis.yml
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - lib/net/pop.rb
83
+ - lib/rubysl/net/pop.rb
84
+ - lib/rubysl/net/pop/pop.rb
85
+ - lib/rubysl/net/pop/version.rb
86
+ - rubysl-net-pop.gemspec
87
+ homepage: https://github.com/rubysl/rubysl-net-pop
88
+ licenses:
89
+ - BSD
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.0.7
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Ruby standard library pop.
111
+ test_files: []