mail 1.2.1 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mail might be problematic. Click here for more details.

@@ -1,6 +1,47 @@
1
+ == Fri Nov 13 00:31:04 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
2
+
3
+ * Hacked and mutilated the network section, made it easier to extend out with other
4
+ delivery and retriever methods. API changed SLIGHTLY with this. Please check the
5
+ readme
6
+ * Version bump to 1.2.5
7
+
8
+ == Thu Nov 12 02:58:01 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
9
+
10
+ * Resolved Issue #8 - Message ID not handling multiple periods in left hand side
11
+ * Resolved Issue #6 - Ordering of add_file and body items causes invalid emails
12
+
13
+ == Tue Nov 10 08:15:14 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
14
+
15
+ * Resolved Issue #5 - Message ID generation issue
16
+ * Resolved Issue #7 - README add_file examples don't seem to work - Updated readme and
17
+ rdoc in Message#add_file
18
+
19
+ == Mon Nov 9 23:38:33 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
20
+
21
+ * Added ability to create new email via a hash or hash-like object. <mikel>
22
+ * Moved all of the Part init into the Message class. Part now just uses Message's init,
23
+ also moved all the attachment related functions into Message. As Part is a subclass
24
+ of message, you shouldn't see any interface changes here.
25
+
26
+ == Fri Nov 6 22:52:10 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
27
+
28
+ * a6ef2b4: Fixed Issue #4 - Can't call encoding on non existant
29
+ content-transer-encoding header
30
+
31
+ == Fri Nov 6 00:51:55 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
32
+
33
+ * Handled unstructured field folding "blank" lines
34
+ * Fixed error in header.rb that created fields into an array, instead of a FieldList, resulting
35
+ in mail.encode returning a random sort order on the header.
36
+ * Made unstructured fields attempt to decode their values on :decode
37
+
38
+ == Thu Nov 5 04:45:31 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
39
+
40
+ * 2acb70a: Closes Issue #1 - Handling badly formatted content-type fields <mikel>
41
+
1
42
  == Wed Nov 4 23:24:32 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
2
43
 
3
- * Created commit 2b5d608: Closes Issue #2 - Empty header field values not parsing <mikel>
44
+ * 2b5d608: Closes Issue #2 - Empty header field values not parsing <mikel>
4
45
  * Version bumb to 1.2.1
5
46
 
6
47
  == Wed Nov 4 12:54:43 UTC 2009 Mikel Lindsaar <raasdnil@gmail.com>
@@ -70,7 +70,13 @@ lib/mail/header.rb
70
70
  lib/mail/mail.rb
71
71
  lib/mail/message.rb
72
72
  lib/mail/network/deliverable.rb
73
+ lib/mail/network/delivery_methods/file_delivery.rb
74
+ lib/mail/network/delivery_methods/sendmail.rb
75
+ lib/mail/network/delivery_methods/smtp.rb
76
+ lib/mail/network/delivery_methods/test_mailer.rb
73
77
  lib/mail/network/retrievable.rb
78
+ lib/mail/network/retriever_methods/imap.rb
79
+ lib/mail/network/retriever_methods/pop3.rb
74
80
  lib/mail/parsers/address_lists.rb
75
81
  lib/mail/parsers/address_lists.treetop
76
82
  lib/mail/parsers/content_disposition.rb
@@ -23,6 +23,11 @@ Finally, Mail has been designed with a very simple object oriented system
23
23
  that really opens up the email messages you are parsing, if you know what
24
24
  you are doing, you can fiddle with every last bit of your email directly.
25
25
 
26
+ == Discussion
27
+
28
+ If you want to discuss mail with like minded individuals, please subscribe to
29
+ the Google Group http://groups.google.com/group/mail-ruby
30
+
26
31
  == Current Capabilities of Mail
27
32
 
28
33
  * RFC2822 Support, Reading and Writing
@@ -205,7 +210,7 @@ what you are doing.
205
210
  to 'you@test.lindsaar.net'
206
211
  subject 'Here is the image you wanted'
207
212
  body File.read('body.txt')
208
- add_file 'New Header Image', '/somefile.png'
213
+ add_file '/somefile.png'
209
214
  end
210
215
 
211
216
  or
@@ -219,7 +224,7 @@ or
219
224
  to 'you@test.lindsaar.net'
220
225
  subject 'Here is the image you wanted'
221
226
  body File.read('body.txt')
222
- add_file 'New Header Image', '/somefile.png'
227
+ add_file {:filename => 'somefile.png', :data => File.read('/somefile.png')}
223
228
  end
224
229
 
225
230
  mail.deliver!
@@ -28,7 +28,13 @@ module Mail # :doc:
28
28
  require File.join(dir_name, 'utilities')
29
29
  require File.join(dir_name, 'configuration')
30
30
  require File.join(dir_name, 'network', 'deliverable')
31
+ require File.join(dir_name, 'network', 'delivery_methods', 'smtp')
32
+ require File.join(dir_name, 'network', 'delivery_methods', 'file_delivery')
33
+ require File.join(dir_name, 'network', 'delivery_methods', 'sendmail')
34
+ require File.join(dir_name, 'network', 'delivery_methods', 'test_mailer')
31
35
  require File.join(dir_name, 'network', 'retrievable')
36
+ require File.join(dir_name, 'network', 'retriever_methods', 'pop3')
37
+ require File.join(dir_name, 'network', 'retriever_methods', 'imap')
32
38
 
33
39
  require File.join(dir_name, 'message')
34
40
  require File.join(dir_name, 'part')
@@ -54,6 +54,11 @@ module Mail
54
54
 
55
55
  alias :to_s :encoded
56
56
 
