tlsmail_ext 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt ADDED
@@ -0,0 +1,4 @@
1
+ December 9, 2010
2
+ - Imported the old gem so I could do some work on it.
3
+ - Turned off verbosity while assigning class alias. Silences the "warning: already initialized constant XXXX" error.
4
+ - Bumped version to 0.0.2
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ Rakefile
2
+ README.txt
3
+ CHANGELOG.txt
4
+ Manifest.txt
5
+ lib/tlsmail.rb
6
+ lib/net/pop.rb
7
+ lib/net/smtp.rb
8
+ test/test_helper.rb
9
+ test/tlsmail_test.rb
10
+ test/template.parameters.rb
data/README.txt ADDED
@@ -0,0 +1,48 @@
1
+ == Installation
2
+
3
+ # gem install tlsmail
4
+
5
+ == License
6
+
7
+ This library is public domain software and provided without warranty of any kind.
8
+
9
+ == Author
10
+
11
+ zoriorz@gmail.com, xrlange@gmail.com
12
+
13
+ == Description
14
+
15
+ This library dynamically replace net/smtp and net/pop with these in ruby 1.9 and
16
+ enables pop or smtp via SSL/TLS.
17
+
18
+ This is tested on Ruby 1.8.4 and 1.8.5(both windows).
19
+
20
+ == Usage
21
+
22
+ === SMTP
23
+
24
+ def send_message
25
+ require "time"
26
+ msgstr = <<END_OF_MESSAGE
27
+ From: Your Name <you@gmail.com>
28
+ To: Destination Address <yourfriend@gmail.com>
29
+ Subject: test message
30
+ Date: #{Time.now.rfc2822}
31
+
32
+ test message body.
33
+ END_OF_MESSAGE
34
+
35
+ Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
36
+ Net::SMTP.start("smtp.gmail.com", "587", "your.domain", "username", "password", :plain) do |smtp|
37
+ smtp.send_message msgstr, "you@gmail.com", "yourfriend@gmail.com"
38
+ end
39
+ end
40
+
41
+ === POP
42
+
43
+ def receive_messages
44
+ Net::POP.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
45
+ Net::POP.start("pop.gmail.com", "995", "username", "password") do |pop|
46
+ p pop.mails[0].pop
47
+ end
48
+ end
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ include FileUtils
12
+ #require File.join(File.dirname(__FILE__), 'lib', 'tlsmail', 'version')
13
+
14
+ AUTHOR = "zorio" # can also be an array of Authors
15
+ EMAIL = "zoriorz@gmail.com"
16
+ DESCRIPTION = "This library enables pop or smtp via ssl/tls by dynamically replacing these classes to these in ruby 1.9."
17
+ GEM_NAME = "tlsmail" # what ppl will type to install your gem
18
+ RUBYFORGE_PROJECT = "tlsmail" # The unix name for your project
19
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
+
21
+
22
+ NAME = "tlsmail"
23
+ REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
24
+ VERS = File.read('VERSION').chomp
25
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
26
+ RDOC_OPTS = ['--quiet', '--title', "tlsmail documentation",
27
+ "--opname", "index.html",
28
+ "--line-numbers",
29
+ "--main", "README",
30
+ "--inline-source"]
31
+
32
+ class Hoe
33
+ def extra_deps
34
+ @extra_deps.reject { |x| Array(x).first == 'hoe' }
35
+ end
36
+ end
37
+
38
+ # Generate all the Rake tasks
39
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
40
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
41
+ p.author = AUTHOR
42
+ p.description = DESCRIPTION
43
+ p.email = EMAIL
44
+ p.summary = DESCRIPTION
45
+ p.url = HOMEPATH
46
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
47
+ p.test_globs = ["test/**/*_test.rb"]
48
+ p.clean_globs = CLEAN #An array of file patterns to delete on clean.
49
+ p.need_tar = false
50
+ # == Optional
51
+ #p.changes - A description of the release's latest changes.
52
+ #p.extra_deps - An array of rubygem dependencies.
53
+ #p.spec_extras - A hash of extra values to set in the gemspec.
54
+ end
data/lib/net/pop.rb ADDED
@@ -0,0 +1,989 @@
1
+ # = net/pop.rb
2
+ #
3
+ # Copyright (c) 1999-2003 Yukihiro Matsumoto.
4
+ #
5
+ # Copyright (c) 1999-2003 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/index.cgi?cmd=view;name=net%2Fpop.rb
17
+ #
18
+ # $Id: pop.rb 6285 2004-05-09 16:19:35Z aamine $
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"
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
+ Net.instance_eval {remove_const :Revision} if defined?(Revision) # Revision = %q$Revision: 6285 $.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
+ @use_ssl = false
326
+ @verify = nil
327
+ @certs = nil
328
+
329
+ # Enable SSL for all new instances.
330
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
331
+ # to OpenSSL::SSL::VERIFY_PEER.
332
+ # +certs+ is a file or directory holding CA certs to use to verify the
333
+ # server cert; Defaults to nil.
334
+ def POP3.enable_ssl(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil)
335
+ @use_ssl = true
336
+ @verify = verify
337
+ @certs = certs
338
+ end
339
+
340
+ # Disable SSL for all new instances.
341
+ def POP3.disable_ssl
342
+ @use_ssl = nil
343
+ @verify = nil
344
+ @certs = nil
345
+ end
346
+
347
+ def POP3.use_ssl?
348
+ @use_ssl
349
+ end
350
+
351
+ def POP3.verify
352
+ @verify
353
+ end
354
+
355
+ def POP3.certs
356
+ @certs
357
+ end
358
+
359
+ #
360
+ # Session management
361
+ #
362
+
363
+ # Creates a new POP3 object and open the connection. Equivalent to
364
+ #
365
+ # Net::POP3.new(address, port, isapop).start(account, password)
366
+ #
367
+ # If +block+ is provided, yields the newly-opened POP3 object to it,
368
+ # and automatically closes it at the end of the session.
369
+ #
370
+ # === Example
371
+ #
372
+ # Net::POP3.start(addr, port, account, password) do |pop|
373
+ # pop.each_mail do |m|
374
+ # file.write m.pop
375
+ # m.delete
376
+ # end
377
+ # end
378
+ #
379
+ def POP3.start(address, port = nil,
380
+ account = nil, password = nil,
381
+ isapop = false, &block) # :yield: pop
382
+ new(address, port, isapop).start(account, password, &block)
383
+ end
384
+
385
+ # Creates a new POP3 object.
386
+ #
387
+ # +address+ is the hostname or ip address of your POP3 server.
388
+ #
389
+ # The optional +port+ is the port to connect to.
390
+ #
391
+ # The optional +isapop+ specifies whether this connection is going
392
+ # to use APOP authentication; it defaults to +false+.
393
+ #
394
+ # This method does *not* open the TCP connection.
395
+ def initialize(addr, port = nil, isapop = false)
396
+ @address = addr
397
+ @use_ssl = POP3.use_ssl?
398
+ @port = port || (POP3.use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port)
399
+ @apop = isapop
400
+ @certs = POP3.certs
401
+ @verify = POP3.verify
402
+
403
+ @command = nil
404
+ @socket = nil
405
+ @started = false
406
+ @open_timeout = 30
407
+ @read_timeout = 60
408
+ @debug_output = nil
409
+
410
+ @mails = nil
411
+ @n_mails = nil
412
+ @n_bytes = nil
413
+ end
414
+
415
+ # Does this instance use APOP authentication?
416
+ def apop?
417
+ @apop
418
+ end
419
+
420
+ # does this instance use SSL?
421
+ def use_ssl?
422
+ @use_ssl
423
+ end
424
+
425
+ # Enables SSL for this instance. Must be called before the connection is
426
+ # established to have any effect.
427
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
428
+ # to OpenSSL::SSL::VERIFY_PEER.
429
+ # +certs+ is a file or directory holding CA certs to use to verify the
430
+ # server cert; Defaults to nil.
431
+ # +port+ is port to establish the SSL connection on; Defaults to 995.
432
+ def enable_ssl(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil,
433
+ port = POP3.default_pop3s_port)
434
+ @use_ssl = true
435
+ @verify = verify
436
+ @certs = certs
437
+ @port = port
438
+ end
439
+
440
+ def disable_ssl
441
+ @use_ssl = false
442
+ @verify = nil
443
+ @certs = nil
444
+ end
445
+
446
+ # Provide human-readable stringification of class state.
447
+ def inspect
448
+ "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
449
+ end
450
+
451
+ # *WARNING*: This method causes a serious security hole.
452
+ # Use this method only for debugging.
453
+ #
454
+ # Set an output stream for debugging.
455
+ #
456
+ # === Example
457
+ #
458
+ # pop = Net::POP.new(addr, port)
459
+ # pop.set_debug_output $stderr
460
+ # pop.start(account, passwd) do |pop|
461
+ # ....
462
+ # end
463
+ #
464
+ def set_debug_output(arg)
465
+ @debug_output = arg
466
+ end
467
+
468
+ # The address to connect to.
469
+ attr_reader :address
470
+
471
+ # The port number to connect to.
472
+ attr_reader :port
473
+
474
+ # Seconds to wait until a connection is opened.
475
+ # If the POP3 object cannot open a connection within this time,
476
+ # it raises a TimeoutError exception.
477
+ attr_accessor :open_timeout
478
+
479
+ # Seconds to wait until reading one block (by one read(1) call).
480
+ # If the POP3 object cannot complete a read() within this time,
481
+ # it raises a TimeoutError exception.
482
+ attr_reader :read_timeout
483
+
484
+ # Set the read timeout.
485
+ def read_timeout=(sec)
486
+ @command.socket.read_timeout = sec if @command
487
+ @read_timeout = sec
488
+ end
489
+
490
+ # +true+ if the POP3 session has started.
491
+ def started?
492
+ @started
493
+ end
494
+
495
+ alias active? started? #:nodoc: obsolete
496
+
497
+ # Starts a POP3 session.
498
+ #
499
+ # When called with block, gives a POP3 object to the block and
500
+ # closes the session after block call finishes.
501
+ #
502
+ # This method raises a POPAuthenticationError if authentication fails.
503
+ def start(account, password) # :yield: pop
504
+ raise IOError, 'POP session already started' if @started
505
+ if block_given?
506
+ begin
507
+ do_start account, password
508
+ return yield(self)
509
+ ensure
510
+ do_finish
511
+ end
512
+ else
513
+ do_start account, password
514
+ return self
515
+ end
516
+ end
517
+
518
+ def do_start(account, password)
519
+ s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
520
+ if use_ssl?
521
+ raise 'openssl library not installed' unless defined?(OpenSSL)
522
+ context = OpenSSL::SSL::SSLContext.new
523
+ context.verify_mode = @verify
524
+ if @certs
525
+ if File.file?(@certs)
526
+ context.ca_file = @certs
527
+ elsif File.directory?(@certs)
528
+ context.ca_path = @certs
529
+ else
530
+ raise ArgumentError, "certs path is not file/directory: #{@certs}"
531
+ end
532
+ end
533
+ s = OpenSSL::SSL::SSLSocket.new(s, context)
534
+ s.sync_close = true
535
+ s.connect
536
+ end
537
+ @socket = InternetMessageIO.new(s)
538
+ logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})"
539
+ @socket.read_timeout = @read_timeout
540
+ @socket.debug_output = @debug_output
541
+ on_connect
542
+ @command = POP3Command.new(@socket)
543
+ if apop?
544
+ @command.apop account, password
545
+ else
546
+ @command.auth account, password
547
+ end
548
+ @started = true
549
+ ensure
550
+ # Authentication failed, clean up connection.
551
+ unless @started
552
+ s.close if s and not s.closed?
553
+ @socket = nil
554
+ @command = nil
555
+ end
556
+ end
557
+ private :do_start
558
+
559
+ def on_connect
560
+ end
561
+ private :on_connect
562
+
563
+ # Finishes a POP3 session and closes TCP connection.
564
+ def finish
565
+ raise IOError, 'POP session not yet started' unless started?
566
+ do_finish
567
+ end
568
+
569
+ def do_finish
570
+ @mails = nil
571
+ @command.quit if @command
572
+ ensure
573
+ @started = false
574
+ @command = nil
575
+ @socket.close if @socket and not @socket.closed?
576
+ @socket = nil
577
+ end
578
+ private :do_finish
579
+
580
+ def command
581
+ raise IOError, 'POP session not opened yet' \
582
+ if not @socket or @socket.closed?
583
+ @command
584
+ end
585
+ private :command
586
+
587
+ #
588
+ # POP protocol wrapper
589
+ #
590
+
591
+ # Returns the number of messages on the POP server.
592
+ def n_mails
593
+ return @n_mails if @n_mails
594
+ @n_mails, @n_bytes = command().stat
595
+ @n_mails
596
+ end
597
+
598
+ # Returns the total size in bytes of all the messages on the POP server.
599
+ def n_bytes
600
+ return @n_bytes if @n_bytes
601
+ @n_mails, @n_bytes = command().stat
602
+ @n_bytes
603
+ end
604
+
605
+ # Returns an array of Net::POPMail objects, representing all the
606
+ # messages on the server. This array is renewed when the session
607
+ # restarts; otherwise, it is fetched from the server the first time
608
+ # this method is called (directly or indirectly) and cached.
609
+ #
610
+ # This method raises a POPError if an error occurs.
611
+ def mails
612
+ return @mails.dup if @mails
613
+ if n_mails() == 0
614
+ # some popd raises error for LIST on the empty mailbox.
615
+ @mails = []
616
+ return []
617
+ end
618
+
619
+ @mails = command().list.map {|num, size|
620
+ POPMail.new(num, size, self, command())
621
+ }
622
+ @mails.dup
623
+ end
624
+
625
+ # Yields each message to the passed-in block in turn.
626
+ # Equivalent to:
627
+ #
628
+ # pop3.mails.each do |popmail|
629
+ # ....
630
+ # end
631
+ #
632
+ # This method raises a POPError if an error occurs.
633
+ def each_mail(&block) # :yield: message
634
+ mails().each(&block)
635
+ end
636
+
637
+ alias each each_mail
638
+
639
+ # Deletes all messages on the server.
640
+ #
641
+ # If called with a block, yields each message in turn before deleting it.
642
+ #
643
+ # === Example
644
+ #
645
+ # n = 1
646
+ # pop.delete_all do |m|
647
+ # File.open("inbox/#{n}") do |f|
648
+ # f.write m.pop
649
+ # end
650
+ # n += 1
651
+ # end
652
+ #
653
+ # This method raises a POPError if an error occurs.
654
+ #
655
+ def delete_all # :yield: message
656
+ mails().each do |m|
657
+ yield m if block_given?
658
+ m.delete unless m.deleted?
659
+ end
660
+ end
661
+
662
+ # Resets the session. This clears all "deleted" marks from messages.
663
+ #
664
+ # This method raises a POPError if an error occurs.
665
+ def reset
666
+ command().rset
667
+ mails().each do |m|
668
+ m.instance_eval {
669
+ @deleted = false
670
+ }
671
+ end
672
+ end
673
+
674
+ def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
675
+ command().uidl.each do |num, uid|
676
+ @mails.find {|m| m.number == num }.uid = uid
677
+ end
678
+ end
679
+
680
+ def logging(msg)
681
+ @debug_output << msg + "\n" if @debug_output
682
+ end
683
+
684
+ end # class POP3
685
+
686
+ #
687
+ # This class is equivalent to POP3, except that it uses APOP authentication.
688
+ #
689
+ class APOP < POP3
690
+ # Always returns true.
691
+ def apop?
692
+ true
693
+ end
694
+ end
695
+
696
+ # class aliases
697
+ saved_verbosity = $-v
698
+ $-v = nil
699
+ Net.instance_eval {remove_const :POP} if defined?(POP) # POP = POP3
700
+ Net.instance_eval {remove_const :POPSession} if defined?(POPSession)# POPSession = POP3
701
+ Net.instance_eval {remove_const :POP3Session} if defined?(POP3Session)# POP3Session = POP3
702
+ Net.instance_eval {remove_const :APOPSession} if defined?(APOPSession)# APOPSession = APOP
703
+ $-v = saved_verbosity
704
+
705
+ #
706
+ # This class represents a message which exists on the POP server.
707
+ # Instances of this class are created by the POP3 class; they should
708
+ # not be directly created by the user.
709
+ #
710
+ class POPMail
711
+
712
+ def initialize(num, len, pop, cmd) #:nodoc:
713
+ @number = num
714
+ @length = len
715
+ @pop = pop
716
+ @command = cmd
717
+ @deleted = false
718
+ @uid = nil
719
+ end
720
+
721
+ # The sequence number of the message on the server.
722
+ attr_reader :number
723
+
724
+ # The length of the message in octets.
725
+ attr_reader :length
726
+ alias size length
727
+
728
+ # Provide human-readable stringification of class state.
729
+ def inspect
730
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
731
+ end
732
+
733
+ #
734
+ # This method fetches the message. If called with a block, the
735
+ # message is yielded to the block one chunk at a time. If called
736
+ # without a block, the message is returned as a String. The optional
737
+ # +dest+ argument will be prepended to the returned String; this
738
+ # argument is essentially obsolete.
739
+ #
740
+ # === Example without block
741
+ #
742
+ # POP3.start('pop.example.com', 110,
743
+ # 'YourAccount, 'YourPassword') do |pop|
744
+ # n = 1
745
+ # pop.mails.each do |popmail|
746
+ # File.open("inbox/#{n}", 'w') do |f|
747
+ # f.write popmail.pop
748
+ # end
749
+ # popmail.delete
750
+ # n += 1
751
+ # end
752
+ # end
753
+ #
754
+ # === Example with block
755
+ #
756
+ # POP3.start('pop.example.com', 110,
757
+ # 'YourAccount, 'YourPassword') do |pop|
758
+ # n = 1
759
+ # pop.mails.each do |popmail|
760
+ # File.open("inbox/#{n}", 'w') do |f|
761
+ # popmail.pop do |chunk| ####
762
+ # f.write chunk
763
+ # end
764
+ # end
765
+ # n += 1
766
+ # end
767
+ # end
768
+ #
769
+ # This method raises a POPError if an error occurs.
770
+ #
771
+ def pop( dest = '', &block ) # :yield: message_chunk
772
+ if block_given?
773
+ @command.retr(@number, &block)
774
+ nil
775
+ else
776
+ @command.retr(@number) do |chunk|
777
+ dest << chunk
778
+ end
779
+ dest
780
+ end
781
+ end
782
+
783
+ alias all pop #:nodoc: obsolete
784
+ alias mail pop #:nodoc: obsolete
785
+
786
+ # Fetches the message header and +lines+ lines of body.
787
+ #
788
+ # The optional +dest+ argument is obsolete.
789
+ #
790
+ # This method raises a POPError if an error occurs.
791
+ def top(lines, dest = '')
792
+ @command.top(@number, lines) do |chunk|
793
+ dest << chunk
794
+ end
795
+ dest
796
+ end
797
+
798
+ # Fetches the message header.
799
+ #
800
+ # The optional +dest+ argument is obsolete.
801
+ #
802
+ # This method raises a POPError if an error occurs.
803
+ def header(dest = '')
804
+ top(0, dest)
805
+ end
806
+
807
+ # Marks a message for deletion on the server. Deletion does not
808
+ # actually occur until the end of the session; deletion may be
809
+ # cancelled for _all_ marked messages by calling POP3#reset().
810
+ #
811
+ # This method raises a POPError if an error occurs.
812
+ #
813
+ # === Example
814
+ #
815
+ # POP3.start('pop.example.com', 110,
816
+ # 'YourAccount, 'YourPassword') do |pop|
817
+ # n = 1
818
+ # pop.mails.each do |popmail|
819
+ # File.open("inbox/#{n}", 'w') do |f|
820
+ # f.write popmail.pop
821
+ # end
822
+ # popmail.delete ####
823
+ # n += 1
824
+ # end
825
+ # end
826
+ #
827
+ def delete
828
+ @command.dele @number
829
+ @deleted = true
830
+ end
831
+
832
+ alias delete! delete #:nodoc: obsolete
833
+
834
+ # True if the mail has been deleted.
835
+ def deleted?
836
+ @deleted
837
+ end
838
+
839
+ # Returns the unique-id of the message.
840
+ # Normally the unique-id is a hash string of the message.
841
+ #
842
+ # This method raises a POPError if an error occurs.
843
+ def unique_id
844
+ return @uid if @uid
845
+ @pop.set_all_uids
846
+ @uid
847
+ end
848
+
849
+ alias uidl unique_id
850
+
851
+ def uid=(uid) #:nodoc: internal use only
852
+ @uid = uid
853
+ end
854
+
855
+ end # class POPMail
856
+
857
+
858
+ class POP3Command #:nodoc: internal use only
859
+
860
+ def initialize(sock)
861
+ @socket = sock
862
+ @error_occured = false
863
+ res = check_response(critical { recv_response() })
864
+ @apop_stamp = res.slice(/<.+>/)
865
+ end
866
+
867
+ def inspect
868
+ "#<#{self.class} socket=#{@socket}>"
869
+ end
870
+
871
+ def auth(account, password)
872
+ check_response_auth(critical {
873
+ check_response_auth(get_response('USER %s', account))
874
+ get_response('PASS %s', password)
875
+ })
876
+ end
877
+
878
+ def apop(account, password)
879
+ raise POPAuthenticationError, 'not APOP server; cannot login' \
880
+ unless @apop_stamp
881
+ check_response_auth(critical {
882
+ get_response('APOP %s %s',
883
+ account,
884
+ Digest::MD5.hexdigest(@apop_stamp + password))
885
+ })
886
+ end
887
+
888
+ def list
889
+ critical {
890
+ getok 'LIST'
891
+ list = []
892
+ @socket.each_list_item do |line|
893
+ m = /\A(\d+)[ \t]+(\d+)/.match(line) or
894
+ raise POPBadResponse, "bad response: #{line}"
895
+ list.push [m[1].to_i, m[2].to_i]
896
+ end
897
+ return list
898
+ }
899
+ end
900
+
901
+ def stat
902
+ res = check_response(critical { get_response('STAT') })
903
+ m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
904
+ raise POPBadResponse, "wrong response format: #{res}"
905
+ [m[1].to_i, m[2].to_i]
906
+ end
907
+
908
+ def rset
909
+ check_response(critical { get_response('RSET') })
910
+ end
911
+
912
+ def top(num, lines = 0, &block)
913
+ critical {
914
+ getok('TOP %d %d', num, lines)
915
+ @socket.each_message_chunk(&block)
916
+ }
917
+ end
918
+
919
+ def retr(num, &block)
920
+ critical {
921
+ getok('RETR %d', num)
922
+ @socket.each_message_chunk(&block)
923
+ }
924
+ end
925
+
926
+ def dele(num)
927
+ check_response(critical { get_response('DELE %d', num) })
928
+ end
929
+
930
+ def uidl(num = nil)
931
+ if num
932
+ res = check_response(critical { get_response('UIDL %d', num) })
933
+ return res.split(/ /)[1]
934
+ else
935
+ critical {
936
+ getok('UIDL')
937
+ table = {}
938
+ @socket.each_list_item do |line|
939
+ num, uid = line.split
940
+ table[num.to_i] = uid
941
+ end
942
+ return table
943
+ }
944
+ end
945
+ end
946
+
947
+ def quit
948
+ check_response(critical { get_response('QUIT') })
949
+ end
950
+
951
+ private
952
+
953
+ def getok(fmt, *fargs)
954
+ @socket.writeline sprintf(fmt, *fargs)
955
+ check_response(recv_response())
956
+ end
957
+
958
+ def get_response(fmt, *fargs)
959
+ @socket.writeline sprintf(fmt, *fargs)
960
+ recv_response()
961
+ end
962
+
963
+ def recv_response
964
+ @socket.readline
965
+ end
966
+
967
+ def check_response(res)
968
+ raise POPError, res unless /\A\+OK/i =~ res
969
+ res
970
+ end
971
+
972
+ def check_response_auth(res)
973
+ raise POPAuthenticationError, res unless /\A\+OK/i =~ res
974
+ res
975
+ end
976
+
977
+ def critical
978
+ return '+OK dummy ok response' if @error_occured
979
+ begin
980
+ return yield()
981
+ rescue Exception
982
+ @error_occured = true
983
+ raise
984
+ end
985
+ end
986
+
987
+ end # class POP3Command
988
+
989
+ end # module Net