kelredd-mailer 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  == Description
4
4
 
5
- This is just a little gem to let you configure and send email using TMail over Net:SMTP.
5
+ This is just a little gem to let you configure and send email using TMail over Net:SMTP. It also lets you check email using Net::POP3.
6
6
 
7
7
  == Installation
8
8
 
@@ -10,7 +10,7 @@ This is just a little gem to let you configure and send email using TMail over N
10
10
 
11
11
  == Usage Examples
12
12
 
13
- === A simple configuration
13
+ === Sending mail: A simple configuration
14
14
 
15
15
  require 'mailer'
16
16
 
@@ -28,7 +28,7 @@ This is just a little gem to let you configure and send email using TMail over N
28
28
  end
29
29
 
30
30
 
31
- === A more complex configuration
31
+ === Sending mail: A more complex configuration
32
32
 
33
33
  # Note: this is showing a more complex configuration
34
34
  # only setup or configure what you need or want to
@@ -63,19 +63,68 @@ This is just a little gem to let you configure and send email using TMail over N
63
63
  # not required, don't pass .send a block if you don't want your mail to have a body
64
64
  "a message body"
65
65
  end
66
+
67
+ === Checking mail
68
+
69
+ mailbox = Mailer::Mailbox.new({
70
+ :server => "pop.server", # set to whatever needed
71
+ :port => 995, # set to whatever needed
72
+ :email => "test@example.com", # the inbox to check
73
+ :password => "secret" # the email account pop password
74
+ })
75
+
76
+ # check if you have mail
77
+ mailbox.empty?
78
+
79
+ # open the mailbox, getting a Net:POP3 object
80
+ mailbox.open do |box|
81
+
82
+ # box is a Net::POP3 obj
83
+ box.each_mail do |mail|
84
+ puts mail.header
85
+ end
86
+
87
+ end
88
+
89
+ # check any email in mailbox, getting a collection of Net::POPMail objects
90
+ mailbox.check do |email|
91
+
92
+ # email is a collection of Net::POPMail objects
93
+ puts email.length
94
+
95
+ end
96
+
97
+ # download said email to files
98
+ mailbox.check do |email|
99
+
100
+ # nothing special here, just using Net::POP3 patterns for handling mail
101
+ pop.mails.each_with_index do |mail, index|
102
+ File.open( "inbox/#{index}", 'w+' ) do |file|
103
+ f.write mail.pop
104
+ end
105
+ mail.delete
106
+ end
107
+
108
+ end
109
+
110
+ # empty the mailbox
111
+ mailbox.empty!
66
112
 
67
113
  == Testing
114
+
68
115
  Mailer has some helpers and Shoulda macros to ease testing that emails were delivered with the correct parameters, fields, and content.
69
116
 
70
117
  Note: This testing only tests that mail objects were built successfully and passed all checks for delivery. This does not actually send the mails or test sending the mails at the Net::SMTP level.
71
118
 
72
119
  === Helpers
120
+
73
121
  In test_helper.rb or wherever:
74
122
 
75
123
  require 'mailer/test_helpers'
76
124
  include Mailer::TestHelpers
77
125
 
78
126
  === Shoulda Macros
127
+
79
128
  In test_helper.rb or wherever:
80
129
 
81
130
  require 'mailer/shoulda_macros/test_unit'
@@ -86,6 +135,8 @@ It's only live testing and known to be working with SMTP servers requiring TLS (
86
135
 
87
136
  Right now, the Mailer can only have one configuration. Maybe like to extend it to create instances of Mailers with different configurations?
88
137
 
138
+ I want to add helpers for downloading email with attachments and storing in memory as a string and file streams. Maybe?
139
+
89
140
  == License
90
141
 
91
142
  Copyright (c) 2009 Kelly Redding
data/lib/mailer/config.rb CHANGED
@@ -28,7 +28,14 @@ module Mailer
28
28
  end
29
29
 
30
30
  def log_file=(file)