57
+ # Returns the raw source right now. Need to implement
58
+ def decoded
59
+ raw_source
60
+ end
61
+
57
62
  def charset
58
63
  @charset
59
64
  end
@@ -7,7 +7,11 @@ require 'singleton'
7
7
  module Mail
8
8
 
9
9
  # The Configuration class is a Singleton used to hold the default
10
- # configuration for all SMTP, POP3 and IMAP operations handled by Mail.
10
+ # configuration for all class wide configurations of Mail.
11
+ #
12
+ # This includes things like running in Test mode, the POP3, IMAP,
13
+ # SMTP, Sendmail and File delivery method information et al.
14
+ #
11
15
  # See Mail.defaults for more information.
12
16
  class Configuration
13
17
  include Singleton
@@ -35,11 +39,19 @@ module Mail
35
39
  # Mail.defaults do
36
40
  # smtp '127.0.0.1', 25
37
41
  # end
38
- def smtp(*args)
42
+ def smtp(*args, &block)
39
43
  if args.size > 0
40
- @smtp = [args[0], (args[1].to_i > 0 ? args[1] : 25)]
44
+ host_array = [args[0], (args[1].to_i > 0 ? args[1] : 25)]
41
45
  end
42
- @smtp
46
+ set_settings(Mail::SMTP, host_array, &block)
47
+ end
48
+
49
+ # Allows you to define the delivery method for mail, defaults to SMTP
50
+ #
51
+ # This can either be a symbol (:smtp, :sendmail, :file, :test) or you can pass
52
+ # in your own delivery class, in which case this will be set.
53
+ def delivery_method(value = nil)
54
+ value ? @delivery_method = lookup_delivery_method(value) : @delivery_method ||= Mail::SMTP
43
55
  end
44
56
 
45
57
  # Allows you to specify the POP3 Server by passing the hostname
@@ -57,38 +69,66 @@ module Mail
57
69
  # Mail.defaults do
58
70
  # pop3 '127.0.0.1', 110
59
71
  # end
60
- def pop3(*args)
72
+ def pop3(*args, &block)
61
73
  if args.size > 0
62
- @pop3 = [args[0], (args[1].to_i > 0 ? args[1] : 110)]
74
+ host_array = [args[0], (args[1].to_i > 0 ? args[1] : 110)]
63
75
  end
64
- @pop3
65
- end
66
-
67
- # Pass in the username to use for POP3 or IMAP access
68
- def user(value = nil)
69
- value ? @user = value : @user
70
- end
71
-
72
- def pass(value = nil)
73
- value ? @pass = value : @pass
74
- end
75
-
76
- def enable_tls
77
- @tls = true
78
- end
79
-
80
- def disable_tls
81
- @tls = false
76
+ set_settings(Mail::POP3, host_array, &block)
82
77
  end
83
78
 
84
- def tls?
85
- @tls || false
79
+ # Allows you to define the retriever method for mail, defaults to POP3
80
+ #
81
+ # This can either be a symbol (:pop3, :imap) or you can pass
82
+ # in your own retriever class, in which case this will be set.
83
+ def retriever_method(value = nil)
84
+ value ? @retriever_method = lookup_retriever_method(value) : @retriever_method ||= Mail::POP3
86
85
  end
87
-
86
+
88
87
  def param_encode_language(value = nil)
89
88
  value ? @encode_language = value : @encode_language ||= 'en'
90
89
  end
91
90
 
91
+ private
92
+
93
+ def set_settings(klass, host_array = nil, &block)
94
+ if host_array
95
+ klass.instance.settings do
96
+ host host_array[0]
97
+ port host_array[1]
98
+ end
99
+ end
100
+ if block_given?
101
+ klass.instance.settings(&block)
102
+ end
103
+ klass.instance.settings(&block)
104
+ end
105
+
106
+ def lookup_delivery_method(method)
107
+ case method
108
+ when :smtp || nil
109
+ Mail::SMTP
110
+ when :sendmail
111
+ Mail::Sendmail
112
+ when :file
113
+ Mail::FileDelivery
114
+ when :test
115
+ Mail::TestMailer
116
+ else
117
+ method
118
+ end
119
+ end
120
+
121
+ def lookup_retriever_method(method)
122
+ case method
123
+ when :pop3 || nil
124
+ Mail::POP3
125
+ # when :imap
126
+ # Mail::IMAP
127
+ else
128
+ method
129
+ end
130
+ end
131
+
92
132
  end
93
133
 
94
134
  end
@@ -29,6 +29,8 @@ module Mail
29
29
 
30
30
  def element
31
31
  @element ||= Mail::ContentTypeElement.new(value)
32
+ rescue
33
+ @element ||= Mail::ContentTypeElement.new(sanatize(value))
32
34
  end
33
35
 
34
36
  def main_type
@@ -102,6 +104,15 @@ module Mail
102
104
  super
103
105
  end
104
106
  end
107
+
108
+ def sanatize( val )
109
+ case
110
+ when val.chomp =~ /^text$/
111
+ 'text/plain'
112
+ when val =~ /([\w\d_]+\/[\w\d_]+)\s(.*)/
113
+ "#{$1}; #{$2}"
114
+ end
115
+ end
105
116
 
106
117
  end
107
118
  end
@@ -38,7 +38,7 @@ module Mail
38
38
  end
39
39
 
40
40
  def do_decode
41
- value.blank? ? nil : value
41
+ value.blank? ? nil : Encodings.decode_encode(value, :decode)
42
42
  end
43
43
 
44
44
  # 2.2.3. Long Header Fields
@@ -77,7 +77,7 @@ module Mail
77
77
  @folded_line = []
