rbkb 0.6.10

Sign up to get free protection for your applications and to get access to all the features.
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
+