skaar-daemon-kit 0.1.7.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. data/Configuration.txt +110 -0
  2. data/Deployment.txt +113 -0
  3. data/History.txt +97 -0
  4. data/Logging.txt +92 -0
  5. data/Manifest.txt +174 -0
  6. data/PostInstall.txt +6 -0
  7. data/README.rdoc +130 -0
  8. data/Rakefile +37 -0
  9. data/RuoteParticipants.txt +113 -0
  10. data/TODO.txt +37 -0
  11. data/app_generators/daemon_kit/USAGE +7 -0
  12. data/app_generators/daemon_kit/daemon_kit_generator.rb +178 -0
  13. data/app_generators/daemon_kit/templates/README +48 -0
  14. data/app_generators/daemon_kit/templates/Rakefile +6 -0
  15. data/app_generators/daemon_kit/templates/bin/daemon.erb +7 -0
  16. data/app_generators/daemon_kit/templates/config/arguments.rb +12 -0
  17. data/app_generators/daemon_kit/templates/config/boot.rb +68 -0
  18. data/app_generators/daemon_kit/templates/config/environment.rb +23 -0
  19. data/app_generators/daemon_kit/templates/config/environments/development.rb +2 -0
  20. data/app_generators/daemon_kit/templates/config/environments/production.rb +2 -0
  21. data/app_generators/daemon_kit/templates/config/environments/test.rb +2 -0
  22. data/app_generators/daemon_kit/templates/config/post-daemonize/readme +5 -0
  23. data/app_generators/daemon_kit/templates/config/pre-daemonize/readme +12 -0
  24. data/app_generators/daemon_kit/templates/lib/daemon.rb +2 -0
  25. data/app_generators/daemon_kit/templates/libexec/daemon.erb +18 -0
  26. data/app_generators/daemon_kit/templates/script/console +3 -0
  27. data/app_generators/daemon_kit/templates/script/destroy +14 -0
  28. data/app_generators/daemon_kit/templates/script/generate +14 -0
  29. data/bin/daemon_kit +18 -0
  30. data/daemon_generators/amqp/USAGE +5 -0
  31. data/daemon_generators/amqp/amqp_generator.rb +65 -0
  32. data/daemon_generators/amqp/templates/config/amqp.yml +28 -0
  33. data/daemon_generators/amqp/templates/config/initializers/amqp.rb +7 -0
  34. data/daemon_generators/amqp/templates/libexec/daemon.rb +37 -0
  35. data/daemon_generators/cron/USAGE +5 -0
  36. data/daemon_generators/cron/cron_generator.rb +64 -0
  37. data/daemon_generators/cron/templates/config/initializers/cron.rb +11 -0
  38. data/daemon_generators/cron/templates/libexec/daemon.rb +43 -0
  39. data/daemon_generators/cucumber/USAGE +11 -0
  40. data/daemon_generators/cucumber/cucumber_generator.rb +38 -0
  41. data/daemon_generators/cucumber/templates/cucumber +8 -0
  42. data/daemon_generators/cucumber/templates/cucumber.rake +13 -0
  43. data/daemon_generators/cucumber/templates/cucumber_environment.rb +2 -0
  44. data/daemon_generators/cucumber/templates/env.rb +7 -0
  45. data/daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb +35 -0
  46. data/daemon_generators/deploy_capistrano/templates/Capfile +10 -0
  47. data/daemon_generators/deploy_capistrano/templates/USAGE +10 -0
  48. data/daemon_generators/deploy_capistrano/templates/config/deploy.rb +53 -0
  49. data/daemon_generators/deploy_capistrano/templates/config/deploy/production.rb +6 -0
  50. data/daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb +6 -0
  51. data/daemon_generators/deploy_capistrano/templates/config/environments/staging.rb +0 -0
  52. data/daemon_generators/jabber/USAGE +5 -0
  53. data/daemon_generators/jabber/jabber_generator.rb +65 -0
  54. data/daemon_generators/jabber/templates/config/initializers/jabber.rb +7 -0
  55. data/daemon_generators/jabber/templates/config/jabber.yml +26 -0
  56. data/daemon_generators/jabber/templates/libexec/daemon.rb +27 -0
  57. data/daemon_generators/nanite_agent/USAGE +5 -0
  58. data/daemon_generators/nanite_agent/nanite_agent_generator.rb +68 -0
  59. data/daemon_generators/nanite_agent/templates/config/initializers/nanite_agent.rb +6 -0
  60. data/daemon_generators/nanite_agent/templates/config/nanite.yml +35 -0
  61. data/daemon_generators/nanite_agent/templates/lib/actors/sample.rb +11 -0
  62. data/daemon_generators/nanite_agent/templates/libexec/daemon.rb +31 -0
  63. data/daemon_generators/rspec/USAGE +5 -0
  64. data/daemon_generators/rspec/rspec_generator.rb +55 -0
  65. data/daemon_generators/rspec/templates/spec.rb +11 -0
  66. data/daemon_generators/rspec/templates/spec/spec.opts +1 -0
  67. data/daemon_generators/rspec/templates/spec/spec_helper.rb +21 -0
  68. data/daemon_generators/rspec/templates/tasks/rspec.rake +21 -0
  69. data/daemon_generators/ruote/USAGE +5 -0
  70. data/daemon_generators/ruote/ruote_generator.rb +67 -0
  71. data/daemon_generators/ruote/templates/config/amqp.yml +30 -0
  72. data/daemon_generators/ruote/templates/config/initializers/ruote.rb +13 -0
  73. data/daemon_generators/ruote/templates/config/ruote.yml +23 -0
  74. data/daemon_generators/ruote/templates/lib/daemon.rb +4 -0
  75. data/daemon_generators/ruote/templates/lib/sample.rb +26 -0
  76. data/daemon_generators/ruote/templates/libexec/daemon.rb +33 -0
  77. data/lib/daemon_kit.rb +62 -0
  78. data/lib/daemon_kit/abstract_logger.rb +235 -0
  79. data/lib/daemon_kit/amqp.rb +38 -0
  80. data/lib/daemon_kit/application.rb +187 -0
  81. data/lib/daemon_kit/arguments.rb +165 -0
  82. data/lib/daemon_kit/commands/console.rb +38 -0
  83. data/lib/daemon_kit/config.rb +108 -0
  84. data/lib/daemon_kit/console_daemon.rb +2 -0
  85. data/lib/daemon_kit/core_ext.rb +1 -0
  86. data/lib/daemon_kit/core_ext/configurable.rb +96 -0
  87. data/lib/daemon_kit/core_ext/string.rb +22 -0
  88. data/lib/daemon_kit/cron.rb +48 -0
  89. data/lib/daemon_kit/cucumber/world.rb +38 -0
  90. data/lib/daemon_kit/deployment/capistrano.rb +482 -0
  91. data/lib/daemon_kit/em.rb +43 -0
  92. data/lib/daemon_kit/error_handlers/base.rb +32 -0
  93. data/lib/daemon_kit/error_handlers/hoptoad.rb +61 -0
  94. data/lib/daemon_kit/error_handlers/mail.rb +85 -0
  95. data/lib/daemon_kit/exceptions.rb +15 -0
  96. data/lib/daemon_kit/initializer.rb +438 -0
  97. data/lib/daemon_kit/jabber.rb +170 -0
  98. data/lib/daemon_kit/nanite.rb +7 -0
  99. data/lib/daemon_kit/nanite/agent.rb +56 -0
  100. data/lib/daemon_kit/pid_file.rb +61 -0
  101. data/lib/daemon_kit/ruote_participants.rb +119 -0
  102. data/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
  103. data/lib/daemon_kit/ruote_workitem.rb +169 -0
  104. data/lib/daemon_kit/safety.rb +85 -0
  105. data/lib/daemon_kit/tasks.rb +2 -0
  106. data/lib/daemon_kit/tasks/environment.rake +10 -0
  107. data/lib/daemon_kit/tasks/framework.rake +120 -0
  108. data/lib/daemon_kit/tasks/god.rake +62 -0
  109. data/lib/daemon_kit/tasks/log.rake +8 -0
  110. data/lib/daemon_kit/tasks/monit.rake +29 -0
  111. data/script/console +10 -0
  112. data/script/destroy +14 -0
  113. data/script/generate +14 -0
  114. data/script/txt2html +71 -0
  115. data/spec/abstract_logger_spec.rb +126 -0
  116. data/spec/argument_spec.rb +70 -0
  117. data/spec/config_spec.rb +79 -0
  118. data/spec/configurable_spec.rb +56 -0
  119. data/spec/daemon_kit_spec.rb +7 -0
  120. data/spec/error_handlers_spec.rb +23 -0
  121. data/spec/fixtures/env.yml +15 -0
  122. data/spec/fixtures/noenv.yml +4 -0
  123. data/spec/initializer_spec.rb +26 -0
  124. data/spec/spec.opts +1 -0
  125. data/spec/spec_helper.rb +27 -0
  126. data/tasks/rspec.rake +21 -0
  127. data/templates/god/god.erb +69 -0
  128. data/templates/monit/monit.erb +14 -0
  129. data/test/test_amqp_generator.rb +48 -0
  130. data/test/test_cron_generator.rb +45 -0
  131. data/test/test_daemon-kit_generator.rb +84 -0
  132. data/test/test_daemon_kit_config.rb +28 -0
  133. data/test/test_deploy_capistrano_generator.rb +48 -0
  134. data/test/test_generator_helper.rb +29 -0
  135. data/test/test_helper.rb +7 -0
  136. data/test/test_jabber_generator.rb +49 -0
  137. data/test/test_nanite_agent_generator.rb +49 -0
  138. data/test/test_ruote_generator.rb +45 -0
  139. data/vendor/tmail-1.2.3/tmail.rb +5 -0
  140. data/vendor/tmail-1.2.3/tmail/address.rb +426 -0
  141. data/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
  142. data/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
  143. data/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
  144. data/vendor/tmail-1.2.3/tmail/config.rb +67 -0
  145. data/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
  146. data/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
  147. data/vendor/tmail-1.2.3/tmail/header.rb +960 -0
  148. data/vendor/tmail-1.2.3/tmail/index.rb +9 -0
  149. data/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
  150. data/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
  151. data/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
  152. data/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
  153. data/vendor/tmail-1.2.3/tmail/main.rb +6 -0
  154. data/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
  155. data/vendor/tmail-1.2.3/tmail/net.rb +248 -0
  156. data/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
  157. data/vendor/tmail-1.2.3/tmail/parser.rb +1476 -0
  158. data/vendor/tmail-1.2.3/tmail/port.rb +379 -0
  159. data/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
  160. data/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
  161. data/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
  162. data/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
  163. data/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
  164. data/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
  165. data/vendor/tmail-1.2.3/tmail/version.rb +39 -0
  166. data/vendor/tmail.rb +13 -0
  167. metadata +282 -0