31
- @logger.add(Log4r::FileOutputter.new('fileOutputter', :filename => file, :trunc => false, :formatter => Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %m"))) #rescue nil
31
+ begin
32
+ @logger.add(Log4r::FileOutputter.new('fileOutputter', {
33
+ :filename => file,
34
+ :trunc => false,
35
+ :formatter => Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %m")
36
+ }))
37
+ rescue Exception => err
38
+ end
32
39
  end
33
40
 
34
41
  def check
@@ -0,0 +1,74 @@
1
+ require 'mailer/exceptions'
2
+ require 'mailer/pop_ssl'
3
+
4
+ module Mailer
5
+
6
+ class Mailbox
7
+
8
+ CONFIGS = [:server, :port, :email, :password]
9
+ CONFIGS.each do |config|
10
+ attr_reader config
11
+ end
12
+ attr_reader :logger, :log_file
13
+
14
+ def initialize(configs={})
15
+ @logger = Log4r::Logger.new("[mailer]")
16
+ @logger.add(Log4r::StdoutOutputter.new('console'))
17
+
18
+ configs.each do |config, value|
19
+ instance_variable_set("@#{config.to_s}", value)
20
+ end
21
+
22
+ if @log_file
23
+ begin
24
+ @logger.add(Log4r::FileOutputter.new('fileOutputter', {
25
+ :filename => @log_file,
26
+ :trunc => false,
27
+ :formatter => Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %m")
28
+ }))
29
+ rescue Exception => err
30
+ end
31
+ end
32
+
33
+ Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
34
+
35
+ configs
36
+ end
37
+
38
+ # opens the mailbox returning the pop3 mailbox object
39
+ def open
40
+ start_pop do |box|
41
+ yield box
42
+ end
43
+ end
44
+
45
+ # opens the mail box and returns the mails as a collection
46
+ def check
47
+ start_pop do |box|
48
+ if block_given?
49
+ yield box.mails
50
+ end
51
+ end
52
+ end
53
+
54
+ def empty?
55
+ start_pop do |box|
56
+ box.mails.empty?
57
+ end
58
+ end
59
+
60
+ def empty!
61
+ start_pop do |box|
62
+ box.mails.each {|mail| mail.delete!}
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def start_pop(&block)
69
+ Net::POP3.start(@server, @port, @email, @password, &block)
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,994 @@
1
+ # This file was downloaded from: http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v1_9_0_0/lib/net/pop.rb?revision=13778&view=markup
2
+ # This post led me to it: http://www.spacebabies.nl/2009/01/15/using-gmail-with-ruby-pop3/
3
+
4
+ # = net/pop.rb
5
+ #
6
+ # Copyright (c) 1999-2007 Yukihiro Matsumoto.
7
+ #
8
+ # Copyright (c) 1999-2007 Minero Aoki.
9
+ #
10
+ # Written & maintained by Minero Aoki <aamine@loveruby.net>.
11
+ #
12
+ # Documented by William Webber and Minero Aoki.
13
+ #
14
+ # This program is free software. You can re-distribute and/or
15
+ # modify this program under the same terms as Ruby itself,
16
+ # Ruby Distribute License.
17
+ #
18
+ # NOTE: You can find Japanese version of this document at:
19
+ # http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=net%2Fpop.rb
20
+ #
21
+ # $Id$
22
+ #
23
+ # See Net::POP3 for documentation.
24
+ #
25
+
26
+ require 'net/protocol'
27
+ require 'digest/md5'
28
+ require 'timeout'
29
+
30
+ begin
31
+ require "openssl"
32
+ rescue LoadError
33
+ end
34
+
35
+ module Net
36
+
37
+ # Non-authentication POP3 protocol error
38
+ # (reply code "-ERR", except authentication).
39
+ class POPError < ProtocolError; end
40
+
41
+ # POP3 authentication error.
42
+ class POPAuthenticationError < ProtoAuthError; end
43
+
44
+ # Unexpected response from the server.
45
+ class POPBadResponse < POPError; end
46
+
47
+ #
48
+ # = Net::POP3
49
+ #
50
+ # == What is This Library?
51
+ #
52
+ # This library provides functionality for retrieving
53
+ # email via POP3, the Post Office Protocol version 3. For details
54
+ # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt).
55
+ #
56
+ # == Examples
57
+ #
58
+ # === Retrieving Messages
59
+ #
60
+ # This example retrieves messages from the server and deletes them
61
+ # on the server.
62
+ #
63
+ # Messages are written to files named 'inbox/1', 'inbox/2', ....
64
+ # Replace 'pop.example.com' with your POP3 server address, and
65
+ # 'YourAccount' and 'YourPassword' with the appropriate account
66
+ # details.
67
+ #
68
+ # require 'net/pop'
69
+ #
70
+ # pop = Net::POP3.new('pop.example.com')
71
+ # pop.start('YourAccount', 'YourPassword') # (1)
72
+ # if pop.mails.empty?
73
+ # puts 'No mail.'
74
+ # else
75
+ # i = 0
76
+ # pop.each_mail do |m| # or "pop.mails.each ..." # (2)
77
+ # File.open("inbox/#{i}", 'w') do |f|
78
+ # f.write m.pop
79
+ # end
80
+ # m.delete
81
+ # i += 1
82
+ # end
83
+ # puts "#{pop.mails.size} mails popped."
84
+ # end
85
+ # pop.finish # (3)
86
+ #
87
+ # 1. Call Net::POP3#start and start POP session.
88
+ # 2. Access messages by using POP3#each_mail and/or POP3#mails.
89
+ # 3. Close POP session by calling POP3#finish or use the block form of #start.
90
+ #
91
+ # === Shortened Code
92
+ #
93
+ # The example above is very verbose. You can shorten the code by using
94
+ # some utility methods. First, the block form of Net::POP3.start can
95
+ # be used instead of POP3.new, POP3#start and POP3#finish.
96
+ #
97
+ # require 'net/pop'
98
+ #
99
+ # Net::POP3.start('pop.example.com', 110,
100
+ # 'YourAccount', 'YourPassword') do |pop|
101
+ # if pop.mails.empty?
102
+ # puts 'No mail.'
103
+ # else
104
+ # i = 0
105
+ # pop.each_mail do |m| # or "pop.mails.each ..."
106
+ # File.open("inbox/#{i}", 'w') do |f|
107
+ # f.write m.pop
108
+ # end
109
+ # m.delete
110
+ # i += 1
111
+ # end
112
+ # puts "#{pop.mails.size} mails popped."
113
+ # end
114
+ # end
115
+ #
116
+ # POP3#delete_all is an alternative for #each_mail and #delete.
117
+ #
118
+ # require 'net/pop'
119
+ #
120
+ # Net::POP3.start('pop.example.com', 110,
121
+ # 'YourAccount', 'YourPassword') do |pop|
122
+ # if pop.mails.empty?
123
+ # puts 'No mail.'
124
+ # else
125
+ # i = 1
126
+ # pop.delete_all do |m|
127
+ # File.open("inbox/#{i}", 'w') do |f|
128
+ # f.write m.pop
129
+ # end
130
+ # i += 1
131
+ # end
132
+ # end
133
+ # end
134
+ #
135
+ # And here is an even shorter example.
136
+ #
137
+ # require 'net/pop'
138
+ #
139
+ # i = 0
140
+ # Net::POP3.delete_all('pop.example.com', 110,
141
+ # 'YourAccount', 'YourPassword') do |m|
142
+ # File.open("inbox/#{i}", 'w') do |f|
143
+ # f.write m.pop
144
+ # end
145
+ # i += 1
146
+ # end
147
+ #
148
+ # === Memory Space Issues
149
+ #
150
+ # All the examples above get each message as one big string.
151
+ # This example avoids this.
152
+ #
153
+ # require 'net/pop'
154
+ #
155
+ # i = 1
156
+ # Net::POP3.delete_all('pop.example.com', 110,
157
+ # 'YourAccount', 'YourPassword') do |m|
158
+ # File.open("inbox/#{i}", 'w') do |f|
159
+ # m.pop do |chunk| # get a message little by little.
160
+ # f.write chunk
161
+ # end
162
+ # i += 1
163
+ # end
164
+ # end
165
+ #
166
+ # === Using APOP
167
+ #
168
+ # The net/pop library supports APOP authentication.
169
+ # To use APOP, use the Net::APOP class instead of the Net::POP3 class.
170
+ # You can use the utility method, Net::POP3.APOP(). For example:
171
+ #
172
+ # require 'net/pop'
173
+ #
174
+ # # Use APOP authentication if $isapop == true
175
+ # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
176
+ # pop.start(YourAccount', 'YourPassword') do |pop|
177
+ # # Rest of the code is the same.
178
+ # end
179
+ #
180
+ # === Fetch Only Selected Mail Using 'UIDL' POP Command
181
+ #
182
+ # If your POP server provides UIDL functionality,
183
+ # you can grab only selected mails from the POP server.
184
+ # e.g.
185
+ #
186
+ # def need_pop?( id )
187
+ # # determine if we need pop this mail...
188
+ # end
189
+ #
190
+ # Net::POP3.start('pop.example.com', 110,
191
+ # 'Your account', 'Your password') do |pop|
192
+ # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m|
193
+ # do_something(m.pop)
194
+ # end
195
+ # end
196
+ #
197
+ # The POPMail#unique_id() method returns the unique-id of the message as a
198
+ # String. Normally the unique-id is a hash of the message.
199
+ #
200
+ class POP3 < Protocol
201
+
202
+ Revision = %q$Revision$.split[1]
203
+
204
+ #
205
+ # Class Parameters
206
+ #
207
+
208
+ def POP3.default_port
209
+ default_pop3_port()
210
+ end
211
+
212
+ # The default port for POP3 connections, port 110
213
+ def POP3.default_pop3_port
214
+ 110
215
+ end
216
+
217
+ # The default port for POP3S connections, port 995
218
+ def POP3.default_pop3s_port
219
+ 995
220
+ end
221
+
222
+ def POP3.socket_type #:nodoc: obsolete
223
+ Net::InternetMessageIO
224
+ end
225
+
226
+ #
227
+ # Utilities
228
+ #
229
+
230
+ # Returns the APOP class if +isapop+ is true; otherwise, returns
231
+ # the POP class. For example:
232
+ #
233
+ # # Example 1
234
+ # pop = Net::POP3::APOP($is_apop).new(addr, port)
235
+ #
236
+ # # Example 2
237
+ # Net::POP3::APOP($is_apop).start(addr, port) do |pop|
238
+ # ....
239
+ # end
240
+ #
241
+ def POP3.APOP(isapop)
242
+ isapop ? APOP : POP3
243
+ end
244
+
245
+ # Starts a POP3 session and iterates over each POPMail object,
246
+ # yielding it to the +block+.
247
+ # This method is equivalent to:
248
+ #
249
+ # Net::POP3.start(address, port, account, password) do |pop|
250
+ # pop.each_mail do |m|
251
+ # yield m
252
+ # end
253
+ # end
254
+ #
255
+ # This method raises a POPAuthenticationError if authentication fails.
256
+ #
257
+ # === Example
258
+ #
259
+ # Net::POP3.foreach('pop.example.com', 110,
260
+ # 'YourAccount', 'YourPassword') do |m|
261
+ # file.write m.pop
262
+ # m.delete if $DELETE
263
+ # end
264
+ #
265
+ def POP3.foreach(address, port = nil,
266
+ account = nil, password = nil,
267
+ isapop = false, &block) # :yields: message
268
+ start(address, port, account, password, isapop) {|pop|
269
+ pop.each_mail(&block)
270
+ }
271
+ end
272
+
273
+ # Starts a POP3 session and deletes all messages on the server.
274
+ # If a block is given, each POPMail object is yielded to it before
275
+ # being deleted.
276
+ #
277
+ # This method raises a POPAuthenticationError if authentication fails.
278
+ #
279
+ # === Example
280
+ #
281
+ # Net::POP3.delete_all('pop.example.com', 110,
282
+ # 'YourAccount', 'YourPassword') do |m|
283
+ # file.write m.pop
284
+ # end
285
+ #
286
+ def POP3.delete_all(address, port = nil,
287
+ account = nil, password = nil,
288
+ isapop = false, &block)
289
+ start(address, port, account, password, isapop) {|pop|
290
+ pop.delete_all(&block)
291
+ }
292
+ end
293
+
294
+ # Opens a POP3 session, attempts authentication, and quits.
295
+ #
296
+ # This method raises POPAuthenticationError if authentication fails.
297
+ #
298
+ # === Example: normal POP3
299
+ #
300
+ # Net::POP3.auth_only('pop.example.com', 110,
301
+ # 'YourAccount', 'YourPassword')
302
+ #
303
+ # === Example: APOP
304
+ #
305
+ # Net::POP3.auth_only('pop.example.com', 110,
306
+ # 'YourAccount', 'YourPassword', true)
307
+ #
308
+ def POP3.auth_only(address, port = nil,
309
+ account = nil, password = nil,
310
+ isapop = false)
311
+ new(address, port, isapop).auth_only account, password
312
+ end
313
+
314
+ # Starts a pop3 session, attempts authentication, and quits.
315
+ # This method must not be called while POP3 session is opened.
316
+ # This method raises POPAuthenticationError if authentication fails.
317
+ def auth_only(account, password)
318
+ raise IOError, 'opening previously opened POP session' if started?
319
+ start(account, password) {
320
+ ;
321
+ }
322
+ end
323
+
324
+ #
325
+ # SSL
326
+ #
327
+
328
+ @use_ssl = false
329
+ @verify = nil
330
+ @certs = nil
331
+
332
+ # Enable SSL for all new instances.
333
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
334
+ # to OpenSSL::SSL::VERIFY_NONE.
335
+ # +certs+ is a file or directory holding CA certs to use to verify the
336
+ # server cert; Defaults to nil.
337
+ def POP3.enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE, certs = nil)
338
+ @use_ssl = true
339
+ @verify = verify
340
+ @certs = certs
341
+ end
342
+
343
+ # Disable SSL for all new instances.
344
+ def POP3.disable_ssl
345
+ @use_ssl = nil
346
+ @verify = nil
347
+ @certs = nil
348
+ end
349
+
350
+ def POP3.use_ssl?
351
+ @use_ssl
352
+ end
353
+
354
+ def POP3.verify
355
+ @verify
356
+ end
357
+
358
+ def POP3.certs
359
+ @certs
360
+ end
361
+
362
+ #
363
+ # Session management
364
+ #
365
+
366
+ # Creates a new POP3 object and open the connection. Equivalent to
367
+ #
368
+ # Net::POP3.new(address, port, isapop).start(account, password)
369
+ #
370
+ # If +block+ is provided, yields the newly-opened POP3 object to it,
371
+ # and automatically closes it at the end of the session.
372
+ #
373
+ # === Example
374
+ #
375
+ # Net::POP3.start(addr, port, account, password) do |pop|
376
+ # pop.each_mail do |m|
377
+ # file.write m.pop
378
+ # m.delete
379
+ # end
380
+ # end
381
+ #
382
+ def POP3.start(address, port = nil,
383
+ account = nil, password = nil,
384
+ isapop = false, &block) # :yield: pop
385
+ new(address, port, isapop).start(account, password, &block)
386
+ end
387
+
388
+ # Creates a new POP3 object.
389
+ #
390
+ # +address+ is the hostname or ip address of your POP3 server.
391
+ #
392
+ # The optional +port+ is the port to connect to.
393
+ #
394
+ # The optional +isapop+ specifies whether this connection is going
395
+ # to use APOP authentication; it defaults to +false+.
396
+ #
397
+ # This method does *not* open the TCP connection.
398
+ def initialize(addr, port = nil, isapop = false)
399
+ @address = addr
400
+ @use_ssl = POP3.use_ssl?
401
+ @port = port || (POP3.use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port)
402
+ @apop = isapop
403
+ @certs = POP3.certs
404
+ @verify = POP3.verify
405
+
406
+ @command = nil
407
+ @socket = nil
408
+ @started = false
409
+ @open_timeout = 30
410
+ @read_timeout = 60
411
+ @debug_output = nil
412
+
413
+ @mails = nil
414
+ @n_mails = nil
415
+ @n_bytes = nil
416
+ end
417
+
418
+ # Does this instance use APOP authentication?
419
+ def apop?
420
+ @apop
421
+ end
422
+
423
+ # does this instance use SSL?
424
+ def use_ssl?
425
+ @use_ssl
426
+ end
427
+
428
+ # Enables SSL for this instance. Must be called before the connection is
429
+ # established to have any effect.
430
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
431
+ # to OpenSSL::SSL::VERIFY_NONE.
432
+ # +certs+ is a file or directory holding CA certs to use to verify the
433
+ # server cert; Defaults to nil.
434
+ # +port+ is port to establish the SSL connection on; Defaults to 995.
435
+ def enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE, certs = nil,
436
+ port = POP3.default_pop3s_port)
437
+ @use_ssl = true
438
+ @verify = verify
439
+ @certs = certs
440
+ @port = port
441
+ end
442
+
443
+ def disable_ssl
444
+ @use_ssl = false
445
+ @verify = nil
446
+ @certs = nil
447
+ end
448
+
449
+ # Provide human-readable stringification of class state.
450
+ def inspect
451
+ "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
452
+ end
453
+
454
+ # *WARNING*: This method causes a serious security hole.
455
+ # Use this method only for debugging.
456
+ #
457
+ # Set an output stream for debugging.
458
+ #
459
+ # === Example
460
+ #
461
+ # pop = Net::POP.new(addr, port)
462
+ # pop.set_debug_output $stderr
463
+ # pop.start(account, passwd) do |pop|
464
+ # ....
465
+ # end
466
+ #
467
+ def set_debug_output(arg)
468
+ @debug_output = arg
469
+ end
470
+
471
+ # The address to connect to.
472
+ attr_reader :address
473
+
474
+ # The port number to connect to.
475
+ attr_reader :port
476
+
477
+ # Seconds to wait until a connection is opened.
478
+ # If the POP3 object cannot open a connection within this time,
479
+ # it raises a TimeoutError exception.
480
+ attr_accessor :open_timeout
481
+
482
+ # Seconds to wait until reading one block (by one read(1) call).
483
+ # If the POP3 object cannot complete a read() within this time,
484
+ # it raises a TimeoutError exception.
485
+ attr_reader :read_timeout
486
+
487
+ # Set the read timeout.
488
+ def read_timeout=(sec)
489
+ @command.socket.read_timeout = sec if @command
490
+ @read_timeout = sec
491
+ end
492
+
493
+ # +true+ if the POP3 session has started.
494
+ def started?
495
+ @started
496
+ end
497
+
498
+ alias active? started? #:nodoc: obsolete
499
+
500
+ # Starts a POP3 session.
501
+ #
502
+ # When called with block, gives a POP3 object to the block and
503
+ # closes the session after block call finishes.
504
+ #
505
+ # This method raises a POPAuthenticationError if authentication fails.
506
+ def start(account, password) # :yield: pop
507
+ raise IOError, 'POP session already started' if @started
508
+ if block_given?
509
+ begin
510
+ do_start account, password
511
+ return yield(self)
512
+ ensure
513
+ do_finish
514
+ end
515
+ else
516
+ do_start account, password
517
+ return self
518
+ end
519
+ end
520
+
521
+ def do_start(account, password)
522
+ s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
523
+ if use_ssl?
524
+ raise 'openssl library not installed' unless defined?(OpenSSL)
525
+ context = OpenSSL::SSL::SSLContext.new
526
+ context.verify_mode = @verify
527
+ if @certs
528
+ if File.file?(@certs)
529
+ context.ca_file = @certs
530
+ elsif File.directory?(@certs)
531
+ context.ca_path = @certs
532
+ else
533
+ raise ArgumentError, "certs path is not file/directory: #{@certs}"
534
+ end
535
+ end
536
+ s = OpenSSL::SSL::SSLSocket.new(s, context)
537
+ s.sync_close = true
538
+ s.connect
539
+ if context.verify_mode != OpenSSL::SSL::VERIFY_NONE
540
+ s.post_connection_check(@address)
541
+ end
542
+ end
543
+ @socket = InternetMessageIO.new(s)
544
+ logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})"
545
+ @socket.read_timeout = @read_timeout
546
+ @socket.debug_output = @debug_output
547
+ on_connect
548
+ @command = POP3Command.new(@socket)
549
+ if apop?
550
+ @command.apop account, password
551
+ else
552
+ @command.auth account, password
553
+ end
554
+ @started = true
555
+ ensure
556
+ # Authentication failed, clean up connection.
557
+ unless @started
558
+ s.close if s and not s.closed?
559
+ @socket = nil
560
+ @command = nil
561
+ end
562
+ end
563
+ private :do_start
564
+
565
+ def on_connect
566
+ end
567
+ private :on_connect
568
+
569
+ # Finishes a POP3 session and closes TCP connection.
570
+ def finish
571
+ raise IOError, 'POP session not yet started' unless started?
572
+ do_finish
573
+ end
574
+
575
+ def do_finish
576
+ @mails = nil
577
+ @command.quit if @command
578
+ ensure
579
+ @started = false
580
+ @command = nil
581
+ @socket.close if @socket and not @socket.closed?
582
+ @socket = nil
583
+ end
584
+ private :do_finish
585
+
586
+ def command
587
+ raise IOError, 'POP session not opened yet' \
588
+ if not @socket or @socket.closed?
589
+ @command
590
+ end
591
+ private :command
592
+
593
+ #
594
+ # POP protocol wrapper
595
+ #
596
+
597
+ # Returns the number of messages on the POP server.
598
+ def n_mails
599
+ return @n_mails if @n_mails
600
+ @n_mails, @n_bytes = command().stat
601
+ @n_mails
602
+ end
603
+
604
+ # Returns the total size in bytes of all the messages on the POP server.
605
+ def n_bytes
606
+ return @n_bytes if @n_bytes
607
+ @n_mails, @n_bytes = command().stat
608
+ @n_bytes
609
+ end
610
+
611
+ # Returns an array of Net::POPMail objects, representing all the
612
+ # messages on the server. This array is renewed when the session
613
+ # restarts; otherwise, it is fetched from the server the first time
614
+ # this method is called (directly or indirectly) and cached.
615
+ #
616
+ # This method raises a POPError if an error occurs.
617
+ def mails
618
+ return @mails.dup if @mails
619
+ if n_mails() == 0
620
+ # some popd raises error for LIST on the empty mailbox.
621
+ @mails = []
622
+ return []
623
+ end
624
+
625
+ @mails = command().list.map {|num, size|
626
+ POPMail.new(num, size, self, command())
627
+ }
628
+ @mails.dup
629
+ end
630
+
631
+ # Yields each message to the passed-in block in turn.
632
+ # Equivalent to:
633
+ #
634
+ # pop3.mails.each do |popmail|
635
+ # ....
636
+ # end
637
+ #
638
+ # This method raises a POPError if an error occurs.
639
+ def each_mail(&block) # :yield: message
640
+ mails().each(&block)
641
+ end
642
+
643
+ alias each each_mail
644
+
645
+ # Deletes all messages on the server.
646
+ #
647
+ # If called with a block, yields each message in turn before deleting it.
648
+ #
649
+ # === Example
650
+ #
651
+ # n = 1
652
+ # pop.delete_all do |m|
653
+ # File.open("inbox/#{n}") do |f|
654
+ # f.write m.pop
655
+ # end
656
+ # n += 1
657
+ # end
658
+ #
659
+ # This method raises a POPError if an error occurs.
660
+ #
661
+ def delete_all # :yield: message
662
+ mails().each do |m|
663
+ yield m if block_given?
664
+ m.delete unless m.deleted?
665
+ end
666
+ end
667
+
668
+ # Resets the session. This clears all "deleted" marks from messages.
669
+ #
670
+ # This method raises a POPError if an error occurs.
671
+ def reset
672
+ command().rset
673
+ mails().each do |m|
674
+ m.instance_eval {
675
+ @deleted = false
676
+ }
677
+ end
678
+ end
679
+
680
+ def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
681
+ command().uidl.each do |num, uid|
682
+ @mails.find {|m| m.number == num }.uid = uid
683
+ end
684
+ end
685
+
686
+ def logging(msg)
687
+ @debug_output << msg + "\n" if @debug_output
688
+ end
689
+
690
+ end # class POP3
691
+
692
+ # class aliases
693
+ POP = POP3
694
+ POPSession = POP3
695
+ POP3Session = POP3
696
+
697
+ #
698
+ # This class is equivalent to POP3, except that it uses APOP authentication.
699
+ #
700
+ class APOP < POP3
701
+ # Always returns true.
702
+ def apop?
703
+ true
704
+ end
705
+ end
706
+
707
+ # class aliases
708
+ APOPSession = APOP
709
+
710
+ #
711
+ # This class represents a message which exists on the POP server.
712
+ # Instances of this class are created by the POP3 class; they should
713
+ # not be directly created by the user.
714
+ #
715
+ class POPMail
716
+
717
+ def initialize(num, len, pop, cmd) #:nodoc:
718
+ @number = num
719
+ @length = len
720
+ @pop = pop
721
+ @command = cmd
722
+ @deleted = false
723
+ @uid = nil
724
+ end
725
+
726
+ # The sequence number of the message on the server.
727
+ attr_reader :number
728
+
729
+ # The length of the message in octets.
730
+ attr_reader :length
731
+ alias size length
732
+
733
+ # Provide human-readable stringification of class state.
734
+ def inspect
735
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
736
+ end
737
+
738
+ #
739
+ # This method fetches the message. If called with a block, the
740
+ # message is yielded to the block one chunk at a time. If called
741
+ # without a block, the message is returned as a String. The optional
742
+ # +dest+ argument will be prepended to the returned String; this
743
+ # argument is essentially obsolete.
744
+ #
745
+ # === Example without block
746
+ #
747
+ # POP3.start('pop.example.com', 110,
748
+ # 'YourAccount, 'YourPassword') do |pop|
749
+ # n = 1
750
+ # pop.mails.each do |popmail|
751
+ # File.open("inbox/#{n}", 'w') do |f|
752
+ # f.write popmail.pop
753
+ # end
754
+ # popmail.delete
755
+ # n += 1
756
+ # end
757
+ # end
758
+ #
759
+ # === Example with block
760
+ #
761
+ # POP3.start('pop.example.com', 110,
762
+ # 'YourAccount, 'YourPassword') do |pop|
763
+ # n = 1
764
+ # pop.mails.each do |popmail|
765
+ # File.open("inbox/#{n}", 'w') do |f|
766
+ # popmail.pop do |chunk| ####
767
+ # f.write chunk
768
+ # end
769
+ # end
770
+ # n += 1
771
+ # end
772
+ # end
773
+ #
774
+ # This method raises a POPError if an error occurs.
775
+ #
776
+ def pop( dest = '', &block ) # :yield: message_chunk
777
+ if block_given?
778
+ @command.retr(@number, &block)
779
+ nil
780
+ else
781
+ @command.retr(@number) do |chunk|
782
+ dest << chunk
783
+ end
784
+ dest
785
+ end
786
+ end
787
+
788
+ alias all pop #:nodoc: obsolete
789
+ alias mail pop #:nodoc: obsolete
790
+
791
+ # Fetches the message header and +lines+ lines of body.
792
+ #
793
+ # The optional +dest+ argument is obsolete.
794
+ #
795
+ # This method raises a POPError if an error occurs.
796
+ def top(lines, dest = '')
797
+ @command.top(@number, lines) do |chunk|
798
+ dest << chunk
799
+ end
800
+ dest
801
+ end
802
+
803
+ # Fetches the message header.
804
+ #
805
+ # The optional +dest+ argument is obsolete.
806
+ #
807
+ # This method raises a POPError if an error occurs.
808
+ def header(dest = '')
809
+ top(0, dest)
810
+ end
811
+
812
+ # Marks a message for deletion on the server. Deletion does not
813
+ # actually occur until the end of the session; deletion may be
814
+ # cancelled for _all_ marked messages by calling POP3#reset().
815
+ #
816
+ # This method raises a POPError if an error occurs.
817
+ #
818
+ # === Example
819
+ #
820
+ # POP3.start('pop.example.com', 110,
821
+ # 'YourAccount, 'YourPassword') do |pop|
822
+ # n = 1
823
+ # pop.mails.each do |popmail|
824
+ # File.open("inbox/#{n}", 'w') do |f|
825
+ # f.write popmail.pop
826
+ # end
827
+ # popmail.delete ####
828
+ # n += 1
829
+ # end
830
+ # end
831
+ #
832
+ def delete
833
+ @command.dele @number
834
+ @deleted = true
835
+ end
836
+
837
+ alias delete! delete #:nodoc: obsolete
838
+
839
+ # True if the mail has been deleted.
840
+ def deleted?
841
+ @deleted
842
+ end
843
+
844
+ # Returns the unique-id of the message.
845
+ # Normally the unique-id is a hash string of the message.
846
+ #
847
+ # This method raises a POPError if an error occurs.
848
+ def unique_id
849
+ return @uid if @uid
850
+ @pop.set_all_uids
851
+ @uid
852
+ end
853
+
854
+ alias uidl unique_id
855
+
856
+ def uid=(uid) #:nodoc: internal use only
857
+ @uid = uid
858
+ end
859
+
860
+ end # class POPMail
861
+
862
+
863
+ class POP3Command #:nodoc: internal use only
864
+
865
+ def initialize(sock)
866
+ @socket = sock
867
+ @error_occured = false
868
+ res = check_response(critical { recv_response() })
869
+ @apop_stamp = res.slice(/<.+>/)
870
+ end
871
+
872
+ def inspect
873
+ "#<#{self.class} socket=#{@socket}>"
874
+ end
875
+
876
+ def auth(account, password)
877
+ check_response_auth(critical {
878
+ check_response_auth(get_response('USER %s', account))
879
+ get_response('PASS %s', password)
880
+ })
881
+ end
882
+
883
+ def apop(account, password)
884
+ raise POPAuthenticationError, 'not APOP server; cannot login' \
885
+ unless @apop_stamp
886
+ check_response_auth(critical {
887
+ get_response('APOP %s %s',
888
+ account,
889
+ Digest::MD5.hexdigest(@apop_stamp + password))
890
+ })
891
+ end
892
+
893
+ def list
894
+ critical {
895
+ getok 'LIST'
896
+ list = []
897
+ @socket.each_list_item do |line|
898
+ m = /\A(\d+)[ \t]+(\d+)/.match(line) or
899
+ raise POPBadResponse, "bad response: #{line}"
900
+ list.push [m[1].to_i, m[2].to_i]
901
+ end
902
+ return list
903
+ }
904
+ end
905
+
906
+ def stat
907
+ res = check_response(critical { get_response('STAT') })
908
+ m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
909
+ raise POPBadResponse, "wrong response format: #{res}"
910
+ [m[1].to_i, m[2].to_i]
911
+ end
912
+
913
+ def rset
914
+ check_response(critical { get_response('RSET') })
915
+ end
916
+
917
+ def top(num, lines = 0, &block)
918
+ critical {
919
+ getok('TOP %d %d', num, lines)
920
+ @socket.each_message_chunk(&block)
921
+ }
922
+ end
923
+
924
+ def retr(num, &block)
925
+ critical {
926
+ getok('RETR %d', num)
927
+ @socket.each_message_chunk(&block)
928
+ }
929
+ end
930
+
931
+ def dele(num)
932
+ check_response(critical { get_response('DELE %d', num) })
933
+ end
934
+
935
+ def uidl(num = nil)
936
+ if num
937
+ res = check_response(critical { get_response('UIDL %d', num) })
938
+ return res.split(/ /)[1]
939
+ else
940
+ critical {
941
+ getok('UIDL')
942
+ table = {}
943
+ @socket.each_list_item do |line|
944
+ num, uid = line.split
945
+ table[num.to_i] = uid
946
+ end
947
+ return table
948
+ }
949
+ end
950
+ end
951
+
952
+ def quit
953
+ check_response(critical { get_response('QUIT') })
954
+ end
955
+
956
+ private
957
+
958
+ def getok(fmt, *fargs)
959
+ @socket.writeline sprintf(fmt, *fargs)
960
+ check_response(recv_response())
961
+ end
962
+
963
+ def get_response(fmt, *fargs)
964
+ @socket.writeline sprintf(fmt, *fargs)
965
+ recv_response()
966
+ end
967
+
968
+ def recv_response
969
+ @socket.readline
970
+ end
971
+
972
+ def check_response(res)
973
+ raise POPError, res unless /\A\+OK/i =~ res
974
+ res
975
+ end
976
+
977
+ def check_response_auth(res)
978
+ raise POPAuthenticationError, res unless /\A\+OK/i =~ res
979
+ res
980
+ end
981
+
982
+ def critical
983
+ return '+OK dummy ok response' if @error_occured
984
+ begin
985
+ return yield()
986
+ rescue Exception
987
+ @error_occured = true
988
+ raise
989
+ end
990
+ end
991
+
992
+ end # class POP3Command
993
+
994
+ end # module Net
@@ -78,7 +78,7 @@ module Mailer::ShouldaMacros::TestUnit
78
78
  end
