daemon-kit 0.1.7 → 0.1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/Configuration.txt +58 -0
  2. data/History.txt +24 -0
  3. data/Manifest.txt +50 -2
  4. data/PostInstall.txt +1 -1
  5. data/README.rdoc +7 -9
  6. data/Rakefile +2 -4
  7. data/TODO.txt +6 -5
  8. data/app_generators/daemon_kit/daemon_kit_generator.rb +5 -0
  9. data/app_generators/daemon_kit/templates/Rakefile +3 -1
  10. data/app_generators/daemon_kit/templates/bin/daemon.erb +1 -1
  11. data/app_generators/daemon_kit/templates/config/arguments.rb +12 -0
  12. data/app_generators/daemon_kit/templates/config/boot.rb +2 -2
  13. data/app_generators/daemon_kit/templates/script/console +3 -0
  14. data/app_generators/daemon_kit/templates/script/destroy +14 -0
  15. data/app_generators/daemon_kit/templates/script/generate +14 -0
  16. data/daemon_generators/amqp/templates/config/amqp.yml +5 -5
  17. data/daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb +4 -23
  18. data/daemon_generators/deploy_capistrano/templates/USAGE +10 -0
  19. data/daemon_generators/deploy_capistrano/templates/config/deploy.rb +3 -1
  20. data/lib/daemon_kit.rb +33 -5
  21. data/lib/daemon_kit/amqp.rb +2 -1
  22. data/lib/daemon_kit/application.rb +136 -11
  23. data/lib/daemon_kit/arguments.rb +151 -0
  24. data/lib/daemon_kit/commands/console.rb +38 -0
  25. data/lib/daemon_kit/config.rb +1 -0
  26. data/lib/daemon_kit/console_daemon.rb +2 -0
  27. data/lib/daemon_kit/core_ext.rb +1 -0
  28. data/lib/daemon_kit/core_ext/string.rb +22 -0
  29. data/lib/daemon_kit/deployment/capistrano.rb +6 -9
  30. data/lib/daemon_kit/error_handlers/mail.rb +52 -15
  31. data/lib/daemon_kit/initializer.rb +95 -41
  32. data/lib/daemon_kit/pid_file.rb +61 -0
  33. data/lib/daemon_kit/tasks/environment.rake +5 -4
  34. data/lib/daemon_kit/tasks/framework.rake +15 -1
  35. data/lib/daemon_kit/tasks/god.rake +62 -0
  36. data/lib/daemon_kit/tasks/log.rake +8 -0
  37. data/lib/daemon_kit/tasks/monit.rake +29 -0
  38. data/spec/argument_spec.rb +51 -0
  39. data/spec/config_spec.rb +77 -0
  40. data/spec/daemon_kit_spec.rb +2 -2
  41. data/spec/error_handlers_spec.rb +23 -0
  42. data/spec/fixtures/env.yml +15 -0
  43. data/spec/fixtures/noenv.yml +4 -0
  44. data/spec/initializer_spec.rb +4 -3
  45. data/spec/spec_helper.rb +8 -11
  46. data/templates/god/god.erb +69 -0
  47. data/templates/monit/monit.erb +14 -0
  48. data/test/test_daemon-kit_generator.rb +6 -1
  49. data/test/test_deploy_capistrano_generator.rb +1 -2
  50. data/vendor/tmail-1.2.3/tmail.rb +5 -0
  51. data/vendor/tmail-1.2.3/tmail/address.rb +426 -0
  52. data/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
  53. data/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
  54. data/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
  55. data/vendor/tmail-1.2.3/tmail/config.rb +67 -0
  56. data/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
  57. data/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
  58. data/vendor/tmail-1.2.3/tmail/header.rb +960 -0
  59. data/vendor/tmail-1.2.3/tmail/index.rb +9 -0
  60. data/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
  61. data/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
  62. data/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
  63. data/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
  64. data/vendor/tmail-1.2.3/tmail/main.rb +6 -0
  65. data/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
  66. data/vendor/tmail-1.2.3/tmail/net.rb +248 -0
  67. data/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
  68. data/vendor/tmail-1.2.3/tmail/parser.rb +1476 -0
  69. data/vendor/tmail-1.2.3/tmail/port.rb +379 -0
  70. data/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
  71. data/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
  72. data/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
  73. data/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
  74. data/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
  75. data/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
  76. data/vendor/tmail-1.2.3/tmail/version.rb +39 -0
  77. data/vendor/tmail.rb +13 -0
  78. metadata +57 -18
  79. data/daemon_generators/deploy_capistrano/USAGE +0 -5
  80. data/lib/daemon_kit/patches/force_kill_wait.rb +0 -120