78
78
  @unfolded_line = value.clone
79
79
  fold("#{name}: ".length)
80
- folded = @folded_line.compact.join("\r\n\t")
80
+ folded = @folded_line.map { |l| l unless l.blank? }.compact.join("\r\n\t")
81
81
  "#{name}: #{folded}"
82
82
  end
83
83
  end
@@ -47,7 +47,7 @@ module Mail
47
47
  # Returns an array of all the fields in the header in order that they
48
48
  # were read in.
49
49
  def fields
50
- @fields ||= []
50
+ @fields ||= FieldList.new
51
51
  end
52
52
 
53
53
  # 3.6. Field definitions
@@ -26,6 +26,17 @@ module Mail
26
26
  # body 'This is the body'
27
27
  # end
28
28
  #
29
+ # Or creating via a hash (or hash like object):
30
+ #
31
+ # message = Mail.new({:to => 'mikel@test.lindsaar.net',
32
+ # 'from' => 'bob@test.lindsaar.net',
33
+ # :subject 'This is an email',
34
+ # :body 'This is the body' })
35
+ #
36
+ # Note, the hash keys can be strings or symbols, the passed in object
37
+ # does not need to be a hash, it just needs to respond to :each_pair
38
+ # and yield each key value pair.
39
+ #
29
40
  # As a side note, you can also create a new email through creating
30
41
  # a Mail::Message object directly and then passing in values via string,
31
42
  # symbol or direct method calls. See Mail::Message for more information.
@@ -36,11 +47,7 @@ module Mail
36
47
  # mail['subject'] = 'This is an email'
37
48
  # mail.body = 'This is the body'
38
49
  def Mail.new(*args, &block)
39
- if block_given?
40
- Mail::Message.new(args, &block)
41
- else
42
- Mail::Message.new(args)
43
- end
50
+ Mail::Message.new(args, &block)
44
51
  end
45
52
 
46
53
  # Set the default configuration to send and receive emails. The defaults
@@ -48,19 +55,37 @@ module Mail
48
55
  # if port values are omitted from the SMTP and POP3 method calls, then it
49
56
  # is assumed to use the default ports of 25 and 110 respectively.
50
57
  #
51
- # The methods you can call in the default configuration are:
58
+ # You call defaults in a block, then you can set the basic host and port,
59
+ # by passing arguments, also, if the method you are using required more
60
+ # information (like pop3), then pass a block to it and call methods like
61
+ # user, pass, enable_tls etc.
52
62
  #
53
- # smtp(server_name, port):: Sets the SMTP server domain name and port
54
- # pop3(server_name, port):: Sets the POP3 server domain name and port
55
- # user(username):: Sets the username used for POP3 sessions
56
- # pass(password):: Sets the password used for POP3 sessions
63
+ # The arguments and block are both optional.
57
64
  #
58
65
  # Mail.defaults do
59
66
  # smtp 'smtp.myhost.fr', 587
60
- # pop3 'pop.myhost.fr'
61
- # user 'bernardo'
62
- # pass 'mypass'
63
- # enable_tls
67
+ # pop3 'pop.myhost.fr' do
68
+ # user 'bernardo'
69
+ # pass 'mypass'
70
+ # enable_tls
71
+ # end
72
+ # end
73
+ #
74
+ # You will also want to specify a delivery and retriever type, the defaults
75
+ # are SMTP and POP3. You set the types by passing a symbol:
76
+ #
77
+ # Mail.defaults do
78
+ # retriever_method :pop3
79
+ # delivery_method :smtp
80
+ # end
81
+ #
82
+ # The only implemented methods at the moment are :pop3, :smtp and :test
83
+ #
84
+ # You can also specify your own delivery class which must respond to :deliver!
85
+ # or a retriever class which must respond to :get_messages
86
+ #
87
+ # Mail.defaults do
88
+ # retriever_method MyOwnRetriever.new
64
89
  # end
65
90
  #
66
91
  # Once you have set defaults, you can call Mail.deliver to send an email
@@ -68,6 +93,8 @@ module Mail
68
93
  def Mail.defaults(&block)
69
94
  if block_given?
70
95
  Mail::Configuration.instance.defaults(&block)
96
+ else
97
+ Mail::Configuration.instance
71
98
  end
72
99
  end
73
100
 
@@ -75,6 +102,8 @@ module Mail
75
102
  # configuration first before you use Mail.deliver, if you don't, an appropriate
76
103
  # error will be raised telling you to.
77
104
  #
105
+ # If you do not specify a delivery type, SMTP will be used.
106
+ #
78
107
  # Mail.deliver do
79
108
  # to 'mikel@test.lindsaar.net'
80
109
  # from 'ada@test.lindsaar.net'
@@ -82,27 +111,46 @@ module Mail
82
111
  # body 'Not much to say here'
83
112
  # end
84
113
  #
114
+ # You can also do:
115
+ #
116
+ # mail = Mail.read('email.eml')
117
+ # mail.deliver!
118
+ #
85
119
  # And your email object will be created and sent.
86
-
87
120
  def Mail.deliver(*args, &block)
88
- Mail.new(args, &block).deliver!
121
+ mail = Mail.new(args, &block)
122
+ Deliverable.perform_delivery!(mail)
123
+ mail
89
124
  end
90
125
 
126
+ # Returns all the messages on the server using the retriever method set up
127
+ # in Mail.defaults
91
128
  def Mail.get_all_mail(&block)
92
- Mail::Message.get_all_mail(&block)
129
+ if block_given?
130
+ Retrievable.get_messages.each do |message|
131
+ yield message
132
+ end
133
+ else
134
+ Retrievable.get_messages
135
+ end
93
136
  end