@@ -0,0 +1,3 @@
1
+ #:stopdoc:
2
+ require 'tmail/mailbox'
3
+ #:startdoc:
@@ -0,0 +1,578 @@
1
+ =begin rdoc
2
+
3
+ = Mail class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+
33
+
34
+ require 'tmail/interface'
35
+ require 'tmail/encode'
36
+ require 'tmail/header'
37
+ require 'tmail/port'
38
+ require 'tmail/config'
39
+ require 'tmail/utils'
40
+ require 'tmail/attachments'
41
+ require 'tmail/quoting'
42
+ require 'socket'
43
+
44
+ module TMail
45
+
46
+ # == Mail Class
47
+ #
48
+ # Accessing a TMail object done via the TMail::Mail class. As email can be fairly complex
49
+ # creatures, you will find a large amount of accessor and setter methods in this class!
50
+ #
51
+ # Most of the below methods handle the header, in fact, what TMail does best is handle the
52
+ # header of the email object. There are only a few methods that deal directly with the body
53
+ # of the email, such as base64_encode and base64_decode.
54
+ #
55
+ # === Using TMail inside your code
56
+ #
57
+ # The usual way is to install the gem (see the {README}[link:/README] on how to do this) and
58
+ # then put at the top of your class:
59
+ #
60
+ # require 'tmail'
61
+ #
62
+ # You can then create a new TMail object in your code with:
63
+ #
64
+ # @email = TMail::Mail.new
65
+ #
66
+ # Or if you have an email as a string, you can initialize a new TMail::Mail object and get it
67
+ # to parse that string for you like so:
68
+ #
69
+ # @email = TMail::Mail.parse(email_text)
70
+ #
71
+ # You can also read a single email off the disk, for example:
72
+ #
73
+ # @email = TMail::Mail.load('filename.txt')
74
+ #
75
+ # Also, you can read a mailbox (usual unix mbox format) and end up with an array of TMail
76
+ # objects by doing something like this:
77
+ #
78
+ # # Note, we pass true as the last variable to open the mailbox read only
79
+ # mailbox = TMail::UNIXMbox.new("mailbox", nil, true)
80
+ # @emails = []
81
+ # mailbox.each_port { |m| @emails << TMail::Mail.new(m) }
82
+ #
83
+ class Mail
84
+
85
+ class << self
86
+
87
+ # Opens an email that has been saved out as a file by itself.
88
+ #
89
+ # This function will read a file non-destructively and then parse
90
+ # the contents and return a TMail::Mail object.
91
+ #
92
+ # Does not handle multiple email mailboxes (like a unix mbox) for that
93
+ # use the TMail::UNIXMbox class.
94
+ #
95
+ # Example:
96
+ # mail = TMail::Mail.load('filename')
97
+ #
98
+ def load( fname )
99
+ new(FilePort.new(fname))
100
+ end
101
+
102
+ alias load_from load
103
+ alias loadfrom load
104
+
105
+ # Parses an email from the supplied string and returns a TMail::Mail
106
+ # object.
107
+ #
108
+ # Example:
109
+ # require 'rubygems'; require 'tmail'
110
+ # email_string =<<HEREDOC
111
+ # To: mikel@lindsaar.net
112
+ # From: mikel@me.com
113
+ # Subject: This is a short Email
114
+ #
115
+ # Hello there Mikel!
116
+ #
117
+ # HEREDOC
118
+ # mail = TMail::Mail.parse(email_string)
119
+ # #=> #<TMail::Mail port=#<TMail::StringPort:id=0xa30ac0> bodyport=nil>
120
+ # mail.body
121
+ # #=> "Hello there Mikel!\n\n"
122
+ def parse( str )
123
+ new(StringPort.new(str))
124
+ end
125
+
126
+ end
127
+
128
+ def initialize( port = nil, conf = DEFAULT_CONFIG ) #:nodoc:
129
+ @port = port || StringPort.new
130
+ @config = Config.to_config(conf)
131
+
132
+ @header = {}
133
+ @body_port = nil
134
+ @body_parsed = false
135
+ @epilogue = ''
136
+ @parts = []
137
+
138
+ @port.ropen {|f|
139
+ parse_header f
140
+ parse_body f unless @port.reproducible?
141
+ }
142
+ end
143
+
144
+ # Provides access to the port this email is using to hold it's data
145
+ #
146
+ # Example:
147
+ # mail = TMail::Mail.parse(email_string)
148
+ # mail.port
149
+ # #=> #<TMail::StringPort:id=0xa2c952>
150
+ attr_reader :port
151
+
152
+ def inspect
153
+ "\#<#{self.class} port=#{@port.inspect} bodyport=#{@body_port.inspect}>"
154
+ end
155
+
156
+ #
157
+ # to_s interfaces
158
+ #
159
+
160
+ public
161
+
162
+ include StrategyInterface
163
+
164
+ def write_back( eol = "\n", charset = 'e' )
165
+ parse_body
166
+ @port.wopen {|stream| encoded eol, charset, stream }
167
+ end
168
+
169
+ def accept( strategy )
170
+ with_multipart_encoding(strategy) {
171
+ ordered_each do |name, field|
172
+ next if field.empty?
173
+ strategy.header_name canonical(name)
174
+ field.accept strategy
175
+ strategy.puts
176
+ end
177
+ strategy.puts
178
+ body_port().ropen {|r|
179
+ strategy.write r.read
180
+ }
181
+ }
182
+ end
183
+
184
+ private
185
+
186
+ def canonical( name )
187
+ name.split(/-/).map {|s| s.capitalize }.join('-')
188
+ end
189
+
190
+ def with_multipart_encoding( strategy )
191
+ if parts().empty? # DO NOT USE @parts
192
+ yield
193
+
194
+ else
195
+ bound = ::TMail.new_boundary
196
+ if @header.key? 'content-type'
197
+ @header['content-type'].params['boundary'] = bound
198
+ else
199
+ store 'Content-Type', %<multipart/mixed; boundary="#{bound}">
200
+ end
201
+
202
+ yield
203
+
204
+ parts().each do |tm|
205
+ strategy.puts
206
+ strategy.puts '--' + bound
207
+ tm.accept strategy
208
+ end
209
+ strategy.puts
210
+ strategy.puts '--' + bound + '--'
211
+ strategy.write epilogue()
212
+ end
213
+ end
214
+
215
+ ###
216
+ ### header
217
+ ###
218
+
219
+ public
220
+
221
+ ALLOW_MULTIPLE = {
222
+ 'received' => true,
223
+ 'resent-date' => true,
224
+ 'resent-from' => true,
225
+ 'resent-sender' => true,
226
+ 'resent-to' => true,
227
+ 'resent-cc' => true,
228
+ 'resent-bcc' => true,
229
+ 'resent-message-id' => true,
230
+ 'comments' => true,
231
+ 'keywords' => true
232
+ }
233
+ USE_ARRAY = ALLOW_MULTIPLE
234
+
235
+ def header
236
+ @header.dup
237
+ end
238
+
239
+ # Returns a TMail::AddressHeader object of the field you are querying.
240
+ # Examples:
241
+ # @mail['from'] #=> #<TMail::AddressHeader "mikel@test.com.au">
242
+ # @mail['to'] #=> #<TMail::AddressHeader "mikel@test.com.au">
243
+ #
244
+ # You can get the string value of this by passing "to_s" to the query:
245
+ # Example:
246
+ # @mail['to'].to_s #=> "mikel@test.com.au"
247
+ def []( key )
248
+ @header[key.downcase]
249
+ end
250
+
251
+ def sub_header(key, param)
252
+ (hdr = self[key]) ? hdr[param] : nil
253
+ end
254
+
255
+ alias fetch []
256
+
257
+ # Allows you to set or delete TMail header objects at will.
258
+ # Examples:
259
+ # @mail = TMail::Mail.new
260
+ # @mail['to'].to_s # => 'mikel@test.com.au'
261
+ # @mail['to'] = 'mikel@elsewhere.org'
262
+ # @mail['to'].to_s # => 'mikel@elsewhere.org'
263
+ # @mail.encoded # => "To: mikel@elsewhere.org\r\n\r\n"
264
+ # @mail['to'] = nil
265
+ # @mail['to'].to_s # => nil
266
+ # @mail.encoded # => "\r\n"
267
+ #
268
+ # Note: setting mail[] = nil actually deletes the header field in question from the object,
269
+ # it does not just set the value of the hash to nil
270
+ def []=( key, val )
271
+ dkey = key.downcase
272
+
273
+ if val.nil?
274
+ @header.delete dkey
275
+ return nil
276
+ end
277
+
278
+ case val
279
+ when String
280
+ header = new_hf(key, val)
281
+ when HeaderField
282
+ ;
283
+ when Array
284
+ ALLOW_MULTIPLE.include? dkey or
285
+ raise ArgumentError, "#{key}: Header must not be multiple"
286
+ @header[dkey] = val
287
+ return val
288
+ else
289
+ header = new_hf(key, val.to_s)
290
+ end
291
+ if ALLOW_MULTIPLE.include? dkey
292
+ (@header[dkey] ||= []).push header
293
+ else
294
+ @header[dkey] = header
295
+ end
296
+
297
+ val
298
+ end
299
+
300
+ alias store []=
301
+
302
+ # Allows you to loop through each header in the TMail::Mail object in a block
303
+ # Example:
304
+ # @mail['to'] = 'mikel@elsewhere.org'
305
+ # @mail['from'] = 'me@me.com'
306
+ # @mail.each_header { |k,v| puts "#{k} = #{v}" }
307
+ # # => from = me@me.com
308
+ # # => to = mikel@elsewhere.org
309
+ def each_header
310
+ @header.each do |key, val|
311
+ [val].flatten.each {|v| yield key, v }
312
+ end
313
+ end
314
+
315
+ alias each_pair each_header
316
+
317
+ def each_header_name( &block )
318
+ @header.each_key(&block)
319
+ end
320
+
321
+ alias each_key each_header_name
322
+
323
+ def each_field( &block )
324
+ @header.values.flatten.each(&block)
325
+ end
326
+
327
+ alias each_value each_field
328
+
329
+ FIELD_ORDER = %w(
330
+ return-path received
331
+ resent-date resent-from resent-sender resent-to
332
+ resent-cc resent-bcc resent-message-id
333
+ date from sender reply-to to cc bcc
334
+ message-id in-reply-to references
335
+ subject comments keywords
336
+ mime-version content-type content-transfer-encoding
337
+ content-disposition content-description
338
+ )
339
+
340
+ def ordered_each
341
+ list = @header.keys
342
+ FIELD_ORDER.each do |name|
343
+ if list.delete(name)
344
+ [@header[name]].flatten.each {|v| yield name, v }
345
+ end
346
+ end
347
+ list.each do |name|
348
+ [@header[name]].flatten.each {|v| yield name, v }
349
+ end
350
+ end
351
+
352
+ def clear
353
+ @header.clear
354
+ end
355
+
356
+ def delete( key )
357
+ @header.delete key.downcase
358
+ end
359
+
360
+ def delete_if
361
+ @header.delete_if do |key,val|
362
+ if Array === val
363
+ val.delete_if {|v| yield key, v }
364
+ val.empty?
365
+ else
366
+ yield key, val
367
+ end
368
+ end
369
+ end
370
+
371
+ def keys
372
+ @header.keys
373
+ end
374
+
375
+ def key?( key )
376
+ @header.key? key.downcase
377
+ end
378
+
379
+ def values_at( *args )
380
+ args.map {|k| @header[k.downcase] }.flatten
381
+ end
382
+
383
+ alias indexes values_at
384
+ alias indices values_at
385
+
386
+ private
387
+
388
+ def parse_header( f )
389
+ name = field = nil
390
+ unixfrom = nil
391
+
392
+ while line = f.gets
393
+ case line
394
+ when /\A[ \t]/ # continue from prev line
395
+ raise SyntaxError, 'mail is began by space' unless field
396
+ field << ' ' << line.strip
397
+
398
+ when /\A([^\: \t]+):\s*/ # new header line
399
+ add_hf name, field if field
400
+ name = $1
401
+ field = $' #.strip
402
+
403
+ when /\A\-*\s*\z/ # end of header
404
+ add_hf name, field if field
405
+ name = field = nil
406
+ break
407
+
408
+ when /\AFrom (\S+)/
409
+ unixfrom = $1
410
+
411
+ when /^charset=.*/
412
+
413
+ else
414
+ raise SyntaxError, "wrong mail header: '#{line.inspect}'"
415
+ end
416
+ end
417
+ add_hf name, field if name
418
+
419
+ if unixfrom
420
+ add_hf 'Return-Path', "<#{unixfrom}>" unless @header['return-path']
421
+ end
422
+ end
423
+
424
+ def add_hf( name, field )
425
+ key = name.downcase
426
+ field = new_hf(name, field)
427
+
428
+ if ALLOW_MULTIPLE.include? key
429
+ (@header[key] ||= []).push field
430
+ else
431
+ @header[key] = field
432
+ end
433
+ end
434
+
435
+ def new_hf( name, field )
436
+ HeaderField.new(name, field, @config)
437
+ end
438
+
439
+ ###
440
+ ### body
441
+ ###
442
+
443
+ public
444
+
445
+ def body_port
446
+ parse_body
447
+ @body_port
448
+ end
449
+
450
+ def each( &block )
451
+ body_port().ropen {|f| f.each(&block) }
452
+ end
453
+
454
+ def quoted_body
455
+ body_port.ropen {|f| return f.read }
456
+ end
457
+
458
+ def quoted_body= str
459
+ body_port.wopen { |f| f.write str }
460
+ str
461
+ end
462
+
463
+ def body=( str )
464
+ # Sets the body of the email to a new (encoded) string.
465
+ #
466
+ # We also reparses the email if the body is ever reassigned, this is a performance hit, however when
467
+ # you assign the body, you usually want to be able to make sure that you can access the attachments etc.
468
+ #
469
+ # Usage:
470
+ #
471
+ # mail.body = "Hello, this is\nthe body text"
472
+ # # => "Hello, this is\nthe body"
473
+ # mail.body
474
+ # # => "Hello, this is\nthe body"
475
+ @body_parsed = false
476
+ parse_body(StringInput.new(str))
477
+ parse_body
478
+ @body_port.wopen {|f| f.write str }
479
+ str
480
+ end
481
+
482
+ alias preamble quoted_body
483
+ alias preamble= quoted_body=
484
+
485
+ def epilogue
486
+ parse_body
487
+ @epilogue.dup
488
+ end
489
+
490
+ def epilogue=( str )
491
+ parse_body
492
+ @epilogue = str
493
+ str
494
+ end
495
+
496
+ def parts
497
+ parse_body
498
+ @parts
499
+ end
500
+
501
+ def each_part( &block )
502
+ parts().each(&block)
503
+ end
504
+
505
+ # Returns true if the content type of this part of the email is
506
+ # a disposition attachment
507
+ def disposition_is_attachment?
508
+ (self['content-disposition'] && self['content-disposition'].disposition == "attachment")
509
+ end
510
+
511
+ # Returns true if this part's content main type is text, else returns false.
512
+ # By main type is meant "text/plain" is text. "text/html" is text
513
+ def content_type_is_text?
514
+ self.header['content-type'] && (self.header['content-type'].main_type != "text")
515
+ end
516
+
517
+ private
518
+
519
+ def parse_body( f = nil )
520
+ return if @body_parsed
521
+ if f
522
+ parse_body_0 f
523
+ else
524
+ @port.ropen {|f|
525
+ skip_header f
526
+ parse_body_0 f
527
+ }
528
+ end
529
+ @body_parsed = true
530
+ end
531
+
532
+ def skip_header( f )
533
+ while line = f.gets
534
+ return if /\A[\r\n]*\z/ === line
535
+ end
536
+ end
537
+
538
+ def parse_body_0( f )
539
+ if multipart?
540
+ read_multipart f
541
+ else
542
+ @body_port = @config.new_body_port(self)
543
+ @body_port.wopen {|w|
544
+ w.write f.read
545
+ }
546
+ end
547
+ end
548
+
549
+ def read_multipart( src )
550
+ bound = @header['content-type'].params['boundary']
551
+ is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/
552
+ lastbound = "--#{bound}--"
553
+
554
+ ports = [ @config.new_preamble_port(self) ]
555
+ begin
556
+ f = ports.last.wopen
557
+ while line = src.gets
558
+ if is_sep === line
559
+ f.close
560
+ break if line.strip == lastbound
561
+ ports.push @config.new_part_port(self)
562
+ f = ports.last.wopen
563
+ else
564
+ f << line
565
+ end
566
+ end
567
+ @epilogue = (src.read || '')
568
+ ensure
569
+ f.close if f and not f.closed?
570
+ end
571
+
572
+ @body_port = ports.shift
573
+ @parts = ports.map {|p| self.class.new(p, @config) }
574
+ end
575
+
576
+ end # class Mail
577
+
578
+ end # module TMail