@@ -0,0 +1,46 @@
1
+ =begin rdoc
2
+
3
+ = Attachment handling file
4
+
5
+ =end
6
+
7
+ require 'stringio'
8
+
9
+ module TMail
10
+ class Attachment < StringIO
11
+ attr_accessor :original_filename, :content_type
12
+ end
13
+
14
+ class Mail
15
+ def has_attachments?
16
+ multipart? && parts.any? { |part| attachment?(part) }
17
+ end
18
+
19
+ def attachment?(part)
20
+ part.disposition_is_attachment? || part.content_type_is_text?
21
+ end
22
+
23
+ def attachments
24
+ if multipart?
25
+ parts.collect { |part|
26
+ if part.multipart?
27
+ part.attachments
28
+ elsif attachment?(part)
29
+ content = part.body # unquoted automatically by TMail#body
30
+ file_name = (part['content-location'] &&
31
+ part['content-location'].body) ||
32
+ part.sub_header("content-type", "name") ||
33
+ part.sub_header("content-disposition", "filename")
34
+
35
+ next if file_name.blank? || content.blank?
36
+
37
+ attachment = Attachment.new(content)
38
+ attachment.original_filename = file_name.strip
39
+ attachment.content_type = part.content_type
40
+ attachment
41
+ end
42
+ }.flatten.compact
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ #--
2
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
24
+ # with permission of Minero Aoki.
25
+ #++
26
+ #:stopdoc:
27
+ module TMail
28
+ module Base64
29
+
30
+ module_function
31
+
32
+ def folding_encode( str, eol = "\n", limit = 60 )
33
+ [str].pack('m')
34
+ end
35
+
36
+ def encode( str )
37
+ [str].pack('m').tr( "\r\n", '' )
38
+ end
39
+
40
+ def decode( str, strict = false )
41
+ str.unpack('m').first
42
+ end
43
+
44
+ end
45
+ end
46
+ #:startdoc:
@@ -0,0 +1,41 @@
1
+ #:stopdoc:
2
+ unless Enumerable.method_defined?(:map)
3
+ module Enumerable #:nodoc:
4
+ alias map collect
5
+ end
6
+ end
7
+
8
+ unless Enumerable.method_defined?(:select)
9
+ module Enumerable #:nodoc:
10
+ alias select find_all
11
+ end
12
+ end
13
+
14
+ unless Enumerable.method_defined?(:reject)
15
+ module Enumerable #:nodoc:
16
+ def reject
17
+ result = []
18
+ each do |i|
19
+ result.push i unless yield(i)
20
+ end
21
+ result
22
+ end
23
+ end
24
+ end
25
+
26
+ unless Enumerable.method_defined?(:sort_by)
27
+ module Enumerable #:nodoc:
28
+ def sort_by
29
+ map {|i| [yield(i), i] }.sort.map {|val, i| i }
30
+ end
31
+ end
32
+ end
33
+
34
+ unless File.respond_to?(:read)
35
+ def File.read(fname) #:nodoc:
36
+ File.open(fname) {|f|
37
+ return f.read
38
+ }
39
+ end
40
+ end
41
+ #:startdoc:
@@ -0,0 +1,67 @@
1
+ #--
2
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
24
+ # with permission of Minero Aoki.
25
+ #++
26
+ #:stopdoc:
27
+ module TMail
28
+
29
+ class Config
30
+
31
+ def initialize( strict )
32
+ @strict_parse = strict
33
+ @strict_base64decode = strict
34
+ end
35
+
36
+ def strict_parse?
37
+ @strict_parse
38
+ end
39
+
40
+ attr_writer :strict_parse
41
+
42
+ def strict_base64decode?
43
+ @strict_base64decode
44
+ end
45
+
46
+ attr_writer :strict_base64decode
47
+
48
+ def new_body_port( mail )
49
+ StringPort.new
50
+ end
51
+
52
+ alias new_preamble_port new_body_port
53
+ alias new_part_port new_body_port
54
+
55
+ end
56
+
57
+ DEFAULT_CONFIG = Config.new(false)
58
+ DEFAULT_STRICT_CONFIG = Config.new(true)
59
+
60
+ def Config.to_config( arg )
61
+ return DEFAULT_STRICT_CONFIG if arg == true
62
+ return DEFAULT_CONFIG if arg == false
63
+ arg or DEFAULT_CONFIG
64
+ end
65
+
66
+ end
67
+ #:startdoc:
@@ -0,0 +1,63 @@
1
+ #:stopdoc:
2
+ unless Object.respond_to?(:blank?)
3
+ class Object
4
+ # Check first to see if we are in a Rails environment, no need to
5
+ # define these methods if we are
6
+
7
+ # An object is blank if it's nil, empty, or a whitespace string.
8
+ # For example, "", " ", nil, [], and {} are blank.
9
+ #
10
+ # This simplifies
11
+ # if !address.nil? && !address.empty?
12
+ # to
13
+ # if !address.blank?
14
+ def blank?
15
+ if respond_to?(:empty?) && respond_to?(:strip)
16
+ empty? or strip.empty?
17
+ elsif respond_to?(:empty?)
18
+ empty?
19
+ else
20
+ !self
21
+ end
22
+ end
23
+ end
24
+
25
+ class NilClass
26
+ def blank?
27
+ true
28
+ end
29
+ end
30
+
31
+ class FalseClass
32
+ def blank?
33
+ true
34
+ end
35
+ end
36
+
37
+ class TrueClass
38
+ def blank?
39
+ false
40
+ end
41
+ end
42
+
43
+ class Array
44
+ alias_method :blank?, :empty?
45
+ end
46
+
47
+ class Hash
48
+ alias_method :blank?, :empty?
49
+ end
50
+
51
+ class String
52
+ def blank?
53
+ empty? || strip.empty?
54
+ end
55
+ end
56
+
57
+ class Numeric
58
+ def blank?
59
+ false
60
+ end
61
+ end
62
+ end
63
+ #:startdoc:
@@ -0,0 +1,581 @@
1
+ #--
2
+ # = COPYRIGHT:
3
+ #
4
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
26
+ # with permission of Minero Aoki.
27
+ #++
28
+ #:stopdoc:
29
+ require 'nkf'
30
+ require 'tmail/base64'
31
+ require 'tmail/stringio'
32
+ require 'tmail/utils'
33
+ #:startdoc:
34
+
35
+
36
+ module TMail
37
+
38
+ #:stopdoc:
39
+ class << self
40
+ attr_accessor :KCODE
41
+ end
42
+ self.KCODE = 'NONE'
43
+
44
+ module StrategyInterface
45
+
46
+ def create_dest( obj )
47
+ case obj
48
+ when nil
49
+ StringOutput.new
50
+ when String
51
+ StringOutput.new(obj)
52
+ when IO, StringOutput
53
+ obj
54
+ else
55
+ raise TypeError, 'cannot handle this type of object for dest'
56
+ end
57
+ end
58
+ module_function :create_dest
59
+
60
+ #:startdoc:
61
+ # Returns the TMail object encoded and ready to be sent via SMTP etc.
62
+ # You should call this before you are packaging up your email to
63
+ # correctly escape all the values that need escaping in the email, line
64
+ # wrap the email etc.
65
+ #
66
+ # It is also a good idea to call this before you marshal or serialize
67
+ # a TMail object.
68
+ #
69
+ # For Example:
70
+ #
71
+ # email = TMail::Load(my_email_file)
72
+ # email_to_send = email.encoded
73
+ def encoded( eol = "\r\n", charset = 'j', dest = nil )
74
+ accept_strategy Encoder, eol, charset, dest
75
+ end
76
+
77
+ # Returns the TMail object decoded and ready to be used by you, your
78
+ # program etc.
79
+ #
80
+ # You should call this before you are packaging up your email to
81
+ # correctly escape all the values that need escaping in the email, line
82
+ # wrap the email etc.
83
+ #
84
+ # For Example:
85
+ #
86
+ # email = TMail::Load(my_email_file)
87
+ # email_to_send = email.encoded
88
+ def decoded( eol = "\n", charset = 'e', dest = nil )
89
+ # Turn the E-Mail into a string and return it with all
90
+ # encoded characters decoded. alias for to_s
91
+ accept_strategy Decoder, eol, charset, dest
92
+ end
93
+
94
+ alias to_s decoded
95
+
96
+ def accept_strategy( klass, eol, charset, dest = nil ) #:nodoc:
97
+ dest ||= ''
98
+ accept klass.new( create_dest(dest), charset, eol )
99
+ dest
100
+ end
101
+
102
+ end
103
+
104
+ #:stopdoc:
105
+
106
+ ###
107
+ ### MIME B encoding decoder
108
+ ###
109
+
110
+ class Decoder
111
+
112
+ include TextUtils
113
+
114
+ encoded = '=\?(?:iso-2022-jp|euc-jp|shift_jis)\?[QB]\?[a-z0-9+/=]+\?='
115
+ ENCODED_WORDS = /#{encoded}(?:\s+#{encoded})*/i
116
+
117
+ OUTPUT_ENCODING = {
118
+ 'EUC' => 'e',
119
+ 'SJIS' => 's',
120
+ }
121
+
122
+ def self.decode( str, encoding = nil )
123
+ encoding ||= (OUTPUT_ENCODING[TMail.KCODE] || 'j')
124
+ opt = '-mS' + encoding
125
+ str.gsub(ENCODED_WORDS) {|s| NKF.nkf(opt, s) }
126
+ end
127
+
128
+ def initialize( dest, encoding = nil, eol = "\n" )
129
+ @f = StrategyInterface.create_dest(dest)
130
+ @encoding = (/\A[ejs]/ === encoding) ? encoding[0,1] : nil
131
+ @eol = eol
132
+ end
133
+
134
+ def decode( str )
135
+ self.class.decode(str, @encoding)
136
+ end
137
+ private :decode
138
+
139
+ def terminate
140
+ end
141
+
142
+ def header_line( str )
143
+ @f << decode(str)
144
+ end
145
+
146
+ def header_name( nm )
147
+ @f << nm << ': '
148
+ end
149
+
150
+ def header_body( str )
151
+ @f << decode(str)
152
+ end
153
+
154
+ def space
155
+ @f << ' '
156
+ end
157
+
158
+ alias spc space
159
+
160
+ def lwsp( str )
161
+ @f << str
162
+ end
163
+
164
+ def meta( str )
165
+ @f << str
166
+ end
167
+
168
+ def text( str )
169
+ @f << decode(str)
170
+ end
171
+
172
+ def phrase( str )
173
+ @f << quote_phrase(decode(str))
174
+ end
175
+
176
+ def kv_pair( k, v )
177
+ v = dquote(v) unless token_safe?(v)
178
+ @f << k << '=' << v
179
+ end
180
+
181
+ def puts( str = nil )
182
+ @f << str if str
183
+ @f << @eol
184
+ end
185
+
186
+ def write( str )
187
+ @f << str
188
+ end
189
+
190
+ end
191
+
192
+
193
+ ###
194
+ ### MIME B-encoding encoder
195
+ ###
196
+
197
+ #
198
+ # FIXME: This class can handle only (euc-jp/shift_jis -> iso-2022-jp).
199
+ #
200
+ class Encoder
201
+
202
+ include TextUtils
203
+
204
+ BENCODE_DEBUG = false unless defined?(BENCODE_DEBUG)
205
+
206
+ def Encoder.encode( str )
207
+ e = new()
208
+ e.header_body str
209
+ e.terminate
210
+ e.dest.string
211
+ end
212
+
213
+ SPACER = "\t"
214
+ MAX_LINE_LEN = 78
215
+ RFC_2822_MAX_LENGTH = 998
216
+
217
+ OPTIONS = {
218
+ 'EUC' => '-Ej -m0',
219
+ 'SJIS' => '-Sj -m0',
220
+ 'UTF8' => nil, # FIXME
221
+ 'NONE' => nil
222
+ }
223
+
224
+ def initialize( dest = nil, encoding = nil, eol = "\r\n", limit = nil )
225
+ @f = StrategyInterface.create_dest(dest)
226
+ @opt = OPTIONS[TMail.KCODE]
227
+ @eol = eol
228
+ @folded = false
229
+ @preserve_quotes = true
230
+ reset
231
+ end
232
+
233
+ def preserve_quotes=( bool )
234
+ @preserve_quotes
235
+ end
236
+
237
+ def preserve_quotes
238
+ @preserve_quotes
239
+ end
240
+
241
+ def normalize_encoding( str )
242
+ if @opt
243
+ then NKF.nkf(@opt, str)
244
+ else str
245
+ end
246
+ end
247
+
248
+ def reset
249
+ @text = ''
250
+ @lwsp = ''
251
+ @curlen = 0
252
+ end
253
+
254
+ def terminate
255
+ add_lwsp ''
256
+ reset
257
+ end
258
+
259
+ def dest
260
+ @f
261
+ end
262
+
263
+ def puts( str = nil )
264
+ @f << str if str
265
+ @f << @eol
266
+ end
267
+
268
+ def write( str )
269
+ @f << str
270
+ end
271
+
272
+ #
273
+ # add
274
+ #
275
+
276
+ def header_line( line )
277
+ scanadd line
278
+ end
279
+
280
+ def header_name( name )
281
+ add_text name.split(/-/).map {|i| i.capitalize }.join('-')
282
+ add_text ':'
283
+ add_lwsp ' '
284
+ end
285
+
286
+ def header_body( str )
287
+ scanadd normalize_encoding(str)
288
+ end
289
+
290
+ def space
291
+ add_lwsp ' '
292
+ end
293
+
294
+ alias spc space
295
+
296
+ def lwsp( str )
297
+ add_lwsp str.sub(/[\r\n]+[^\r\n]*\z/, '')
298
+ end
299
+
300
+ def meta( str )
301
+ add_text str
302
+ end
303
+
304
+ def text( str )
305
+ scanadd normalize_encoding(str)
306
+ end
307
+
308
+ def phrase( str )
309
+ str = normalize_encoding(str)
310
+ if CONTROL_CHAR === str
311
+ scanadd str
312
+ else
313
+ add_text quote_phrase(str)
314
+ end
315
+ end
316
+
317
+ # FIXME: implement line folding
318
+ #
319
+ def kv_pair( k, v )
320
+ return if v.nil?
321
+ v = normalize_encoding(v)
322
+ if token_safe?(v)
323
+ add_text k + '=' + v
324
+ elsif not CONTROL_CHAR === v
325
+ add_text k + '=' + quote_token(v)
326
+ else
327
+ # apply RFC2231 encoding
328
+ kv = k + '*=' + "iso-2022-jp'ja'" + encode_value(v)
329
+ add_text kv
330
+ end
331
+ end
332
+
333
+ def encode_value( str )
334
+ str.gsub(TOKEN_UNSAFE) {|s| '%%%02x' % s[0] }
335
+ end
336
+
337
+ private
338
+
339
+ def scanadd( str, force = false )
340
+ types = ''
341
+ strs = []
342
+ if str.respond_to?(:encoding)
343
+ enc = str.encoding
344
+ str.force_encoding(Encoding::ASCII_8BIT)
345
+ end
346
+ until str.empty?
347
+ if m = /\A[^\e\t\r\n ]+/.match(str)
348
+ types << (force ? 'j' : 'a')
349
+ if str.respond_to?(:encoding)
350
+ strs.push m[0].force_encoding(enc)
351
+ else
352
+ strs.push m[0]
353
+ end
354
+ elsif m = /\A[\t\r\n ]+/.match(str)
355
+ types << 's'
356
+ if str.respond_to?(:encoding)
357
+ strs.push m[0].force_encoding(enc)
358
+ else
359
+ strs.push m[0]
360
+ end
361
+
362
+ elsif m = /\A\e../.match(str)
363
+ esc = m[0]
364
+ str = m.post_match
365
+ if esc != "\e(B" and m = /\A[^\e]+/.match(str)
366
+ types << 'j'
367
+ if str.respond_to?(:encoding)
368
+ strs.push m[0].force_encoding(enc)
369
+ else
370
+ strs.push m[0]
371
+ end
372
+ end
373
+
374
+ else
375
+ raise 'TMail FATAL: encoder scan fail'
376
+ end
377
+ (str = m.post_match) unless m.nil?
378
+ end
379
+
380
+ do_encode types, strs
381
+ end
382
+
383
+ def do_encode( types, strs )
384
+ #
385
+ # result : (A|E)(S(A|E))*
386
+ # E : W(SW)*
387
+ # W : (J|A)+ but must contain J # (J|A)*J(J|A)*
388
+ # A : <<A character string not to be encoded>>
389
+ # J : <<A character string to be encoded>>
390
+ # S : <<LWSP>>
391
+ #
392
+ # An encoding unit is `E'.
393
+ # Input (parameter `types') is (J|A)(J|A|S)*(J|A)
394
+ #
395
+ if BENCODE_DEBUG
396
+ puts
397
+ puts '-- do_encode ------------'
398
+ puts types.split(//).join(' ')
399
+ p strs
400
+ end
401
+
402
+ e = /[ja]*j[ja]*(?:s[ja]*j[ja]*)*/
403
+
404
+ while m = e.match(types)
405
+ pre = m.pre_match
406
+ concat_A_S pre, strs[0, pre.size] unless pre.empty?
407
+ concat_E m[0], strs[m.begin(0) ... m.end(0)]
408
+ types = m.post_match
409
+ strs.slice! 0, m.end(0)
410
+ end
411
+ concat_A_S types, strs
412
+ end
413
+
414
+ def concat_A_S( types, strs )
415
+ if RUBY_VERSION < '1.9'
416
+ a = ?a; s = ?s
417
+ else
418
+ a = 'a'.ord; s = 's'.ord
419
+ end
420
+ i = 0
421
+ types.each_byte do |t|
422
+ case t
423
+ when a then add_text strs[i]
424
+ when s then add_lwsp strs[i]
425
+ else
426
+ raise "TMail FATAL: unknown flag: #{t.chr}"
427
+ end
428
+ i += 1
429
+ end
430
+ end
431
+
432
+ METHOD_ID = {
433
+ ?j => :extract_J,
434
+ ?e => :extract_E,
435
+ ?a => :extract_A,
436
+ ?s => :extract_S
437
+ }
438
+
439
+ def concat_E( types, strs )
440
+ if BENCODE_DEBUG
441
+ puts '---- concat_E'
442
+ puts "types=#{types.split(//).join(' ')}"
443
+ puts "strs =#{strs.inspect}"
444
+ end
445
+
446
+ flush() unless @text.empty?
447
+
448
+ chunk = ''
449
+ strs.each_with_index do |s,i|
450
+ mid = METHOD_ID[types[i]]
451
+ until s.empty?
452
+ unless c = __send__(mid, chunk.size, s)
453
+ add_with_encode chunk unless chunk.empty?
454
+ flush
455
+ chunk = ''
456
+ fold
457
+ c = __send__(mid, 0, s)
458
+ raise 'TMail FATAL: extract fail' unless c
459
+ end
460
+ chunk << c
461
+ end
462
+ end
463
+ add_with_encode chunk unless chunk.empty?
464
+ end
465
+
466
+ def extract_J( chunksize, str )
467
+ size = max_bytes(chunksize, str.size) - 6
468
+ size = (size % 2 == 0) ? (size) : (size - 1)
469
+ return nil if size <= 0
470
+ if str.respond_to?(:encoding)
471
+ enc = str.encoding
472
+ str.force_encoding(Encoding::ASCII_8BIT)
473
+ "\e$B#{str.slice!(0, size)}\e(B".force_encoding(enc)
474
+ else
475
+ "\e$B#{str.slice!(0, size)}\e(B"
476
+ end
477
+ end
478
+
479
+ def extract_A( chunksize, str )
480
+ size = max_bytes(chunksize, str.size)
481
+ return nil if size <= 0
482
+ str.slice!(0, size)
483
+ end
484
+
485
+ alias extract_S extract_A
486
+
487
+ def max_bytes( chunksize, ssize )
488
+ (restsize() - '=?iso-2022-jp?B??='.size) / 4 * 3 - chunksize
489
+ end
490
+
491
+ #
492
+ # free length buffer
493
+ #
494
+
495
+ def add_text( str )
496
+ @text << str
497
+ # puts '---- text -------------------------------------'
498
+ # puts "+ #{str.inspect}"
499
+ # puts "txt >>>#{@text.inspect}<<<"
500
+ end
501
+
502
+ def add_with_encode( str )
503
+ @text << "=?iso-2022-jp?B?#{Base64.encode(str)}?="
504
+ end
505
+
506
+ def add_lwsp( lwsp )
507
+ # puts '---- lwsp -------------------------------------'
508
+ # puts "+ #{lwsp.inspect}"
509
+ fold if restsize() <= 0
510
+ flush(@folded)
511
+ @lwsp = lwsp
512
+ end
513
+
514
+ def flush(folded = false)
515
+ # puts '---- flush ----'
516
+ # puts "spc >>>#{@lwsp.inspect}<<<"
517
+ # puts "txt >>>#{@text.inspect}<<<"
518
+ @f << @lwsp << @text
519
+ if folded
520
+ @curlen = 0
521
+ else
522
+ @curlen += (@lwsp.size + @text.size)
523
+ end
524
+ @text = ''
525
+ @lwsp = ''
526
+ end
527
+
528
+ def fold
529
+ # puts '---- fold ----'
530
+ unless @f.string =~ /^.*?:$/
531
+ @f << @eol
532
+ @lwsp = SPACER
533
+ else
534
+ fold_header
535
+ @folded = true
536
+ end
537
+ @curlen = 0
538
+ end
539
+
540
+ def fold_header
541
+ # Called because line is too long - so we need to wrap.
542
+ # First look for whitespace in the text
543
+ # if it has text, fold there
544
+ # check the remaining text, if too long, fold again
545
+ # if it doesn't, then don't fold unless the line goes beyond 998 chars
546
+
547
+ # Check the text to see if there is whitespace, or if not
548
+ @wrapped_text = []
549
+ until @text.blank?
550
+ fold_the_string
551
+ end
552
+ @text = @wrapped_text.join("#{@eol}#{SPACER}")
553
+ end
554
+
555
+ def fold_the_string
556
+ whitespace_location = @text =~ /\s/ || @text.length
557
+ # Is the location of the whitespace shorter than the RCF_2822_MAX_LENGTH?
558
+ # if there is no whitespace in the string, then this
559
+ unless mazsize(whitespace_location) <= 0
560
+ @text.strip!
561
+ @wrapped_text << @text.slice!(0...whitespace_location)
562
+ # If it is not less, we have to wrap it destructively
563
+ else
564
+ slice_point = RFC_2822_MAX_LENGTH - @curlen - @lwsp.length
565
+ @text.strip!
566
+ @wrapped_text << @text.slice!(0...slice_point)
567
+ end
568
+ end
569
+
570
+ def restsize
571
+ MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
572
+ end
573
+
574
+ def mazsize(whitespace_location)
575
+ # Per RFC2822, the maximum length of a line is 998 chars
576
+ RFC_2822_MAX_LENGTH - (@curlen + @lwsp.size + whitespace_location)
577
+ end
578
+
579
+ end
580
+ #:startdoc:
581
+ end # module TMail