94
137
 
95
138
  def Mail.read(filename)
96
139
  Mail.new(File.read(filename))
97
140
  end
98
141
 
142
+ # Provides a store of all the emails sent
143
+ def Mail.deliveries
144
+ @@deliveries ||= []
145
+ end
146
+
99
147
  protected
100
148
 
101
149
  def Mail.random_tag
102
150
  t = Time.now
103
151
  sprintf('%x%x_%x%x%d%x',
104
152
  t.to_i, t.tv_usec,
105
- $$, Thread.current.object_id, Mail.uniq, rand(255))
153
+ $$, Thread.current.object_id.abs, Mail.uniq, rand(255))
106
154
  end
107
155
 
108
156
  private
@@ -46,21 +46,43 @@ module Mail
46
46
 
47
47
  include Patterns
48
48
  include Utilities
49
- include Deliverable
50
- include Retrievable
51
49
 
52
50
  # Creates a new Mail::Message object through .new
53
51
  def initialize(*args, &block)
54
- self.raw_source = args.flatten[0].to_s.strip
55
- set_envelope_header
56
- parse_message
57
- separate_parts if multipart?
52
+
53
+ if args.flatten.first.respond_to?(:each_pair)
54
+ init_with_hash(args.flatten.first)
55
+ else
56
+ init_with_string(args.flatten[0].to_s.strip)
57
+ end
58
+
58
59
  if block_given?
59
60
  instance_eval(&block)
60
61
  end
62
+
61
63
  self
62
64
  end
63
65
 
66
+ def deliver!
67
+ Deliverable.perform_delivery!(self)
68
+ end
69
+
70
+ def <=>(other)
71
+ if other.nil?
72
+ 1
73
+ else
74
+ self.date <=> other.date
75
+ end
76
+ end
77
+
78
+ def ==(other)
79
+ unless other.respond_to?(:encoded)
80
+ false
81
+ else
82
+ self.encoded == other.encoded
83
+ end
84
+ end
85
+
64
86
  # Provides access to the raw source of the message as it was when it
65
87
  # was instantiated. This is set at initialization and so is untouched
66
88
  # by the parsers or decoder / encoders
@@ -133,8 +155,34 @@ module Mail
133
155
  #
134
156
  # mail.body = 'This is the body'
135
157
  # mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
158
+ #
159
+ # You can also reset the body of an Message object by setting body to nil
160
+ #
161
+ # Example:
162
+ #
163
+ # mail.body = 'this is the body'
164
+ # mail.body.encoded #=> 'this is the body'
165
+ # mail.body = nil
166
+ # mail.body.encoded #=> ''
167
+ #
168
+ # If you try and set the body of an email that is a multipart email, then instead
169
+ # of deleting all the parts of your email, mail will add a text/plain part to
170
+ # your email:
171
+ #
172
+ # mail.add_file 'somefilename.png'
173
+ # mail.parts.length #=> 1
174
+ # mail.body = "This is a body"
175
+ # mail.parts.length #=> 2
176
+ # mail.parts.last.content_type.content_type #=> 'This is a body'
136
177
  def body=(value)
137
- @body = Mail::Body.new(value)
178
+ case
179
+ when value == nil
180
+ @body = Mail::Body.new('')
181
+ when @body && !@body.parts.empty?
182
+ @body << Mail::Part.new(value)
183
+ else
184
+ @body = Mail::Body.new(value)
185
+ end
138
186
  end
139
187
 
140
188
  # Returns the body of the message object. Or, if passed
@@ -561,8 +609,39 @@ module Mail
561
609
  self.body << part
562
610
  end
563
611
 
564
- # Adds a part to the parts list or creates the part list
612
+ # Adds a file to the message. You have two options with this method, you can
613
+ # just pass in the absolute path to the file you want and Mail will read the file,
614
+ # get the filename from the path you pass in and guess the mime type, or you
615
+ # can pass in the filename as a string, and pass in the file data as a blob.
616
+ #
617
+ # Example:
618
+ #
619
+ # m = Mail.new
620
+ # m.add_file('/path/to/filename.png')
621
+ #
622
+ # or
623
+ #
624
+ # m = Mail.new
625
+ # m.add_file(:filename => 'filename.png', :data => File.read('/path/to/filename.png'))
626
+ #
627
+ # The above two alternatives will produce the same email message.
628
+ #
629
+ # Note also that if you add a file to an existing message, Mail will convert that message
630
+ # to a MIME multipart email, moving whatever plain text body you had into it's own text
631
+ # plain part.
632
+ #
633
+ # Example:
634
+ #
635
+ # m = Mail.new do
636
+ # body 'this is some text'
637
+ # end
638
+ # m.multipart? #=> false
639
+ # m.add_file('/path/to/filename.png')
640
+ # m.multipart? #=> true
641
+ # m.parts.first.content_type.content_type #=> 'text/plain'
642
+ # m.parts.last.content_type.content_type #=> 'image/png'
565
643
  def add_file(options)
644
+ convert_to_multipart unless self.multipart? || self.body.decoded.blank?
566
645
  add_multipart_mixed_header
567
646
  if options.is_a?(Hash)
568
647
  self.body << Mail::Part.new(options)
@@ -571,6 +650,14 @@ module Mail
571
650
  end
572
651
  end
573
652
 
653
+ def convert_to_multipart
654
+ text = @body.decoded
655
+ self.body = ''
656
+ text_part = Mail::Part.new({:content_type => 'text/plain;',
657
+ :body => text})
658
+ self.body << text_part
659
+ end
660
+
574
661
  # Encodes the message, calls encode on all it's parts, gets an email message
575
662
  # ready to send