79
79
 
80
80
  def should_not_pass_the_config_check
81
- should "not pass the config check" do
81
+ should "not pass the config check: #{subject.inspect}" do
82
82
  assert_raises(Mailer::ConfigError) do
83
83
  Mailer.config.check
84
84
  end
@@ -3,7 +3,7 @@ module Mailer
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- TINY = 1
6
+ TINY = 2
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
data/lib/mailer.rb CHANGED
@@ -7,6 +7,7 @@ require 'mailer/exceptions'
7
7
  require 'mailer/config'
8
8
  require 'mailer/deliveries'
9
9
  require 'mailer/tls'
10
+ require 'mailer/mailbox'
10
11
 
11
12
  module Mailer
12
13
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kelredd-mailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kelly Redding
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-06 00:00:00 -06:00
12
+ date: 2009-11-17 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,8 @@ files:
46
46
  - lib/mailer/config.rb
47
47
  - lib/mailer/deliveries.rb
48
48
  - lib/mailer/exceptions.rb
49
+ - lib/mailer/mailbox.rb
50
+ - lib/mailer/pop_ssl.rb
49
51
  - lib/mailer/shoulda_macros/test_unit.rb
50
52
  - lib/mailer/test_helpers.rb
51
53
  - lib/mailer/tls.rb