tlsmail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -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
@@ -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
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
@@ -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
@@ -0,0 +1,988 @@
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
+ 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
+ # class aliases
687
+ POP = POP3
688
+ POPSession = POP3
689
+ POP3Session = POP3
690
+
691
+ #
692
+ # This class is equivalent to POP3, except that it uses APOP authentication.
693
+ #
694
+ class APOP < POP3
695
+ # Always returns true.
696
+ def apop?
697
+ true
698
+ end
699
+ end
700
+
701
+ # class aliases
702
+ APOPSession = APOP
703
+
704
+ #
705
+ # This class represents a message which exists on the POP server.
706
+ # Instances of this class are created by the POP3 class; they should
707
+ # not be directly created by the user.
708
+ #
709
+ class POPMail
710
+
711
+ def initialize(num, len, pop, cmd) #:nodoc:
712
+ @number = num
713
+ @length = len
714
+ @pop = pop
715
+ @command = cmd
716
+ @deleted = false
717
+ @uid = nil
718
+ end
719
+
720
+ # The sequence number of the message on the server.
721
+ attr_reader :number
722
+
723
+ # The length of the message in octets.
724
+ attr_reader :length
725
+ alias size length
726
+
727
+ # Provide human-readable stringification of class state.
728
+ def inspect
729
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
730
+ end
731
+
732
+ #
733
+ # This method fetches the message. If called with a block, the
734
+ # message is yielded to the block one chunk at a time. If called
735
+ # without a block, the message is returned as a String. The optional
736
+ # +dest+ argument will be prepended to the returned String; this
737
+ # argument is essentially obsolete.
738
+ #
739
+ # === Example without block
740
+ #
741
+ # POP3.start('pop.example.com', 110,
742
+ # 'YourAccount, 'YourPassword') do |pop|
743
+ # n = 1
744
+ # pop.mails.each do |popmail|
745
+ # File.open("inbox/#{n}", 'w') do |f|
746
+ # f.write popmail.pop
747
+ # end
748
+ # popmail.delete
749
+ # n += 1
750
+ # end
751
+ # end
752
+ #
753
+ # === Example with block
754
+ #
755
+ # POP3.start('pop.example.com', 110,
756
+ # 'YourAccount, 'YourPassword') do |pop|
757
+ # n = 1
758
+ # pop.mails.each do |popmail|
759
+ # File.open("inbox/#{n}", 'w') do |f|
760
+ # popmail.pop do |chunk| ####
761
+ # f.write chunk
762
+ # end
763
+ # end
764
+ # n += 1
765
+ # end
766
+ # end
767
+ #
768
+ # This method raises a POPError if an error occurs.
769
+ #
770
+ def pop( dest = '', &block ) # :yield: message_chunk
771
+ if block_given?
772
+ @command.retr(@number, &block)
773
+ nil
774
+ else
775
+ @command.retr(@number) do |chunk|
776
+ dest << chunk
777
+ end
778
+ dest
779
+ end
780
+ end
781
+
782
+ alias all pop #:nodoc: obsolete
783
+ alias mail pop #:nodoc: obsolete
784
+
785
+ # Fetches the message header and +lines+ lines of body.
786
+ #
787
+ # The optional +dest+ argument is obsolete.
788
+ #
789
+ # This method raises a POPError if an error occurs.
790
+ def top(lines, dest = '')
791
+ @command.top(@number, lines) do |chunk|
792
+ dest << chunk
793
+ end
794
+ dest
795
+ end
796
+
797
+ # Fetches the message header.
798
+ #
799
+ # The optional +dest+ argument is obsolete.
800
+ #
801
+ # This method raises a POPError if an error occurs.
802
+ def header(dest = '')
803
+ top(0, dest)
804
+ end
805
+
806
+ # Marks a message for deletion on the server. Deletion does not
807
+ # actually occur until the end of the session; deletion may be
808
+ # cancelled for _all_ marked messages by calling POP3#reset().
809
+ #
810
+ # This method raises a POPError if an error occurs.
811
+ #
812
+ # === Example
813
+ #
814
+ # POP3.start('pop.example.com', 110,
815
+ # 'YourAccount, 'YourPassword') do |pop|
816
+ # n = 1
817
+ # pop.mails.each do |popmail|
818
+ # File.open("inbox/#{n}", 'w') do |f|
819
+ # f.write popmail.pop
820
+ # end
821
+ # popmail.delete ####
822
+ # n += 1
823
+ # end
824
+ # end
825
+ #
826
+ def delete
827
+ @command.dele @number
828
+ @deleted = true
829
+ end
830
+
831
+ alias delete! delete #:nodoc: obsolete
832
+
833
+ # True if the mail has been deleted.
834
+ def deleted?
835
+ @deleted
836
+ end
837
+
838
+ # Returns the unique-id of the message.
839
+ # Normally the unique-id is a hash string of the message.
840
+ #
841
+ # This method raises a POPError if an error occurs.
842
+ def unique_id
843
+ return @uid if @uid
844
+ @pop.set_all_uids
845
+ @uid
846
+ end
847
+
848
+ alias uidl unique_id
849
+
850
+ def uid=(uid) #:nodoc: internal use only
851
+ @uid = uid
852
+ end
853
+
854
+ end # class POPMail
855
+
856
+
857
+ class POP3Command #:nodoc: internal use only
858
+
859
+ def initialize(sock)
860
+ @socket = sock
861
+ @error_occured = false
862
+ res = check_response(critical { recv_response() })
863
+ @apop_stamp = res.slice(/<.+>/)
864
+ end
865
+
866
+ def inspect
867
+ "#<#{self.class} socket=#{@socket}>"
868
+ end
869
+
870
+ def auth(account, password)
871
+ check_response_auth(critical {
872
+ check_response_auth(get_response('USER %s', account))
873
+ get_response('PASS %s', password)
874
+ })
875
+ end
876
+
877
+ def apop(account, password)
878
+ raise POPAuthenticationError, 'not APOP server; cannot login' \
879
+ unless @apop_stamp
880
+ check_response_auth(critical {
881
+ get_response('APOP %s %s',
882
+ account,
883
+ Digest::MD5.hexdigest(@apop_stamp + password))
884
+ })
885
+ end
886
+
887
+ def list
888
+ critical {
889
+ getok 'LIST'
890
+ list = []
891
+ @socket.each_list_item do |line|
892
+ m = /\A(\d+)[ \t]+(\d+)/.match(line) or
893
+ raise POPBadResponse, "bad response: #{line}"
894
+ list.push [m[1].to_i, m[2].to_i]
895
+ end
896
+ return list
897
+ }
898
+ end
899
+
900
+ def stat
901
+ res = check_response(critical { get_response('STAT') })
902
+ m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
903
+ raise POPBadResponse, "wrong response format: #{res}"
904
+ [m[1].to_i, m[2].to_i]
905
+ end
906
+
907
+ def rset
908
+ check_response(critical { get_response('RSET') })
909
+ end
910
+
911
+ def top(num, lines = 0, &block)
912
+ critical {
913
+ getok('TOP %d %d', num, lines)
914
+ @socket.each_message_chunk(&block)
915
+ }
916
+ end
917
+
918
+ def retr(num, &block)
919
+ critical {
920
+ getok('RETR %d', num)
921
+ @socket.each_message_chunk(&block)
922
+ }
923
+ end
924
+
925
+ def dele(num)
926
+ check_response(critical { get_response('DELE %d', num) })
927
+ end
928
+
929
+ def uidl(num = nil)
930
+ if num
931
+ res = check_response(critical { get_response('UIDL %d', num) })
932
+ return res.split(/ /)[1]
933
+ else
934
+ critical {
935
+ getok('UIDL')
936
+ table = {}
937
+ @socket.each_list_item do |line|
938
+ num, uid = line.split
939
+ table[num.to_i] = uid
940
+ end
941
+ return table
942
+ }
943
+ end
944
+ end
945
+
946
+ def quit
947
+ check_response(critical { get_response('QUIT') })
948
+ end
949
+
950
+ private
951
+
952
+ def getok(fmt, *fargs)
953
+ @socket.writeline sprintf(fmt, *fargs)
954
+ check_response(recv_response())
955
+ end
956
+
957
+ def get_response(fmt, *fargs)
958
+ @socket.writeline sprintf(fmt, *fargs)
959
+ recv_response()
960
+ end
961
+
962
+ def recv_response
963
+ @socket.readline
964
+ end
965
+
966
+ def check_response(res)
967
+ raise POPError, res unless /\A\+OK/i =~ res
968
+ res
969
+ end
970
+
971
+ def check_response_auth(res)
972
+ raise POPAuthenticationError, res unless /\A\+OK/i =~ res
973
+ res
974
+ end
975
+
976
+ def critical
977
+ return '+OK dummy ok response' if @error_occured
978
+ begin
979
+ return yield()
980
+ rescue Exception
981
+ @error_occured = true
982
+ raise
983
+ end
984
+ end
985
+
986
+ end # class POP3Command
987
+
988
+ end # module Net