576
663
  def ready_to_send!
@@ -600,6 +687,25 @@ module Mail
600
687
  raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
601
688
  end
602
689
 
690
+ # Returns true if this part is an attachment
691
+ def attachment?
692
+ find_attachment
693
+ end
694
+
695
+ # Returns the attachment data if there is any
696
+ def attachment
697
+ @attachment
698
+ end
699
+
700
+ # Returns the filename of the attachment
701
+ def filename
702
+ if attachment?
703
+ attachment.filename
704
+ else
705
+ nil
706
+ end
707
+ end
708
+
603
709
  private
604
710
 
605
711
  # 2.1. General Description
@@ -651,17 +757,64 @@ module Mail
651
757
  def add_multipart_mixed_header
652
758
  unless header['content-type']
653
759
  header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
760
+
654
761
  body.boundary = boundary
655
762
  end
656
763
  end
657
764
 
658
- class << self
659
-
660
- # Only POP3 is supported for now
661
- def get_all_mail(&block)
662
- self.pop3_get_all_mail(&block)
765
+ def init_with_hash(hash)
766
+ self.raw_source = ''
767
+ @header = Mail::Header.new
768
+ @body = Mail::Body.new
769
+ hash.each_pair do |k,v|
770
+ next if k.to_sym == :data
771
+ if k.to_sym == :filename
772
+ add_attachment(hash)
773
+ else
774
+ self[k] = v
775
+ end
663
776
  end
777
+ end
778
+
779
+ def add_attachment(options_hash)
780
+ @attachment = Mail::Attachment.new(options_hash)
781
+ self.content_type = "#{attachment.mime_type}; filename=\"#{attachment.filename}\""
782
+ self.content_transfer_encoding = "Base64"
783
+ self.content_disposition = "attachment; filename=\"#{attachment.filename}\""
784
+ self.body = attachment.encoded
785
+ end
786
+
787
+ def init_with_string(string)
788
+ self.raw_source = string
789
+ set_envelope_header
790
+ parse_message
791
+ separate_parts if multipart?
792
+ if filename = attachment?
793
+ encoding = content_transfer_encoding.encoding if content_transfer_encoding
794
+ @attachment = Mail::Attachment.new(:filename => filename,
795
+ :data => body.to_s,
796
+ :encoding => encoding)
797
+ end
798
+ end
799
+
800
+ # Returns the filename of the attachment (if it exists) or returns nil
801
+ def find_attachment
802
+ case
803
+ when content_type && content_type.filename
804
+ filename = content_type.filename
805
+ when content_disposition && content_disposition.filename
806
+ filename = content_disposition.filename
807
+ when content_location && content_location.location
808
+ filename = content_location.location
809
+ else
810
+ filename = nil
811
+ end
812
+ filename
813
+ end
664
814
 
815
+ # Only POP3 is supported for now
816
+ def Message.get_all_mail(&block)
817
+ self.pop3_get_all_mail(&block)
665
818
  end
666
819
 
667
820
  end
@@ -1,46 +1,15 @@
1
1
  # encoding: utf-8
2
-
3
- # Include this module to make a class "deliverable".
4
- # It uses the defaults set in Configuration to retrieve SMTP settings.
5
- #
6
- # Thanks to Nicolas Fouché for this wrapper
7
- #
8
2
  module Mail
3
+ # The deliverable class provides the way mail will send an email out when you
4
+ # are using the Mail.deliver! &block command or when you call Mail.deliver!(message)
5
+ #
6
+ # The default delivery is SMTP, localhost, port 25. You can change this through the
7
+ # Mail.defaults call.s
9
8
  module Deliverable
10
9
 
11
- # Send the message via SMTP.
12
- # The from and to attributes are optional. If not set, they are retrieve from the Message.
13
- def deliver!(from = nil, to = nil, rfc2822 = nil)
14
- config = Mail::Configuration.instance
15
- if config.smtp.blank? || config.smtp[0].blank?
16
- raise ArgumentError.new('Please call +Mail.defaults+ to set the SMTP configuration')
17
- end
18
-
19
- # TODO: use the "return-path" field by default instead of the "from" field ? (see ActionMailer)
20
- from ||= self.from.addresses.first if self.respond_to?(:from) && self.from
21
- raise ArgumentError.new('An author -from- is required to send a message') if from.blank?
22
- to ||= self.to.addresses if self.respond_to?(:to) && self.to
23
- raise ArgumentError.new('At least one recipient -from- is required to send a message') if to.blank?
24
- rfc2822 ||= self.encoded if self.respond_to?(:encoded)
25
- raise ArgumentError.new('A encoded content is required to send a message') if rfc2822.blank?
26
-
27
- smtp = Net::SMTP.new(config.smtp[0], config.smtp[1] || 25)
28
- if config.tls?
29
- if OpenSSL::SSL::VERIFY_NONE.kind_of?(OpenSSL::SSL::SSLContext)
30
- smtp.enable_tls(OpenSSL::SSL::VERIFY_NONE)
31
- else
32
- smtp.enable_tls
33
- end
34
- else
35
- smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto)
36
- end
37
-
38
- smtp.start(helo = 'localhost.localdomain', config.user, config.pass, authentication = :plain) do |smtp|
39
- smtp.sendmail(rfc2822, from, to)
40
- end
41
-
42
- self
10
+ def self.perform_delivery!(mail)
11
+ Mail.defaults.delivery_method.deliver!(mail)
43
12
  end
44
- end
45
13
 
14
+ end
46
15
  end
