rbkb 0.6.10

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 (101) hide show
  1. data/History.txt +74 -0
  2. data/README.rdoc +149 -0
  3. data/Rakefile +47 -0
  4. data/bin/b64 +5 -0
  5. data/bin/bgrep +5 -0
  6. data/bin/blit +5 -0
  7. data/bin/c +5 -0
  8. data/bin/crc32 +5 -0
  9. data/bin/d64 +5 -0
  10. data/bin/dedump +5 -0
  11. data/bin/feed +5 -0
  12. data/bin/hexify +5 -0
  13. data/bin/len +5 -0
  14. data/bin/plugsrv +271 -0
  15. data/bin/rex +10 -0
  16. data/bin/rstrings +5 -0
  17. data/bin/slice +5 -0
  18. data/bin/telson +5 -0
  19. data/bin/unhexify +5 -0
  20. data/bin/urldec +5 -0
  21. data/bin/urlenc +5 -0
  22. data/bin/xor +5 -0
  23. data/cli_usage.rdoc +285 -0
  24. data/doctor-bag.jpg +0 -0
  25. data/lib/rbkb.rb +51 -0
  26. data/lib/rbkb/cli.rb +219 -0
  27. data/lib/rbkb/cli/b64.rb +35 -0
  28. data/lib/rbkb/cli/bgrep.rb +86 -0
  29. data/lib/rbkb/cli/blit.rb +89 -0
  30. data/lib/rbkb/cli/chars.rb +24 -0
  31. data/lib/rbkb/cli/crc32.rb +35 -0
  32. data/lib/rbkb/cli/d64.rb +28 -0
  33. data/lib/rbkb/cli/dedump.rb +52 -0
  34. data/lib/rbkb/cli/feed.rb +229 -0
  35. data/lib/rbkb/cli/hexify.rb +65 -0
  36. data/lib/rbkb/cli/len.rb +76 -0
  37. data/lib/rbkb/cli/rstrings.rb +108 -0
  38. data/lib/rbkb/cli/slice.rb +47 -0
  39. data/lib/rbkb/cli/telson.rb +87 -0
  40. data/lib/rbkb/cli/unhexify.rb +50 -0
  41. data/lib/rbkb/cli/urldec.rb +35 -0
  42. data/lib/rbkb/cli/urlenc.rb +35 -0
  43. data/lib/rbkb/cli/xor.rb +43 -0
  44. data/lib/rbkb/extends.rb +725 -0
  45. data/lib/rbkb/http.rb +21 -0
  46. data/lib/rbkb/http/base.rb +172 -0
  47. data/lib/rbkb/http/body.rb +214 -0
  48. data/lib/rbkb/http/common.rb +74 -0
  49. data/lib/rbkb/http/headers.rb +370 -0
  50. data/lib/rbkb/http/parameters.rb +104 -0
  51. data/lib/rbkb/http/request.rb +58 -0
  52. data/lib/rbkb/http/response.rb +86 -0
  53. data/lib/rbkb/plug.rb +9 -0
  54. data/lib/rbkb/plug/blit.rb +222 -0
  55. data/lib/rbkb/plug/cli.rb +83 -0
  56. data/lib/rbkb/plug/feed_import.rb +74 -0
  57. data/lib/rbkb/plug/peer.rb +67 -0
  58. data/lib/rbkb/plug/plug.rb +215 -0
  59. data/lib/rbkb/plug/proxy.rb +26 -0
  60. data/lib/rbkb/plug/unix_domain.rb +75 -0
  61. data/lib_usage.rdoc +176 -0
  62. data/rbkb.gemspec +38 -0
  63. data/spec/rbkb_spec.rb +7 -0
  64. data/spec/spec_helper.rb +16 -0
  65. data/tasks/ann.rake +80 -0
  66. data/tasks/bones.rake +20 -0
  67. data/tasks/gem.rake +201 -0
  68. data/tasks/git.rake +40 -0
  69. data/tasks/notes.rake +27 -0
  70. data/tasks/post_load.rake +34 -0
  71. data/tasks/rdoc.rake +51 -0
  72. data/tasks/rubyforge.rake +55 -0
  73. data/tasks/setup.rb +292 -0
  74. data/tasks/spec.rake +54 -0
  75. data/tasks/svn.rake +47 -0
  76. data/tasks/test.rake +40 -0
  77. data/test/test_cli_b64.rb +35 -0
  78. data/test/test_cli_bgrep.rb +137 -0
  79. data/test/test_cli_blit.rb +11 -0
  80. data/test/test_cli_chars.rb +21 -0
  81. data/test/test_cli_crc32.rb +108 -0
  82. data/test/test_cli_d64.rb +22 -0
  83. data/test/test_cli_dedump.rb +118 -0
  84. data/test/test_cli_feed.rb +11 -0
  85. data/test/test_cli_helper.rb +96 -0
  86. data/test/test_cli_hexify.rb +63 -0
  87. data/test/test_cli_len.rb +96 -0
  88. data/test/test_cli_rstrings.rb +15 -0
  89. data/test/test_cli_slice.rb +73 -0
  90. data/test/test_cli_telson.rb +11 -0
  91. data/test/test_cli_unhexify.rb +43 -0
  92. data/test/test_cli_urldec.rb +50 -0
  93. data/test/test_cli_urlenc.rb +44 -0
  94. data/test/test_cli_xor.rb +71 -0
  95. data/test/test_helper.rb +5 -0
  96. data/test/test_http.rb +27 -0
  97. data/test/test_http_helper.rb +60 -0
  98. data/test/test_http_request.rb +136 -0
  99. data/test/test_http_response.rb +222 -0
  100. data/test/test_rbkb.rb +19 -0
  101. metadata +238 -0
@@ -0,0 +1,35 @@
1
+ require 'rbkb/cli'
2
+
3
+ # Copyright 2009 emonti at matasano.com
4
+ # See README.rdoc for license information
5
+ #
6
+ # urlenc converts a string or raw data to a url percent-encoded string
7
+ # Input can be supplied via stdin, a string argument, or a file (with -f).
8
+ # (url percent-encoding is just fancy hex encoding)
9
+ class Rbkb::Cli::Urlenc < Rbkb::Cli::Executable
10
+ def make_parser()
11
+ super()
12
+ add_std_file_opt(:indat)
13
+ arg = @oparse
14
+ arg.banner += " <data | blank for stdin>"
15
+
16
+ arg.on("-p", "--[no-]plus",
17
+ "Convert spaces to '+' (default: false)") do |p|
18
+ @opts[:plus] = p
19
+ end
20
+ end
21
+
22
+ def parse(*args)
23
+ super(*args)
24
+ parse_string_argument(:indat)
25
+ parse_catchall()
26
+ end
27
+
28
+ def go(*args)
29
+ super(*args)
30
+ # Default to standard input
31
+ @opts[:indat] ||= @stdin.read()
32
+ @stdout << @opts[:indat].urlenc(:plus => @opts[:plus]) + "\n"
33
+ self.exit(0)
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rbkb/cli'
3
+
4
+ # Copyright 2009 emonti at matasano.com
5
+ # See README.rdoc for license information
6
+ #
7
+ # Repeating string xor. Takes input from a string, stdin, or a file (-f).
8
+ class Rbkb::Cli::Xor < Rbkb::Cli::Executable
9
+ def make_parser()
10
+ super()
11
+ add_std_file_opt(:indat)
12
+ arg = @oparse
13
+ arg.banner += " -k|-s <key> <data | stdin>"
14
+
15
+ arg.separator " Key options (you must specify one of the following):"
16
+ arg.on("-s", "--strkey STRING", "xor against STRING") do |s|
17
+ bail "only one key option can be specified with -s or -x" if @opts[:key]
18
+ @opts[:key] = s
19
+ end
20
+
21
+ arg.on("-x", "--hexkey HEXSTR", "xor against binary HEXSTR") do |x|
22
+ bail "only one key option can be specified with -s or -x" if @opts[:key]
23
+ x.sub!(/^0[xX]/, '')
24
+ bail "Unable to parse hex string" unless @opts[:key] = x.unhexify
25
+ end
26
+ return arg
27
+ end
28
+
29
+ def parse(*args)
30
+ super(*args)
31
+ bail("You must specify a key with -s or -x\n#{@oparse}") unless @opts[:key]
32
+ parse_string_argument(:indat)
33
+ parse_catchall()
34
+ end
35
+
36
+ def go(*args)
37
+ super(*args)
38
+ @opts[:indat] ||= @stdin.read
39
+ @stdout << @opts[:indat].xor(@opts[:key])
40
+ self.exit(0)
41
+ end
42
+ end
43
+
@@ -0,0 +1,725 @@
1
+ # Copyright 2009 emonti at matasano.com
2
+ # See README.rdoc for license information
3
+ #
4
+ require "stringio"
5
+ require 'zlib'
6
+ require 'open3'
7
+
8
+ module Rbkb
9
+ DEFAULT_BYTE_ORDER=:big
10
+ HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
11
+ end
12
+
13
+ # Generates a random alphanumeric string of 'size' bytes (8 by default)
14
+ def random_alphanum(size = 8)
15
+ chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
16
+ (1..size).collect{|a| chars[rand(chars.size)]}.join
17
+ end
18
+
19
+ # Generates a random string of 'size' bytes (8 by default)
20
+ def random_string(size = 8)
21
+ chars = (0..255).map {|c| c.chr }
22
+ (1..size).collect {|a| chars[rand(chars.size)]}
23
+ end
24
+
25
+ # Simple syntactic sugar to pass any object to a block
26
+ def with(x)
27
+ yield x if block_given?; x
28
+ end if not defined? with
29
+
30
+
31
+ #-----------------------------------------------------------------------------
32
+
33
+ # Mixins and class-specific items
34
+
35
+ class String
36
+ # shortcut for hex sanity with regex
37
+ def ishex? ; (self =~ /^[a-f0-9]+$/i)? true : false ; end
38
+
39
+ # Encode into percent-hexify url encoding format
40
+ def urlenc(opts={})
41
+ s=self
42
+ plus = opts[:plus]
43
+ unless (opts[:rx] ||= /[^A-Za-z0-9_\.~-]/).kind_of? Regexp
44
+ raise "rx must be a regular expression for a character class"
45
+ end
46
+ hx = Rbkb::HEXCHARS
47
+
48
+ s.gsub(opts[:rx]) do |c|
49
+ c=c[0]
50
+ (plus and c==32)? '+' : "%" + (hx[(c >> 4)] + hx[(c & 0xf )])
51
+ end
52
+ end
53
+
54
+ # Undo percent-hexified url encoding data
55
+ def urldec(opts={})
56
+ s=self
57
+ s.gsub!('+', ' ') unless opts[:noplus]
58
+ s.gsub(/%([A-Fa-f0-9]{2})/) {$1.hex.chr}
59
+ end
60
+
61
+ # Base64 encode
62
+ def b64(len=nil)
63
+ ret = [self].pack("m").gsub("\n", "")
64
+ if len and Numeric === len
65
+ ret.scan(/.{1,#{len}}/).join("\n") + "\n"
66
+ else
67
+ ret
68
+ end
69
+ end
70
+
71
+ # Base64 decode
72
+ def d64; self.unpack("m")[0]; end
73
+
74
+ # right-align to 'a' alignment padded with 'p'
75
+ def ralign(a, p=' ')
76
+ s=self
77
+ p ||= ' '
78
+ l = s.length
79
+ pad = l.pad(a)
80
+ s.rjust(pad+l, p)
81
+ end
82
+
83
+ # left-align to 'a' alignment padded with 'p'
84
+ def lalign(a, p=' ')
85
+ s=self
86
+ p ||= ' '
87
+ l = s.length
88
+ pad = l.pad(a)
89
+ s.ljust(pad+l, p)
90
+ end
91
+
92
+
93
+ # Convert a string to ASCII hex string. Supports a few options for format:
94
+ #
95
+ # :delim - delimter between each hex byte
96
+ # :prefix - prefix before each hex byte
97
+ # :suffix - suffix after each hex byte
98
+ #
99
+ def hexify(opts={})
100
+ s=self
101
+ delim = opts[:delim]
102
+ pre = (opts[:prefix] || "")
103
+ suf = (opts[:suffix] || "")
104
+
105
+ if (rx=opts[:rx]) and not rx.kind_of? Regexp
106
+ raise "rx must be a regular expression for a character class"
107
+ end
108
+
109
+ hx=Rbkb::HEXCHARS
110
+
111
+ out=Array.new
112
+
113
+ s.each_byte do |c|
114
+ hc = if (rx and not rx.match c.chr)
115
+ c.chr
116
+ else
117
+ pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
118
+ end
119
+ out << (hc)
120
+ end
121
+ out.join(delim)
122
+ end
123
+
124
+
125
+ # Convert ASCII hex string to raw.
126
+ #
127
+ # Parameters:
128
+ #
129
+ # d = optional 'delimiter' between hex bytes (zero+ spaces by default)
130
+ def unhexify(d=/\s*/)
131
+ self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
132
+ end
133
+
134
+ # Converts a hex value to numeric.
135
+ #
136
+ # Parameters:
137
+ #
138
+ # order => :big or :little endian (default is :big)
139
+ #
140
+ def hex_to_num(order=:big)
141
+ s=self
142
+ raise "invalid hex value: '#{s.inspect}'" unless s.ishex?
143
+
144
+ r = if order == :little
145
+ s.scan(/.{2}/).reverse.join
146
+ elsif order == :big
147
+ s
148
+ else
149
+ raise "Invalid byte order #{order.inspect}"
150
+ end.hex
151
+ end
152
+
153
+
154
+ # A "generalized" lazy bytestring -> numeric converter.
155
+ #
156
+ # Parameters:
157
+ #
158
+ # order => :big or :little endian (default is :big)
159
+ #
160
+ # Bonus: should work seamlessly with really large strings.
161
+ #
162
+ # >> ("\xFF"*10).dat_to_num
163
+ # => 1208925819614629174706175
164
+ # >> ("\xFF"*20).dat_to_num
165
+ # => 1461501637330902918203684832716283019655932542975
166
+ #
167
+ def dat_to_num(order=:big)
168
+ s=self
169
+ s.reverse! if order == :little
170
+ r = 0
171
+ s.each_byte {|c| r = ((r << 8) | c)}
172
+ r
173
+ end
174
+ alias lazy_to_n dat_to_num
175
+ alias lazy_to_num dat_to_num
176
+ alias dat_to_n dat_to_num
177
+
178
+
179
+ #### Crypto'ey stuff
180
+
181
+ # calculates entropy in string
182
+ #
183
+ # TQBF's description:
184
+ # "I also added a chi-squared test to quickly figure out entropy of a
185
+ # string, in "bits of randomness per byte". This is useful, so..."
186
+ def entropy
187
+ e = 0
188
+ 0.upto(255) do |i|
189
+ x = count(i.chr)/size.to_f
190
+ if x > 0
191
+ e += - x * x.log2
192
+ end
193
+ end
194
+ e
195
+ end
196
+
197
+ # xor against a key. key will be repeated or truncated to self.size.
198
+ def xor(k)
199
+ s=self
200
+ out=StringIO.new ; i=0;
201
+ s.each_byte do |x|
202
+ out.write((x ^ (k[i] || k[i=0]) ).chr)
203
+ i+=1
204
+ end
205
+ out.string
206
+ end
207
+
208
+ # convert bytes to number then xor against another byte-string or number
209
+ def ^(x)
210
+ x = x.dat_to_num unless x.is_a? Numeric
211
+ (self.dat_to_num ^ x)#.to_bytes
212
+ end
213
+
214
+ # Byte rotation as found in lame ciphers.
215
+ # This was cribbed from Timur Duehr with only a minor change.
216
+ def rotate_bytes(k=0)
217
+ r = self.dup
218
+ i=0
219
+ self.each_byte do |b|
220
+ r[i] = ((b + k) % 384).chr
221
+ i+=1
222
+ end
223
+ return r
224
+ end
225
+
226
+ # String randomizer
227
+ def randomize ; self.split('').randomize.to_s ; end
228
+
229
+ # In-place string randomizer
230
+ def randomize! ; self.replace(randomize) end
231
+
232
+
233
+ # Returns or prints a hexdump in the style of 'hexdump -C'
234
+ #
235
+ # :len => optionally specify a length other than 16 for a wider or thinner
236
+ # dump. If length is an odd number, it will be rounded up.
237
+ #
238
+ # :out => optionally specify an alternate IO object for output. By default,
239
+ # hexdump will output to STDOUT. Pass a StringIO object and it will return
240
+ # it as a string.
241
+ #
242
+ # Example:
243
+ #
244
+ # Here's the default behavior done explicitely:
245
+ #
246
+ # >> xxd = dat.hexdump(:len => 16, :out => StringIO.new)
247
+ # => <a string containing hexdump>
248
+ #
249
+ # Here's how to change it to STDERR
250
+ #
251
+ # >> xxd = dat.hexdump(:len => 16, :out => STDERR)
252
+ # <prints hexdump on STDERR>
253
+ # -> nil # return value is nil!
254
+ #
255
+ def hexdump(opt={})
256
+ s=self
257
+ out = opt[:out] || StringIO.new
258
+ len = (opt[:len] and opt[:len] > 0)? opt[:len] + (opt[:len] % 2) : 16
259
+
260
+ off = opt[:start_addr] || 0
261
+ offlen = opt[:start_len] || 8
262
+
263
+ hlen=len/2
264
+
265
+ s.scan(/(?:.|\n){1,#{len}}/) do |m|
266
+ out.write(off.to_s(16).rjust(offlen, "0") + ' ')
267
+
268
+ i=0
269
+ m.each_byte do |c|
270
+ out.write c.to_s(16).rjust(2,"0") + " "
271
+ out.write(' ') if (i+=1) == hlen
272
+ end
273
+
274
+ out.write(" " * (len-i) ) # pad
275
+ out.write(" ") if i < hlen
276
+
277
+ out.write(" |" + m.tr("\0-\37\177-\377", '.') + "|\n")
278
+ off += m.length
279
+ end
280
+
281
+ out.write(off.to_s(16).rjust(offlen,'0') + "\n")
282
+
283
+ if out.class == StringIO
284
+ out.string
285
+ end
286
+ end
287
+
288
+
289
+ # Converts a hexdump back to binary - takes the same options as hexdump().
290
+ # Fairly flexible. Should work both with 'xxd' and 'hexdump -C' style dumps.
291
+ def dehexdump(opt={})
292
+ s=self
293
+ out = opt[:out] || StringIO.new
294
+ len = (opt[:len] and opt[:len] > 0)? opt[:len] : 16
295
+
296
+ hcrx = /[A-Fa-f0-9]/
297
+ dumprx = /^(#{hcrx}+):?\s*((?:#{hcrx}{2}\s*){0,#{len}})/
298
+ off = opt[:start_addr] || 0
299
+
300
+ i=1
301
+ # iterate each line of hexdump
302
+ s.split(/\r?\n/).each do |hl|
303
+ # match and check offset
304
+ if m = dumprx.match(hl) and $1.hex == off
305
+ i+=1
306
+ # take the data chunk and unhexify it
307
+ raw = $2.unhexify
308
+ off += out.write(raw)
309
+ else
310
+ raise "Hexdump parse error on line #{i} #{s}"
311
+ end
312
+ end
313
+
314
+ if out.class == StringIO
315
+ out.string
316
+ end
317
+ end
318
+ alias dedump dehexdump
319
+ alias undump dehexdump
320
+ alias unhexdump dehexdump
321
+
322
+
323
+ # Binary grep
324
+ #
325
+ # Parameters:
326
+ #
327
+ # find : A Regexp or string to search for in self
328
+ # align : nil | numeric alignment (matches only made if aligned)
329
+ def bgrep(find, align=nil)
330
+ if align and (not align.is_a?(Integer) or align < 0)
331
+ raise "alignment must be a integer >= 0"
332
+ end
333
+
334
+ dat=self
335
+ if find.kind_of? Regexp
336
+ search = lambda do |m, buf|
337
+ if m = m.match(buf)
338
+ mtch = m[0]
339
+ off,endoff = m.offset(0)
340
+ return off, endoff, mtch
341
+ end
342
+ end
343
+ else
344
+ search = lambda do |s, buf|
345
+ if off = buf.index(s)
346
+ return off, off+s.size, s
347
+ end
348
+ end
349
+ end
350
+
351
+ ret=[]
352
+ pos = 0
353
+ while (res = search.call(find, dat[pos..-1]))
354
+ off, endoff, match = res
355
+ if align and ( pad = (pos+off).pad(align) ) != 0
356
+ pos += pad
357
+ else
358
+ hit = [pos+off, pos+endoff, match]
359
+ if not block_given? or yield([pos+off, pos+endoff, match])
360
+ ret << hit
361
+ end
362
+ pos += endoff
363
+ end
364
+ end
365
+ return ret
366
+ end
367
+
368
+ # A 'strings' method a-la unix strings utility. Finds printable strings in
369
+ # a binary blob.
370
+ # Supports ASCII and little endian unicode (though only for ASCII printable
371
+ # character.)
372
+ #
373
+ # === Parameters and options:
374
+ #
375
+ # * Use the :minimum parameter to specify minimum number of characters
376
+ # to match. (default = 6)
377
+ #
378
+ # * Use the :encoding parameter as one of :ascii, :unicode, or :both
379
+ # (default = :ascii)
380
+ #
381
+ # * The 'strings' method uses Regexp under the hood. Therefore
382
+ # you can pass a character class for "valid characters" with :valid
383
+ # (default = /[\r\n [:print:]]/)
384
+ #
385
+ # * Supports an optional block, which will be passed |offset, type, string|
386
+ # for each match.
387
+ # The block's boolean return value also determines whether the match
388
+ # passes or fails (true or false/nil) and gets returned by the function.
389
+ #
390
+ # === Return Value:
391
+ #
392
+ # Returns an array consisting of matches with the following elements:
393
+ #
394
+ # [[start_offset, end_offset, string_type, string], ...]
395
+ #
396
+ # * string_type will be one of :ascii or :unicode
397
+ # * end_offset will include the terminating null character
398
+ # * end_offset will include all null bytes in unicode strings (including
399
+ # * both terminating nulls)
400
+ #
401
+ # If strings are null terminated, the trailing null *IS* included
402
+ # in the end_offset. Unicode matches will also include null bytes.
403
+ #
404
+ # Todos?
405
+ # - better unicode support (i.e. not using half-assed unicode)
406
+ # - support other encodings such as all those the binutils strings does?
407
+ def strings(opts={})
408
+ opts[:encoding] ||= :both
409
+ prx = (opts[:valid] || /[\r\n [:print:]]/)
410
+ min = (opts[:minimum] || 6)
411
+ align = opts[:align]
412
+
413
+ raise "Minimum must be numeric and > 0" unless min.kind_of? Numeric and min > 0
414
+
415
+ arx = /(#{prx}{#{min}}?#{prx}*\x00?)/
416
+ urx = /((?:#{prx}\x00){#{min}}(?:#{prx}\x00)*(?:\x00\x00)?)/
417
+
418
+ rx = case (opts[:encoding] || :both).to_sym
419
+ when :ascii
420
+ arx
421
+ when :unicode
422
+ urx
423
+ when :both
424
+ Regexp.union( arx, urx )
425
+ else
426
+ raise "Encoding must be :unicode, :ascii, or :both"
427
+ end
428
+
429
+ off=0
430
+ ret = []
431
+
432
+ while mtch = rx.match(self[off..-1])
433
+ # calculate relative offsets
434
+ rel_off = mtch.offset(0)
435
+ startoff = off + rel_off[0]
436
+ endoff = off + rel_off[1]
437
+ off += rel_off[1]
438
+
439
+ if align and (pad=startoff.pad(align)) != 0
440
+ off = startoff + pad
441
+ next
442
+ end
443
+
444
+ stype = if mtch[1]
445
+ :ascii
446
+ elsif mtch[2]
447
+ :unicode
448
+ end
449
+
450
+
451
+ mret = [startoff, endoff, stype, mtch[0] ]
452
+
453
+ # yield to a block for additional criteria
454
+ next if block_given? and not yield( *mret )
455
+
456
+ ret << mret
457
+ end
458
+
459
+ return ret
460
+ end
461
+
462
+ # Does string "start with" dat?
463
+ # No clue whether/when this is faster than a regex, but it is easier to type.
464
+ def starts_with?(dat)
465
+ self[0,dat.size] == dat
466
+ end
467
+
468
+ # Returns a single null-terminated ascii string from beginning of self.
469
+ # This will return the entire string if no null is encountered.
470
+ #
471
+ # Parameters:
472
+ #
473
+ # off = specify an optional beggining offset
474
+ #
475
+ def cstring(off=0)
476
+ self[ off, self.index("\x00") || self.size ]
477
+ end
478
+
479
+ # returns CRC32 checksum for the string object
480
+ def crc32
481
+ ## pure ruby version. slower, but here for reference (found on some forum)
482
+ # r = 0xFFFFFFFF
483
+ # self.each_byte do |b|
484
+ # r ^= b
485
+ # 8.times do
486
+ # r = (r>>1) ^ (0xEDB88320 * (r & 1))
487
+ # end
488
+ # end
489
+ # r ^ 0xFFFFFFFF
490
+ ## or... we can just use:
491
+ Zlib.crc32 self
492
+ end
493
+
494
+ # This attempts to identify a blob of data using 'file(1)' via popen3
495
+ # (using popen3 because IO.popen blows)
496
+ # Tried doing this with a fmagic ruby extention to libmagic, but it was
497
+ # a whole lot slower.
498
+ def pipe_magick(arg="")
499
+ ret=""
500
+ Open3.popen3("file #{arg} -") do |w,r,e|
501
+ w.write self; w.close
502
+ ret = r.read ; r.close
503
+ ret.sub!(/^\/dev\/stdin: /, "")
504
+ end
505
+ ret
506
+ end
507
+
508
+ # Converts a '_' delimited string to CamelCase like 'foo_class' into
509
+ # 'FooClass'.
510
+ # See also: camelize_meth, decamelize
511
+ def camelize
512
+ self.gsub(/(^|_)([a-z])/) { $2.upcase }
513
+ end
514
+
515
+ # Converts a '_' delimited string to method style camelCase like 'foo_method'
516
+ # into 'fooMethod'.
517
+ # See also: camelize, decamelize
518
+ def camelize_meth
519
+ self.gsub(/_([a-z])/) { $1.upcase }
520
+ end
521
+
522
+
523
+ # Converts a CamelCase or camelCase string into '_' delimited form like
524
+ # 'FooBar' or 'fooBar' into 'foo_bar'.
525
+ #
526
+ # Note: This method only handles camel humps. Strings with consecutive
527
+ # uppercase chars like 'FooBAR' will be converted to 'foo_bar'
528
+ #
529
+ # See also: camelize, camelize_meth
530
+ def decamelize
531
+ self.gsub(/(^|[a-z])([A-Z])/) do
532
+ ($1.empty?)? $2 : "#{$1}_#{$2}"
533
+ end.downcase
534
+ end
535
+
536
+ # convert a string to its idiomatic ruby class name
537
+ def class_name
538
+ r = ""
539
+ up = true
540
+ each_byte do |c|
541
+ if c == 95
542
+ if up
543
+ r << "::"
544
+ else
545
+ up = true
546
+ end
547
+ else
548
+ m = up ? :upcase : :to_s
549
+ r << (c.chr.send(m))
550
+ up = false
551
+ end
552
+ end
553
+ r
554
+ end
555
+
556
+
557
+
558
+ # Returns a reference to actual constant for a given name in namespace
559
+ # can be used to lookup classes from enums and such
560
+ def const_lookup(ns=Object)
561
+ if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
562
+ ns.const_get(c.first)
563
+ end
564
+ end
565
+
566
+ # Return a self encapsulated in a StringIO object. This is handy.
567
+ def to_stringio
568
+ StringIO.new(self)
569
+ end
570
+
571
+ end # class String
572
+
573
+
574
+ class Symbol
575
+ # looks up this symbol as a constant defined in 'ns' (Object by default)
576
+ def const_lookup(ns=Object)
577
+ self.to_s.const_lookup(ns)
578
+ end
579
+ end
580
+
581
+ class Array
582
+ # randomizes the order of contents in the Array (self)
583
+ def randomize ; self.sort_by { rand } ; end
584
+
585
+ # Returns a randomly chosen element from self.
586
+ # Drew *is* sparta.
587
+ def rand_elem; self[rand(self.length)] ; end
588
+ end
589
+
590
+ class Float
591
+ def log2; Math.log(self)/Math.log(2); end
592
+ end
593
+
594
+
595
+ class Numeric
596
+
597
+ # calculate padding based on alignment(a)
598
+ def pad(a)
599
+ raise "bad alignment #{a.inspect}" unless a.kind_of? Numeric and a > 0
600
+ return self < 1 ? a + self : (a-1) - (self-1) % a
601
+ end
602
+
603
+ # tells you whether a number is within printable range
604
+ def printable?; self >= 0x20 and self <= 0x7e; end
605
+
606
+ # just to go with the flow
607
+ def randomize ; rand(self) ; end
608
+
609
+ # shortcut for packing a single number... wtf...
610
+ def pack(arg) ; [self].pack(arg) ; end
611
+
612
+ def clear_bits(c) ; (self ^ (self & c)) ; end
613
+
614
+ # Returns an array of chars per 8-bit break-up.
615
+ # Accepts a block for some transformation on each byte.
616
+ # (used by to_bytes and to_hex under the hood)
617
+ #
618
+ # args:
619
+ # order: byte order - :big or :little
620
+ # (only :big has meaning)
621
+ # siz: pack to this size. larger numbers will wrap
622
+ def to_chars(order=nil, siz=nil)
623
+ order ||= Rbkb::DEFAULT_BYTE_ORDER
624
+ n=self
625
+ siz ||= self.size
626
+ ret=[]
627
+ siz.times do
628
+ c = (n % 256)
629
+ if block_given? then (c = yield(c)) end
630
+ ret << c
631
+ n=(n >> 8)
632
+ end
633
+ return ((order == :big)? ret.reverse : ret)
634
+ end
635
+
636
+ # "packs" a number into bytes using bit-twiddling instead of pack()
637
+ #
638
+ # Uses to_chars under the hood. See also: to_hex
639
+ #
640
+ # args:
641
+ # siz: pack to this size. larger numbers will wrap
642
+ # order: byte order - :big or :little
643
+ # (only :big has meaning)
644
+ def to_bytes(order=nil, siz=nil)
645
+ to_chars(order,siz) {|c| c.chr }.join
646
+ end
647
+
648
+ # Converts a number to hex string with width and endian options.
649
+ # "packs" a number into bytes using bit-twiddling instead of pack()
650
+ #
651
+ # Uses to_chars under the hood. See also: to_bytes
652
+ #
653
+ # args:
654
+ # siz: pack to this size. larger numbers will wrap
655
+ # order: byte order - :big or :little
656
+ # (only :big has meaning)
657
+ #
658
+ def to_hex(o=nil, s=nil)
659
+ to_chars(o,s) {|c|
660
+ Rbkb::HEXCHARS[c.clear_bits(0xf) >> 4]+Rbkb::HEXCHARS[c.clear_bits(0xf0)]
661
+ }.join
662
+ end
663
+
664
+ # TODO Fix Numeric.to_guid for new to_bytes/char etc.
665
+ # def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
666
+ # raw = self.to_bytes(order, 16)
667
+ # a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
668
+ # e = e.join
669
+ # [a,b,c,d,e].join("-").upcase
670
+ # end
671
+
672
+ end # class Numeric
673
+
674
+
675
+ # some extra features for zlib... more to come?
676
+ module Zlib
677
+ OSMAP = {
678
+ OS_MSDOS => :msdos,
679
+ OS_AMIGA => :amiga,
680
+ OS_VMS => :vms,
681
+ OS_UNIX => :unix,
682
+ OS_ATARI => :atari,
683
+ OS_OS2 => :os2,
684
+ OS_TOPS20 => :tops20,
685
+ OS_WIN32 => :win32,
686
+ OS_VMCMS => :vmcms,
687
+ OS_ZSYSTEM => :zsystem,
688
+ OS_CPM => :cpm,
689
+ OS_RISCOS => :riscos,
690
+ OS_UNKNOWN => :unknown
691
+ }
692
+
693
+ # Helpers for Zlib::GzipFile... more to come?
694
+ class GzipFile
695
+
696
+ ## extra info dump for gzipped files
697
+ def get_xtra_info
698
+ info = {
699
+ :file_crc => crc.to_hex,
700
+ :file_comment => comment,
701
+ :file_name => orig_name,
702
+ :level => level,
703
+ :mtime => mtime,
704
+ :os => (Zlib::OSMAP[os_code] || os_code)
705
+ }
706
+ end
707
+ end
708
+ end
709
+
710
+ class Object
711
+ ## This is from Topher Cyll's Stupd IRB tricks
712
+ def mymethods
713
+ (self.methods - self.class.superclass.methods).sort
714
+ end
715
+ end
716
+
717
+ module Enumerable
718
+ def each_recursive(&block)
719
+ self.each do |n|
720
+ block.call(n)
721
+ n.each_recursive(&block) if n.kind_of? Array or n.kind_of? Hash
722
+ end
723
+ end
724
+ end
725
+