@@ -0,0 +1,18 @@
1
+ module Mail
2
+ class FileDelivery
3
+ include Singleton
4
+
5
+ def settings(&block)
6
+ if block_given?
7
+ instance_eval(&block)
8
+ end
9
+ self
10
+ end
11
+
12
+ def FileDelivery.deliver!(mail)
13
+ # To be implemented
14
+ Mail.deliveries << mail
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Mail
2
+ class Sendmail
3
+ include Singleton
4
+
5
+ def settings(&block)
6
+ if block_given?
7
+ instance_eval(&block)
8
+ end
9
+ self
10
+ end
11
+
12
+ def Sendmail.deliver!(mail)
13
+ # To be implemented
14
+ Mail.deliveries << mail
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,87 @@
1
+ module Mail
2
+ class SMTP
3
+ include Singleton
4
+
5
+ def settings(&block)
6
+ if block_given?
7
+ instance_eval(&block)
8
+ end
9
+ self
10
+ end
11
+
12
+ # This is the host that you will send your SMTP mails to, defaults to 'localhost'
13
+ def host(value = nil)
14
+ value ? @host = value : @host ||= 'localhost'
15
+ end
16
+
17
+ # This is the port that SMTP email get sent to, default is 25
18
+ def port(value = nil)
19
+ value ? @port = value.to_i : @port ||= 25
20
+ end
21
+
22
+ # The username to use during SMTP authentication
23
+ def user(value = nil)
24
+ value ? @user = value : @user
25
+ end
26
+
27
+ # Password to use during SMTP authentication
28
+ def pass(value = nil)
29
+ value ? @pass = value : @pass
30
+ end
31
+
32
+ # The helo domain used at the begining of an SMTP conversation,
33
+ # default is 'localhost.localdomain'
34
+ def helo(value = nil)
35
+ value ? @help = value : @helo ||= 'localhost.localdomain'
36
+ end
37
+
38
+ # Turn on TLS
39
+ def enable_tls
40
+ @tls = true
41
+ end
42
+
43
+ # Turn on TLS
44
+ def disable_tls
45
+ @tls = false
46
+ end
47
+
48
+ # TLS Enabled? Default is false
49
+ def tls?
50
+ @tls || false
51
+ end
52
+
53
+ # Send the message via SMTP.
54
+ # The from and to attributes are optional. If not set, they are retrieve from the Message.
55
+ def SMTP.deliver!(mail)
56
+
57
+ config = Mail.defaults
58
+
59
+ # TODO: use the "return-path" field by default instead of the "from" field ? (see ActionMailer)
60
+ from = mail.from.addresses.first if mail.respond_to?(:from) && mail.from
61
+ raise ArgumentError.new('An author -from- is required to send a message') if from.blank?
62
+ to ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
63
+ raise ArgumentError.new('At least one recipient -to, cc, bcc- is required to send a message') if to.blank?
64
+ msg_str ||= mail.encoded if mail.respond_to?(:encoded)
65
+ raise ArgumentError.new('A encoded content is required to send a message') if msg_str.blank?
66
+
67
+ smtp = Net::SMTP.new(config.smtp.host, config.smtp.port)
68
+ if config.smtp.tls?
69
+ if OpenSSL::SSL::VERIFY_NONE.kind_of?(OpenSSL::SSL::SSLContext)
70
+ smtp.enable_tls(OpenSSL::SSL::VERIFY_NONE)
71
+ else
72
+ smtp.enable_tls
73
+ end
74
+ else
75
+ smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto)
76
+ end
77
+
78
+ smtp.start(config.smtp.helo, config.smtp.user, config.smtp.pass, authentication = :plain) do |smtp|
79
+ smtp.sendmail(msg_str, from, to)
80
+ end
81
+
82
+ self
83
+ end
84
+
85
+
86
+ end
87
+ end
@@ -0,0 +1,10 @@
1
+ module Mail
2
+ class TestMailer
3
+ include Singleton
4
+
5
+ def TestMailer.deliver!(mail)
6
+ Mail.deliveries << mail
7
+ end
8
+
9
+ end
10
+ end
@@ -7,55 +7,9 @@
7
7
  #
8
8
  module Mail
9
9
  module Retrievable
10
-
11
- module ClassMethods
12
-
13
- # Get all emails via POP3.
14
- # TODO :limit option
15
- # TODO :order option
16
- def pop3_get_all_mail(&block)
17
- config = Mail::Configuration.instance
18
- if config.pop3.blank? || config.pop3[0].blank?
19
- raise ArgumentError.new('Please call +Mail.defaults+ to set the POP3 configuration')
20
- end
21
-
22
- pop3_start do |pop3|
23
- if block_given?
24
- pop3.each_mail do |pop_mail|
25
- yield self.new(pop_mail.pop)
26
- end
27
- else
28
- emails = []
29
- pop3.each_mail do |pop_mail|
30
- emails << self.new(pop_mail.pop)
31
- end
32
- emails
33
- end
34
- end
35
-
36
- end
37
-
38
- private
39
-
40
- def pop3_start(config = Mail::Configuration.instance, &block)
41
- raise ArgumentError.new("Mail::Retrievable#pop3_start takes a block") unless block_given?
42
-
43
- pop3 = Net::POP3.new(config.pop3[0], config.pop3[1] || 110, isapop = false)
44
- pop3.enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE) if config.tls?
45
- pop3.start(config.user, config.pass)
46
-
47
- yield pop3
48
- ensure
49
- if defined?(pop3) && pop3 && pop3.started?
50
- pop3.reset # This clears all "deleted" marks from messages.
51
- pop3.finish
52
- end
53
- end
54
-
55
- end
56
-
57
- def self.included(receiver)
58
- receiver.extend ClassMethods
10
+
11
+ def self.get_messages
12
+ Mail.defaults.retriever_method.get_messages
59
13
  end
60
14
 
61
15
  end
@@ -0,0 +1,17 @@
1
+ module Mail
2
+ class IMAP
3
+ include Singleton
4
+
5
+ def settings(&block)
6
+ if block_given?
7
+ instance_eval(&block)
8
+ end
9
+ self
10
+ end
11
+
12
+ def IMAP.get_messages(&block)
13
+ # To be implemented
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,92 @@
1
+ module Mail
2
+ class POP3
3
+ include Singleton
4
+
5
+ require 'net/pop'
6
+
7
+ def settings(&block)
8
+ if block_given?
9
+ instance_eval(&block)
10
+ end
11
+ self
12
+ end
13
+
14
+ # This is the host that you will send your POP3 mails to, defaults to 'localhost'
15
+ def host(value = nil)
16
+ value ? @host = value : @host ||= 'localhost'
17
+ end
18
+
19
+ # This is the port that POP3 email get sent to, default is 110
20
+ def port(value = nil)
21
+ value ? @port = value.to_i : @port ||= 110
22
+ end
23
+
24
+ # The username to use during POP3 authentication
25
+ def user(value = nil)
26
+ value ? @user = value : @user
27
+ end
28
+
29
+ # Password to use during POP3 authentication
30
+ def pass(value = nil)
31
+ value ? @pass = value : @pass
32
+ end
33
+
34
+ # Turn on TLS
35
+ def enable_tls
36
+ @tls = true
37
+ end
38
+
39
+ # Turn on TLS
40
+ def disable_tls
41
+ @tls = false
42
+ end
43
+
44
+ # TLS Enabled? Default is false
45
+ def tls?
46
+ @tls || false
47
+ end
48
+
49
+ # Get all emails via POP3.
50
+ # TODO :limit option
51
+ # TODO :order option
52
+ def POP3.get_messages(&block)
53
+ config = Mail.defaults
54
+ if config.pop3.host.blank? || config.pop3.port.blank?
55
+ raise ArgumentError.new('Please call +Mail.defaults+ to set the POP3 configuration')
56
+ end
57
+
58
+ start do |pop3|
59
+ if block_given?
60
+ pop3.each_mail do |pop_mail|
61
+ yield Mail.new(pop_mail.pop)
62
+ end
63
+ else
64
+ emails = []
65
+ pop3.each_mail do |pop_mail|
66
+ emails << Mail.new(pop_mail.pop)
67
+ end
68
+ emails
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ private
75
+
76
+ def POP3.start(config = Mail::Configuration.instance, &block)
77
+ raise ArgumentError.new("Mail::Retrievable#pop3_start takes a block") unless block_given?
78
+
79
+ pop3 = Net::POP3.new(config.pop3.host, config.pop3.port, isapop = false)
80
+ pop3.enable_ssl(verify = OpenSSL::SSL::VERIFY_NONE) if config.pop3.tls?
81
+ pop3.start(config.pop3.user, config.pop3.pass)
82
+
83
+ yield pop3
84
+ ensure
85
+ if defined?(pop3) && pop3 && pop3.started?
86
+ pop3.reset # This clears all "deleted" marks from messages.
87
+ pop3.finish
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -961,6 +961,53 @@ module Mail
961
961
  r0
962
962
  end
963
963
 
964
+ def _nt_mtext
965
+ start_index = index
966
+ if node_cache[:mtext].has_key?(index)
967
+ cached = node_cache[:mtext][index]
968
+ @index = cached.interval.end if cached
969
+ return cached
970
+ end
971
+
972
+ s0, i0 = [], index
973
+ loop do
974
+ i1 = index
975
+ r2 = _nt_atext
976
+ if r2
977
+ r1 = r2
978
+ else
979
+ if has_terminal?(".", false, index)
980
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
981
+ @index += 1
982
+ else
983
+ terminal_parse_failure(".")
984
+ r3 = nil
985
+ end
986
+ if r3
987
+ r1 = r3
988
+ else
989
+ @index = i1
990
+ r1 = nil
991
+ end
992
+ end
993
+ if r1
994
+ s0 << r1
995
+ else
996
+ break
997
+ end
998
+ end
999
+ if s0.empty?
1000
+ @index = i0
1001
+ r0 = nil
1002
+ else
1003
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1004
+ end
1005
+
1006
+ node_cache[:mtext][start_index] = r0
1007
+
1008
+ r0
1009
+ end
1010
+
964
1011
  module Atom0
965
1012
  end
966
1013
 
@@ -1118,6 +1165,35 @@ module Mail
1118
1165
  r0
1119
1166
  end
1120
1167
 
1168
+ def _nt_message_id_text
1169
+ start_index = index
1170
+ if node_cache[:message_id_text].has_key?(index)
1171
+ cached = node_cache[:message_id_text][index]
1172
+ @index = cached.interval.end if cached
1173
+ return cached
1174
+ end
1175
+
1176
+ s0, i0 = [], index
1177
+ loop do
1178
+ r1 = _nt_mtext
1179
+ if r1
1180
+ s0 << r1
1181
+ else
1182
+ break
1183
+ end
1184
+ end
1185
+ if s0.empty?
1186
+ @index = i0
1187
+ r0 = nil
1188
+ else
1189
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1190
+ end
1191
+
1192
+ node_cache[:message_id_text][start_index] = r0
1193
+
1194
+ r0
1195
+ end
1196
+
1121
1197
  module DotAtomText0
1122
1198
  def domain_text
1123
1199
  elements[0]
@@ -4378,7 +4454,7 @@ module Mail
4378
4454
  end
4379
4455
 
4380
4456
  i0 = index
4381
- r1 = _nt_dot_atom_text
4457
+ r1 = _nt_message_id_text
4382
4458
  if r1
4383
4459
  r0 = r1
4384
4460
  else
@@ -84,6 +84,10 @@ module Mail
84
84
  "|" / "}" /
85
85
  "~"
86
86
  end
87
+
88
+ rule mtext
89
+ (atext / ".")+
90
+ end
87
91
 
88
92
  rule atom
89
93
  CFWS? atext+ CFWS?
@@ -97,6 +101,10 @@ module Mail
97
101
  CFWS? local_dot_atom_text CFWS?
98
102
  end
99
103
 
104
+ rule message_id_text
105
+ mtext+
106
+ end
107
+
100
108
  rule dot_atom_text
101
109
  (domain_text "."?)+
102
110
  end
@@ -360,7 +368,7 @@ module Mail
360
368
  end
361
369
 
362
370
  rule id_left
363
- dot_atom_text / no_fold_quote / obs_id_left
371
+ message_id_text / no_fold_quote / obs_id_left
364
372
  end
365
373
 
366
374
  rule id_right
@@ -2,25 +2,6 @@
2
2
  module Mail
3
3
  class Part < Message
4
4
 
5
- def initialize(*args, &block)
6
- if args.flatten[0].is_a?(Hash)
7
- options_hash = args.flatten[0]
8
- super('') # Make an empty message, we are dealing with an attachment
9
- @attachment = Mail::Attachment.new(options_hash)
10
- self.content_type = "#{attachment.mime_type}; filename=\"#{attachment.filename}\""
11
- self.content_transfer_encoding = "Base64"
12
- self.content_disposition = "attachment; filename=\"#{attachment.filename}\""
13
- self.body = attachment.encoded
14
- else
15
- super
16
- if filename = attachment?
17
- @attachment = Mail::Attachment.new(:filename => filename,
18
- :data => body.to_s,
19
- :encoding => content_transfer_encoding.encoding)
20
- end
21
- end
22
- end
23
-
24
5
  # Creates a new empty Content-ID field and inserts it in the correct order
25
6
  # into the Header. The ContentIdField object will automatically generate
26
7
  # a unique content ID if you try and encode it or output it to_s without
@@ -31,25 +12,6 @@ module Mail
31
12
  header['content-id'] = content_id_val
32
13
  end
33
14
 
34
- # Returns true if this part is an attachment
35
- def attachment?
36
- find_attachment
37
- end
38
-
39
- # Returns the attachment data if there is any
40
- def attachment
41
- @attachment
42
- end
43
-
44
- # Returns the filename of the attachment
45
- def filename
46
- if attachment?
47
- attachment.filename
48
- else
49
- nil
50
- end
51
- end
52
-
53
15
  # Returns true if the part has a content ID field, the field may or may
54
16
  # not have a value, but the field exists or not.
55
17
  def has_content_id?
@@ -115,21 +77,6 @@ module Mail
115
77
  @delivery_status_data ||= Header.new(body.to_s.gsub("\r\n\r\n", "\r\n"))
116
78
  end
117
79
 
118
- # Returns the filename of the attachment (if it exists) or returns nil
119
- def find_attachment
120
- case
121
- when content_type && content_type.filename
122
- filename = content_type.filename
123
- when content_disposition && content_disposition.filename
124
- filename = content_disposition.filename
125
- when content_location && content_location.location
126
- filename = content_location.location
127
- else
128
- filename = nil
129
- end
130
- filename
131
- end
132
-
133
80
  end
134
81
 
135
82
  end
@@ -25,6 +25,7 @@ module Mail
25
25
  PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
26
26
  TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
27
27
 
28
+
28
29
  module ClassMethods
29
30
 
30
31
  end
@@ -25,9 +25,13 @@ module Mail
25
25
  # in double quotes, otherwise returns the string unmodified
26
26
  def quote_phrase( str )
27
27
  if RUBY_VERSION >= '1.9'
28
- string = str.dup
29
- string.force_encoding('ASCII-8BIT')
30
- (PHRASE_UNSAFE === string) ? dquote(str) : str
28
+ original_encoding = str.encoding
29
+ str.force_encoding('ASCII-8BIT')
30
+ if (PHRASE_UNSAFE === str)
31
+ dquote(str).force_encoding(original_encoding)
32
+ else
33
+ str.force_encoding(original_encoding)
34
+ end
31
35
  else
32
36
  (PHRASE_UNSAFE === str) ? dquote(str) : str
33
37
  end
@@ -3,7 +3,7 @@ module Mail
3
3
  module VERSION
4
4
  MAJOR = 1
5
5
  MINOR = 2
6
- TINY = 1
6
+ TINY = 5
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikel Lindsaar
@@ -133,7 +133,13 @@ files:
133
133
  - ./lib/mail/mail.rb
134
134
  - ./lib/mail/message.rb
135
135
  - ./lib/mail/network/deliverable.rb
136
+ - ./lib/mail/network/delivery_methods/file_delivery.rb
137
+ - ./lib/mail/network/delivery_methods/sendmail.rb
138
+ - ./lib/mail/network/delivery_methods/smtp.rb
139
+ - ./lib/mail/network/delivery_methods/test_mailer.rb
136
140
  - ./lib/mail/network/retrievable.rb
141
+ - ./lib/mail/network/retriever_methods/imap.rb
142
+ - ./lib/mail/network/retriever_methods/pop3.rb
137
143
  - ./lib/mail/parsers/address_lists.rb
138
144
  - ./lib/mail/parsers/address_lists.treetop
139
145
  - ./lib/mail/parsers/content_disposition.rb