peekbot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/dispatch.fcgi ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/local/bin/ruby
2
+ require 'rubygems'
3
+ require 'camping'
4
+ require 'camping/fastcgi'
5
+ require 'peekbot'
6
+
7
+ Camping::FastCGI.serve("peekbot.rb")
8
+
9
+ FCGI.each do |req|
10
+ # req.out << "Content-Type: text/html\r\n\r\nHello, World!<br>\n"
11
+ # req.env.keys.sort.each {|k| req.out << "#{k}=#{req.env[k]}<br>\n"}
12
+ camp_do(req)
13
+ # req.finish
14
+ end
15
+
data/m.bat ADDED
@@ -0,0 +1,3 @@
1
+ del peekbot-*.gem
2
+ call gem uninstall peekbot
3
+ ruby peekbot.gemspec
@@ -0,0 +1,51 @@
1
+ #!/usr/local/bin/ruby
2
+ lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
3
+ $:.unshift(lib_path) unless $:.include?(lib_path)
4
+
5
+ lib_path=File.expand_path(File.dirname(__FILE__)+"//..//..//ripxplore//lib")
6
+ $:.unshift(lib_path) unless $:.include?(lib_path)
7
+
8
+ require 'RipXplore'
9
+ require 'FileCache'
10
+ PEEKBOT_URL_CACHE_DIR=File.dirname(__FILE__)+'/.image_cache'
11
+ NATIVE_FILE_CACHE_PATH='/static/native_files'
12
+ PEEKBOT_NATIVE_FILE_CACHE_DIR=File.dirname(__FILE__)+NATIVE_FILE_CACHE_PATH
13
+
14
+ LAST_PEEKBOT_UPDATE=File.stat(__FILE__).mtime
15
+
16
+ filecache=FileCache.new(PEEKBOT_URL_CACHE_DIR,PEEKBOT_NATIVE_FILE_CACHE_DIR)
17
+ filecache.make_file_system_image_list
18
+ all_image_urls=filecache.file_system_image_urls_in_cache
19
+ all_image_urls.reverse.each do |url|
20
+ puts "dumping #{url} at #{Time.now.to_s}"
21
+ begin
22
+ thread1=Thread.new do
23
+ image=filecache.get_file_system_image(url)
24
+ puts "got image"
25
+ image.pictures.each do |picture|
26
+ local_filename=filecache.force_native_file_to_cache(picture,:to_picture,picture.picture_format.to_s)
27
+ puts "made picture #{local_filename}"
28
+ end
29
+ image.texts.each do |text|
30
+ if text.respond_to?(:to_screendump) then
31
+ local_filename=filecache.force_native_file_to_cache(text,:to_screendump,"png")
32
+ puts "made screendump #{local_filename}"
33
+ end
34
+ end
35
+
36
+ end
37
+ 60.times do
38
+ break unless thread1.alive?
39
+ sleep 1
40
+ end
41
+ if thread1.alive? then
42
+ puts "killing thread"
43
+ thread1.kill
44
+ end
45
+ rescue
46
+ puts $!
47
+ end
48
+ end
49
+
50
+
51
+
data/peekbot.rb ADDED
@@ -0,0 +1,599 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'camping'
6
+
7
+ Markaby::Builder.set(:indent, 2)
8
+
9
+ lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
10
+ $:.unshift(lib_path) unless $:.include?(lib_path)
11
+
12
+ lib_path=File.expand_path(File.dirname(__FILE__)+"//..//..//ripxplore//lib")
13
+ $:.unshift(lib_path) unless $:.include?(lib_path)
14
+
15
+ require 'peekbot_helper'
16
+
17
+ require 'RipXplore'
18
+
19
+ PEEKBOT_URL_CACHE_DIR=File.dirname(__FILE__)+'/.image_cache'
20
+ NATIVE_FILE_CACHE_PATH='/static/native_files'
21
+ PEEKBOT_NATIVE_FILE_CACHE_DIR=File.dirname(__FILE__)+NATIVE_FILE_CACHE_PATH
22
+
23
+ LAST_PEEKBOT_UPDATE=File.stat(__FILE__).mtime
24
+
25
+ require 'FileCache'
26
+ @@filecache=FileCache.new(PEEKBOT_URL_CACHE_DIR,PEEKBOT_NATIVE_FILE_CACHE_DIR)
27
+
28
+
29
+ Camping.goes :PeekBot
30
+
31
+ @@images={}
32
+ def get_image(url)
33
+ if @@images[url].nil? then
34
+
35
+ @@images[url]=@@filecache.get_file_system_image(url)
36
+ end
37
+ @@images[url]
38
+ end
39
+
40
+ def local_mode?
41
+ remote_host=@env["REMOTE_ADDR"]
42
+ is_localhost=(remote_host=~/127\.0\.0\.1/)
43
+ is_localhost
44
+ end
45
+ def path_for_cached_native_file(native_file,method_to_cache,extension)
46
+ local_filename=@@filecache.force_native_file_to_cache(native_file,method_to_cache,extension)
47
+ local_filename.sub(PEEKBOT_NATIVE_FILE_CACHE_DIR,NATIVE_FILE_CACHE_PATH)
48
+ end
49
+
50
+ module PeekBot::Controllers
51
+
52
+
53
+ # The root slash shows the `index' view.
54
+ class Index < R '/'
55
+ def get
56
+ if local_mode? then
57
+ redirect R(NavigateArchive,"file://#{Dir.getwd}")
58
+ else
59
+ render :index
60
+ end
61
+ end
62
+ end
63
+
64
+ class HelpWanted < R '/help_wanted/'
65
+ def get
66
+ render :help_wanted
67
+ end
68
+ end
69
+
70
+ class NavigateArchive < R '/navigate_archive/(.+)'
71
+ def get(archive_url)
72
+ @working_url=WorkingUrl.new(archive_url)
73
+ @images,@directories,@other_files=PeekBotHelper.get_archive_contents(@working_url.archive_url)
74
+ render :navigate_archive
75
+ end
76
+ end
77
+
78
+ class Static < R '/static/(.+)'
79
+ MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript', '.jpg' => 'image/jpeg,','.png' => 'image/png','.gif' => 'image/gif'}
80
+ PATH = File.expand_path(File.dirname(__FILE__))
81
+ def get(path)
82
+ @headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] || "text/plain"
83
+ unless path.include? ".." # prevent directory traversal attacks
84
+ @headers['X-Sendfile'] = "#{PATH}/static/#{path}"
85
+ else
86
+ @status = "403"
87
+ "403 - Invalid path"
88
+ end
89
+ end
90
+ end
91
+
92
+
93
+ class CatalogImage < R '/catalog_image/(.+)'
94
+ def get(image_url)
95
+ @working_url=WorkingUrl.split_image_url_into_parts(image_url)
96
+ @image=get_image(@working_url.image_url)
97
+ render :catalog_image
98
+ end
99
+ end
100
+
101
+ class AllFilesOfType < R '/all_([^/]+)s/(.+)'
102
+ def get(filetype,image_url)
103
+ @working_url=WorkingUrl.split_image_url_into_parts(image_url)
104
+ @image=get_image(@working_url.image_url)
105
+ render "all_#{filetype}s".to_sym
106
+ end
107
+ end
108
+
109
+ class NativeFile < R '/native_file/(.+)/(\d+)'
110
+ def get(image_url,native_file_index)
111
+ @display_mode=input.display_mode
112
+ @working_url=WorkingUrl.split_image_url_into_parts(image_url)
113
+ @native_file_index=native_file_index
114
+ @image=get_image(@working_url.image_url)
115
+ @native_file=@image.files[native_file_index.to_i]
116
+ render :native_file
117
+ end
118
+ end
119
+
120
+ class SectorViewer < R '/sector_viewer/(.+)/(\d+)/(\d+)'
121
+ def get(image_url,track,sector)
122
+ @display_mode=input.display_mode
123
+ @working_url=WorkingUrl.split_image_url_into_parts(image_url)
124
+ @track=track.to_i
125
+ @sector=sector.to_i
126
+ @image=get_image(@working_url.image_url)
127
+ render :sector_viewer
128
+ end
129
+ end
130
+
131
+ class RandomFile < R '/random_([^/]+)/'
132
+ def get(filetype)
133
+ @random_filetype=filetype
134
+ random_filetype_collection_name="#{@random_filetype}s".to_sym
135
+ @image=@@filecache.random_file_system_image(random_filetype_collection_name)
136
+ @working_url=WorkingUrl.split_image_url_into_parts(@image.filename)
137
+ random_filetype_collection=@image.send(random_filetype_collection_name)
138
+ random_index=rand(random_filetype_collection.length)
139
+ @native_file=random_filetype_collection[random_index]
140
+ render :random_file
141
+ end
142
+ end
143
+
144
+ # Any other page name gets sent to the view
145
+ # of the same name.
146
+ #
147
+ # /index -> Views#index
148
+ # /sample -> Views#sample
149
+ #
150
+
151
+ class Page < R '/(\w+)'
152
+ def get(page_name)
153
+ render page_name
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+
160
+ module PeekBot::Views
161
+
162
+ # If you have a `layout' method like this, it
163
+ # will wrap the HTML in the other methods. The
164
+ # `self << yield' is where the HTML is inserted.
165
+ def layout
166
+ html do
167
+ head do
168
+ title 'peekbot'
169
+ link :href=>R(Static,'style.css'), :rel=>'stylesheet', :type=>'text/css'
170
+ end
171
+ body do
172
+ self << header
173
+ self << yield
174
+ self << footer
175
+ end
176
+ # footer { 'hello'}
177
+ end
178
+ end
179
+
180
+ # The `index' view. Inside your views, you express
181
+ # the HTML in Ruby. See http://code.whytheluckystiff.net/markaby/.
182
+ def index
183
+
184
+ text "peekbot is an attempt to make vintage computing artifacts visible to the modern intarweb."
185
+ text "It is a web proxy that allows navigation through online collections of disk images. "
186
+ text "For disk images in formats which peekbot understands, the file system contained on that disk image can be explored."
187
+ br
188
+ br
189
+ text "Many of the common file types found in the file systems can be converted to a format that can be viewed in a browser."
190
+ text "For example, tokenised BASIC files can be listed as ASCII, or (some) old graphic formats converted to PNG."
191
+ text "There is also a track/sector viewer, and any file found on a disk image can be viewed as a hex dump."
192
+ br
193
+ br
194
+ text "peekbot is based on"
195
+ a 'ripxplore',:href=>'http://ripxplore.rubyforge.org/'
196
+ text ", a ruby library for identifying and extracting data from disk images."
197
+ br
198
+ br
199
+ text "To get started, you could "
200
+ a 'view a random picture',:href=>R(RandomFile,:picture)
201
+ text" or else navigate with peekbot through some online disk image archives by clicking on one of the archive descriptions below."
202
+ br
203
+ br
204
+ ul do
205
+ [
206
+ ['http://cocomag.dyndns.org/swarc/',"Tim Lindner's Tandy Color Computer archive"],
207
+ ['http://mirrors.apple2.org.za/ftp.apple.asimov.net/',"Asimov Apple 2 image collections mirrored at apple2.org.za"],
208
+ ['http://mirrors.apple2.org.za/landover.no-ip.com/',"Landover Apple 2 image collections mirrored at apple2.org.za"],
209
+ # ['http://mirrors.apple2.org.za/',"Apple 2 image collections mirrored at apple2.org.za"],
210
+ ['http://www.atariarchives.org/oldhackers/',"Old Hackers Atari User Group Disk Archive (at AtariArchives.org)"],
211
+ # ['http://www.atariarchives.org/swlibrary/',"Atari Software Library (at AtariArchives.org)"],
212
+ # ['http://www.cs.vu.nl/pub/ipoorten/atari.8bit/megazine/atr/','Atari Megazine archive'],
213
+ ['ftp://ftp.whtech.com/emulators/pc99/',"TI 99/4A archive at ftp.whtech.com"],
214
+ ['http://disk-images.jamtronix.com/ti99/',"Jamtronix mirror of TOSEC TI 99/4A collection"],
215
+ ['http://disk-images.jamtronix.com/coco/',"Jamtronix mirror of Tandy Color Computer dsks"],
216
+ ['http://www.classic-computers.org.nz/system-80/software_archive.htm',"Terry Stewart's collection of Dick Smith System 80 software"],
217
+ # ['http://disk-images.jamtronix.com/trs80/',"Jamtronix mirror of the Dutch TRS-80 usergroup collection (http://www.trs80.nl/)"],
218
+ # ['http://www.zimmers.net/anonftp/pub/cbm/c64/',"Bob Zimmerman's mirror of the FUNET C64 archive"],
219
+ # ['http://ftp.strassenbahn.tk:81/inntram-public/off-topic/c64/',"Strassenbahn.Tk C64 Archive"],
220
+ #
221
+ ['http://www.zx81.nl/files.html','ZX81 archive at www.zx81.nl'],
222
+ ].each do |archive_url|
223
+ li do
224
+ a archive_url[1], :href=> R(NavigateArchive,archive_url[0])
225
+ #br
226
+ #text "[#{archive_url[0]}]"
227
+ end
228
+ end
229
+ end
230
+
231
+ br
232
+ text "NB - peekbot does not archive any images itself - they are all downloaded into peekbot from the referenced archive on demand."
233
+ end
234
+
235
+ def nav_bar
236
+
237
+ return if @working_url.nil?
238
+ raise "security violation - only localhost can access file:// URLs" if @working_url.archive_url=~/^file:/ && ! (local_mode?)
239
+ raise 'invalid @working_url' unless @working_url.kind_of?(WorkingUrl)
240
+
241
+ if ! @working_url.archive_url.nil? then
242
+ text ' now peeking at : '
243
+ if @working_url.image_basename.nil? then
244
+ text URI.unescape(@working_url.archive_url)
245
+ a "[go direct]", :href=>@working_url.archive_url unless local_mode?
246
+ else
247
+ a @working_url.archive_url, :href=> R(NavigateArchive,@working_url.archive_url)
248
+ if @native_file.nil? then
249
+ a @working_url.image_basename, :href=> R(CatalogImage,@working_url.image_url)
250
+ else
251
+ a @working_url.image_basename, :href=> R(CatalogImage,@working_url.image_url)
252
+ text ' / '
253
+ text @native_file.filename
254
+ end
255
+ br
256
+ a "[download #{File.basename(@working_url.image_url)}]", :href=>@working_url.image_url unless local_mode?
257
+ end
258
+ end
259
+ end
260
+ def navigate_archive
261
+
262
+ table {
263
+ tr.header { td { text "Navigation Options"} }
264
+ @directories.each do |dirname|
265
+ tr.directory {
266
+ td {a URI.unescape(dirname), :href=> R(NavigateArchive,PeekBotHelper.combine_url(@working_url.archive_url,dirname).to_s)}
267
+ td {}
268
+ }
269
+ end
270
+
271
+ tr.header { td { text "Peekable Images"} }
272
+ @images.each do |filename|
273
+ tr.image {
274
+ td {
275
+ a 'explore', :href=> R(CatalogImage,PeekBotHelper.combine_url(@working_url.archive_url,filename))
276
+ if (! local_mode?) then
277
+ text ' | '
278
+ begin
279
+ a 'download', :href=> PeekBotHelper.combine_url(@working_url.archive_url,filename).to_s
280
+ rescue #nasty kludge but for the moment, just skip over filenames that URI doesn't like
281
+ end
282
+ end
283
+ }
284
+ td {text URI.unescape(filename) }
285
+ }
286
+ end
287
+
288
+ if @other_files.length>0 then
289
+ tr.header { td { text "Other Files"} }
290
+ @other_files.each do |filename|
291
+ tr.other_file {
292
+ td {
293
+ begin
294
+ a 'download', :href=> PeekBotHelper.combine_url(@working_url.archive_url,filename).to_s
295
+ rescue #nasty kludge but for the moment, just skip over filenames that URI doesn't like
296
+ end
297
+ }
298
+ td {text URI.unescape(filename)}
299
+ }
300
+ end
301
+ end
302
+ }
303
+ end
304
+
305
+ def native_file
306
+ h3 @native_file.filename
307
+ case @display_mode
308
+ when 'hex' then
309
+ pre.hex_dump @native_file.to_hex_dump
310
+ when 'to_picture', 'to_png' then
311
+ scale =600/@native_file.picture_width
312
+ scale=1 if scale<1
313
+ img :src=>path_for_cached_native_file(@native_file,:to_picture,@native_file.picture_format.to_s),:width=>@native_file.picture_width*scale,:height=>@native_file.picture_height*scale
314
+ when 'to_screendump' then
315
+ #~ scale =800/@native_file.screendump_width
316
+ #~ scale=1 if scale<1
317
+ #~ img :src=>path_for_cached_native_file(@native_file,:to_screendump,"png"),:width=>@native_file.screendump_width*scale,:height=>@native_file.screendump_height*scale
318
+ center {
319
+ img :src=>path_for_cached_native_file(@native_file,:to_screendump,"png"),:width=>'80%'
320
+ }
321
+ else
322
+ pre.text @native_file.send(@display_mode)
323
+ end
324
+ end
325
+
326
+ def all_pictures
327
+ h2 "VIEWING ALL PICTURES"
328
+ @image.pictures.each do |picture_file|
329
+ h3 picture_file.filename
330
+ scale =600/picture_file.picture_width
331
+ scale=1 if scale<1
332
+ img :src=>path_for_cached_native_file(picture_file,:to_picture,picture_file.picture_format.to_s),:width=>picture_file.picture_width*scale,:height=>picture_file.picture_height*scale, :alt=>picture_file.filename
333
+ end
334
+ end
335
+
336
+ def all_texts
337
+ h2 "VIEWING ALL TEXT FILES"
338
+ @image.texts.each do |text_file|
339
+ h3 text_file.filename
340
+ pre text_file.to_text
341
+ end
342
+ end
343
+
344
+ def random_file
345
+ h2 "RANDOM #{@random_filetype.to_s.upcase}"
346
+ native_file=@native_file
347
+ text "Peekbot chose this #{@random_filetype.to_s} at random for you. It was found on a disk image that appears to have been originally for the #{@image.host_system.full_name}. You may like to look at:"
348
+ ul {
349
+ if (native_file.respond_to?(:to_screendump)) then
350
+ i=@image.files.index(native_file)
351
+ li {a "how this file would have looked on the #{@image.host_system.full_name} ", :href=> R(NativeFile,@working_url.image_url,i)+"?display_mode=to_screendump"}
352
+ end
353
+ li {a "all #{@image.files.length} files on this disk image", :href=> R(CatalogImage,@working_url.image_url)}
354
+ if @image.pictures.length>1 then
355
+ li {a "the #{@image.pictures.length} pictures on this disk image", :href=> R(AllFilesOfType,:picture,@working_url.image_url)}
356
+ end
357
+ if @image.texts.length>1 then
358
+ li {a "the #{@image.texts.length} texts on this disk image", :href=> R(AllFilesOfType,:text,@working_url.image_url)}
359
+ end
360
+
361
+ [:picture,:text,:listing].each do |random_filetype|
362
+ li {a "#{random_filetype.to_s== @random_filetype.to_s ? "another" : "a"} random #{random_filetype.to_s}", :href=> R(RandomFile,random_filetype)}
363
+ end
364
+ }
365
+
366
+ h3 native_file.filename
367
+ if random_filetype.to_s==:picture.to_s then
368
+ scale =600/native_file.picture_width
369
+ scale=1 if scale<1
370
+ img :src=>path_for_cached_native_file(native_file,:to_picture,native_file.picture_format.to_s),:width=>native_file.picture_width*scale,:height=>native_file.picture_height*scale, :alt=>native_file.filename
371
+ else
372
+ pre native_file.send("to_#{random_filetype}".to_sym)
373
+ end
374
+ end
375
+
376
+
377
+ def image_header
378
+ div.image_attributes do
379
+ h2 'image summary'
380
+ table.image_info do
381
+ [:host_system,:image_format,:file_system,:track_count,:volume_name].each do |att|
382
+ if (@image.respond_to?(att)) then
383
+ att_value=@image.send(att)
384
+ tr {
385
+ td att.to_s
386
+ td att_value
387
+ } unless (att_value.nil? || att_value==0)
388
+ end
389
+ end
390
+ if @image.pictures.length>0 then
391
+ tr {
392
+ td "pictures"
393
+ td {
394
+ text @image.pictures.length
395
+ a "VIEW ALL", :href=> R(AllFilesOfType,:picture,@working_url.image_url)
396
+ }
397
+ }
398
+ end
399
+ if @image.texts.length>0 then
400
+ tr {
401
+ td "text files"
402
+ td {
403
+ text @image.texts.length
404
+ a "VIEW ALL", :href=> R(AllFilesOfType,:text,@working_url.image_url)
405
+ }
406
+ }
407
+
408
+ end
409
+
410
+
411
+ end
412
+ end
413
+ end
414
+
415
+ def track_list
416
+ div.track_list do
417
+ h3 'tracks'
418
+ @image.track_list.each do |track|
419
+ a "[%02X]" % track,:href=>R(SectorViewer,@working_url.image_url,track,@image.sectors_in_track(track)[0])+"\#sector_viewer",:rel=>"nofollow"
420
+ text " "
421
+ end
422
+ end unless @image.track_list.length==0
423
+ end
424
+
425
+ def sector_viewer
426
+ image_header
427
+ div.sector_viewer do
428
+ h2 "sector viewer"
429
+ h3 {a "sectors in track $#{"%02x" % track}",:name=>"sector_viewer"}
430
+ @image.sectors_in_track(track).each do |sector|
431
+ a "[%02X]" % sector,:href=>R(SectorViewer,@working_url.image_url,track,sector)+"\#sector_viewer",:rel=>"nofollow"
432
+ text " "
433
+ end
434
+ pre @image.dump_sector(track,sector)
435
+
436
+ end
437
+
438
+ track_list
439
+ end
440
+ def catalog_image
441
+
442
+ image_header
443
+
444
+ div.image_catalog do
445
+ h2 'all files'
446
+
447
+ table.image_catalog {
448
+ tr {
449
+ th 'View As'
450
+ th 'Filename'
451
+ th 'Type'
452
+ th 'Length (bytes)'
453
+ th 'Load Address'
454
+ }
455
+
456
+ @image.files.length.times do |i|
457
+ file=@image.files[i]
458
+ tr {
459
+
460
+ td {
461
+ a "HEX",:href=>R(NativeFile,@working_url.image_url,i)+"?display_mode=hex",:rel=>"nofollow"
462
+ [:to_listing,:to_text,:to_picture,:to_screendump].each do |display_mode|
463
+ if file.respond_to?(display_mode) then
464
+ text " | "
465
+ description=display_mode.to_s.sub("to_","").upcase
466
+ a description,:href=>R(NativeFile,@working_url.image_url,i)+"?display_mode=#{display_mode.to_s}"
467
+ end
468
+ end
469
+ }
470
+ td file.filename
471
+ td file.type_description
472
+ td "$%04X" % file.contents.length
473
+ td "$%04X" % file.load_address
474
+ }
475
+ end
476
+ }
477
+ end unless @image.files.length==0
478
+ track_list
479
+ end
480
+
481
+ def header
482
+ h1.header do
483
+ a :href=>'/' do
484
+ img :src=>'/static/peekbot.png', :height=>130, :width=>229
485
+ end
486
+ end
487
+ nav_bar
488
+ end
489
+
490
+ def footer
491
+ p.footer do
492
+ text 'peekbot '
493
+ a "version #{RipXplore::VERSION}" , :href=>'/version_info'
494
+ text ' powered by '
495
+ a 'ripxplore',:href=>'http://ripxplore.rubyforge.org/'
496
+ text ', '
497
+ a 'camping',:href=>'http://code.whytheluckystiff.net/camping/'
498
+ text ' & '
499
+ a 'ruby',:href=>'http://www.ruby-lang.org/'
500
+ i {
501
+ text "(last updated:#{LAST_PEEKBOT_UPDATE} : "
502
+ a 'change log',:href=>'/static/changes.txt'
503
+ text ")"
504
+ }
505
+ br
506
+ text 'whipped up by '
507
+ a 'jonno',:href=>'http://blog.jamtronix.com/'
508
+ text ' at '
509
+ a 'jamtronix dot com',:href=>'http://www.jamtronix.com/'
510
+ br
511
+ br
512
+ text "want to"
513
+ a "help build peekbot?" , :href=>'/help_wanted/'
514
+
515
+ end
516
+ end
517
+
518
+
519
+ def version_info
520
+ table {
521
+ tr {
522
+ td 'RipXplore Version'
523
+ td RipXplore::VERSION
524
+ }
525
+ tr {
526
+ td 'Image Format File Extensions'
527
+ td ImageFormat.all_possible_extensions.join(", ")
528
+ }
529
+
530
+ tr {
531
+ td 'File Systems'
532
+ td FileSystem.all_file_systems.join(", ")
533
+ }
534
+
535
+ tr {
536
+ td 'Native File Types'
537
+ td NativeFileType.all_native_file_types.join(", ")
538
+ }
539
+ }
540
+ end
541
+
542
+ def help_wanted
543
+ h1 "help wanted!"
544
+ text "peekbot is very much a work in progress. I am putting this version out
545
+ now as a 'proof of concept', and to see if anyone finds it intersting
546
+ enough to help work on it. "
547
+ br
548
+ br
549
+ text "Areas where assistance would be most valuable:"
550
+ br
551
+ br
552
+ text" 1) web design. peekbot uses camping, with markaby + css for layout, so
553
+ hopefully someone with css chops could make it look much slicker
554
+ without too much effort. I'm sure the UI and navigation could be much
555
+ improved as well - I am a humble backend hack so would be very happy
556
+ to work with anyone who knew how to make a web app easy to use. "
557
+ br
558
+ br
559
+ text"2) plug-ins for image formats (especially .sdk), file systems, and
560
+ native file types. My goal for ripxplore is to have everything in 100%
561
+ native ruby for maximum portability, and the framework is convoluted
562
+ and not yet well documented, so it's probably a bit early for anyone
563
+ but a masochist ruby freak to try writing a plugin unaided, but I
564
+ would be happy to work with anyone interested, or else if someone has
565
+ some images they'd particularly like to see peekbot interpret, and can
566
+ give specs or source code i can convert, that would be great too.
567
+ There is a heap of opporunity for the C64 particularly, including
568
+ converters to detect various image formats and render as PNG."
569
+ br
570
+ br
571
+ text"3) documentation - how to use the website, how to use ripxplore for
572
+ specialised conversion and extraction tasks & how to write a plugin."
573
+ br
574
+ br
575
+ text"If you are interested in any of the above, contact me vie email at jonno at jamtronix dot com"
576
+ end
577
+ end
578
+
579
+ if __FILE__ == $0 then
580
+ #run from command line
581
+ port=6502
582
+ require 'mongrel'
583
+ require 'mongrel/camping'
584
+ begin
585
+ config = Mongrel::Configurator.new do
586
+ listener :port => port do
587
+ uri "/", :handler => Mongrel::Camping::CampingHandler.new(PeekBot)
588
+ trap("INT") { stop }
589
+ run
590
+ end
591
+ end
592
+ puts "** peekbot is running at http://localhost:#{port}/"
593
+ config.join
594
+ rescue Errno::EADDRINUSE
595
+ puts "** ERROR : port #{port} in use - is peekbot already running?"
596
+ end
597
+
598
+ end
599
+
data/peekbot_helper.rb ADDED
@@ -0,0 +1,153 @@
1
+
2
+ lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
3
+ $:.unshift(lib_path) unless $:.include?(lib_path)
4
+
5
+ require 'RipXplore'
6
+ require 'hpricot'
7
+ require 'open-uri'
8
+
9
+ WEB_PAGE_EXTENSIONS=['.html','.htm','.php','.asp','.aspx']
10
+ class PeekBotHelper
11
+ def PeekBotHelper.get_archive_contents(archive_url)
12
+
13
+ images=[]
14
+ directories=[]
15
+ other_files=[]
16
+ if (url_is_website?(archive_url)) then
17
+ path=URI.split(archive_url)[5]
18
+ directories<<'../' unless (path=="" || path=='/')
19
+ html=get_url(archive_url)
20
+ doc=Hpricot(html)
21
+ doc.search("a[@href]").each do |a|
22
+ href=a.attributes["href"]
23
+ href.sub!("/index.html","/")
24
+ next if href=~/:/ #skip anything that has a colon - we only want relative URLs
25
+ next if href=~/#/ #skip anything that has a hash - we don't want anchored links
26
+ next if href=~/\?/ #skip anything thats a query
27
+ next if href=~/^\.+$/ #skip . and ..
28
+ if (href=~/\w\/$/) || WEB_PAGE_EXTENSIONS.include?(File.extname(href))
29
+ # if !(href=~/^\//) then #directories end with a /, but skip absolute paths
30
+
31
+ directories<<(href)
32
+ # end
33
+ else
34
+ if FileSystemImage.is_file_system_image_filename?(href) then
35
+ images<<href
36
+ else
37
+ other_files<<href unless href=~/\/$/
38
+ end
39
+ end
40
+ end
41
+ elsif (url_is_ftpsite?(archive_url)) then
42
+
43
+ require 'net/ftp'
44
+ require 'uri'
45
+ ftp_uri=URI.parse(archive_url)
46
+ partial_path="/"
47
+ ftp_uri.path.split("/").each do |p|
48
+ next if p.length<1
49
+ directories<<partial_path
50
+ partial_path+=(p+"/")
51
+ end
52
+ ftp=Net::FTP.new(ftp_uri.host)
53
+ ftp.login(ftp_uri.user.nil? ? 'anonymous': ftp_uri.user,ftp_uri.password)
54
+ ftp.nlst(URI.unescape(ftp_uri.path)).each do |f|
55
+ #yes this is a horrible hack - assume that filenames with a . are files, if no dot then assume it's a directory
56
+ if f=~/\./ then
57
+ if FileSystemImage.is_file_system_image_filename?(f) then
58
+ images<<f
59
+ else
60
+ other_files<<f
61
+ end
62
+ else
63
+ f="/"+f unless f=~/\// #if we are at the root directory, make sure all paths start with a /
64
+ directories<<f
65
+ end
66
+ end
67
+ ftp.quit
68
+ else #assume it's a local file
69
+ dirname=PeekBotHelper.file_url_to_pathname(archive_url)
70
+ dirname+=File::Separator unless dirname=~/#{File::Separator}$/
71
+ # "stripped #{archive_url} to #{dirname}"
72
+ Dir.foreach(dirname) do |filename|
73
+ next if filename=="."
74
+ images<<filename if FileSystemImage.is_file_system_image_filename?(filename)
75
+ full_dir_path=file_url_to_pathname(combine_url("#{dirname}",filename))
76
+ directories<<filename if FileTest.directory?(full_dir_path)
77
+ end
78
+ end
79
+ [images.sort,directories.sort,other_files.sort]
80
+ end
81
+
82
+ @@url_cache={}
83
+ def PeekBotHelper.get_url(url)
84
+ if @@url_cache[url].nil? then
85
+ @@url_cache[url]=open(url).read
86
+ end
87
+ @@url_cache[url]
88
+ end
89
+
90
+ def PeekBotHelper.url_is_website?(url)
91
+ url=~/^http[s]?:/ ?true : false
92
+ end
93
+
94
+ def PeekBotHelper.url_is_ftpsite?(url)
95
+ url=~/^ftp?:/ ?true : false
96
+ end
97
+
98
+ def PeekBotHelper.url_is_local_file?(url)
99
+ return true if (url=~/^file:/ ) || (url=~/^[^\/]*\\/ ) || (url[1].chr==':') || (url[0].chr==File::Separator)
100
+ false
101
+ end
102
+
103
+ def PeekBotHelper.file_url_to_pathname(url)
104
+ pathname=URI.unescape(url.sub(/file:\/+/,""))
105
+ # puts pathname
106
+ # puts pathname[1].chr
107
+ pathname="#{File::Separator}#{pathname}" unless ((pathname[1].chr==':') || (pathname[0].chr==File::Separator))
108
+ pathname
109
+ end
110
+ def PeekBotHelper.combine_url(first_url,second_url)
111
+
112
+ # URI::FTP.join seems broken (complains about missing typcode) so here's a horrible hacky replacement
113
+ # puts "joining #{first_url},#{second_url}"
114
+ if url_is_ftpsite?(first_url) then
115
+ ftp_uri=URI.parse(first_url)
116
+ result=URI::FTP.new('ftp',
117
+ "#{ftp_uri.user}:#{ftp_uri.password}",
118
+ ftp_uri.host, ftp_uri.port, nil,
119
+ second_url,
120
+ nil, nil,nil).to_s.sub("//:@","//")
121
+ elsif url_is_local_file?(first_url) then
122
+ dir_path=PeekBotHelper.file_url_to_pathname(first_url)
123
+ result="file://#{File.expand_path(second_url,dir_path)}"
124
+ else
125
+ result=URI.join(first_url,second_url)
126
+ end
127
+ # puts "RESULT: #{result}"
128
+ result
129
+ end
130
+ end
131
+
132
+ class WorkingUrl
133
+ attr_accessor :archive_url,:image_url
134
+ def initialize(archive_url=nil,image_url=nil)
135
+ #when running under Apache + mod_rewrite, the second / in http:// is getting stripped, so add it back in if necessary
136
+ @archive_url=archive_url.sub(/:\/([^\/])/,'://\1').gsub(' ','%20').gsub('#','%23').gsub('[','%5B').gsub(']','%5D') unless archive_url.nil?
137
+ @image_url=image_url.sub(/:\/([^\/])/,'://\1').gsub(' ','%20').gsub('#','%23').gsub('[','%5B').gsub(']','%5D') unless image_url.nil?
138
+ end
139
+
140
+ def image_basename
141
+ if image_url.nil? then
142
+ nil
143
+ else
144
+ URI.unescape(File.basename(image_url))
145
+ end
146
+ end
147
+
148
+
149
+ def WorkingUrl.split_image_url_into_parts(url)
150
+ return WorkingUrl.new(File.dirname(url)+"/",url)
151
+ end
152
+ end
153
+
@@ -0,0 +1,56 @@
1
+ PEEKBOT CHANGE LOG
2
+
3
+ 2008-12-14
4
+ * initial support for ZX81
5
+
6
+ 2008-12-13
7
+ * low-res Coco pictures
8
+
9
+ 2008-12-07
10
+ * detokenise TI 99/4a BASIC files
11
+
12
+ 2008-12-05
13
+ * eliminate timeout (500 camping error) the first time a random picture/text/listing was viewed when app restarted
14
+
15
+ 2008-11-30
16
+ * added 'screendump' feature
17
+
18
+ 2008-11-29
19
+ * now caches PNG & GIF files (rather than re-extracting from image on each view)
20
+ * added random text and listing feature
21
+
22
+ 2008-11-28
23
+ * added RLE support
24
+
25
+ 2008-11-25
26
+ * added GIF support
27
+
28
+ 2008-11-25
29
+ * 'All Pictures' & 'All Texts' views
30
+ * Random Picture page
31
+
32
+ 2008-11-24
33
+ * Apple 2 hires picture improvements
34
+
35
+ 2008-11-21
36
+ * added support for Tandy Color Computer files (Tim Lindner)
37
+
38
+ 2008-11-15
39
+ * fixed some TI 99/4a file system bugs
40
+ * added support for TI Artist images
41
+
42
+
43
+ 2008-11-14
44
+ * added 'help wanted' page
45
+ * added support for FTP archives
46
+ * added link to TI 99/4a archive at ftp.whtech.com
47
+ * partial support for TI 99/4a formats
48
+
49
+ 2008-11-13
50
+ * removed listing (disassembly) of binary files
51
+ * removed links to Apple 2 archives that aren't mainly DSK files
52
+ * made peekbot logo in page header clickable
53
+ * links to hex dumps now marked 'rel=nofollow' (no point getting a hex dump into google)
54
+
55
+ 2008-11-09
56
+ * first public version
Binary file
data/static/style.css ADDED
@@ -0,0 +1,67 @@
1
+ body { background-color: #cde; color: #333; }
2
+
3
+ body, p, ol, ul, td, th {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 22px;
6
+ line-height: 28px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 16px;
13
+ white-space: pre-wrap; /* css-3 */
14
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
15
+ white-space: -pre-wrap; /* Opera 4-6 */
16
+ white-space: -o-pre-wrap; /* Opera 7 */
17
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
18
+
19
+ }
20
+
21
+
22
+ td,th {
23
+ padding-left: 10px;
24
+ padding-right: 10px;
25
+ }
26
+
27
+ tr.header {
28
+ background-color: #aaf;
29
+ }
30
+ pre.hex_dump {
31
+ background-color: #aaf;
32
+ }
33
+
34
+ p.footer {
35
+ font-size: 12px;
36
+ line-height: 16px;
37
+ }
38
+
39
+ h2 {
40
+ background-color: #bcd;
41
+ }
42
+
43
+ h3 {
44
+ background-color: #bcd;
45
+ }
46
+
47
+ img {
48
+ border: 0px;
49
+ }
50
+
51
+ li.directory {
52
+ background-color: #89a;
53
+ }
54
+
55
+ li.image {
56
+ background-color: #9ab;
57
+ }
58
+
59
+ li.other_file {
60
+ background-color: #abc;
61
+ }
62
+
63
+ a { color: #000; }
64
+ a:visited { color: #666; }
65
+ a:hover { color: #fff; background-color:#000; }
66
+
67
+
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: peekbot
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2008-12-20 00:00:00 +11:00
8
+ summary: a web app that allows browsing of disk images as used by vintage computer emulators
9
+ require_paths:
10
+ - lib
11
+ email: jonno@jamtronix.com
12
+ homepage: http://peekbot.rubyforge.org
13
+ rubyforge_project:
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: .
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Jonno Downes
31
+ files:
32
+ - ./dispatch.fcgi
33
+ - ./m.bat
34
+ - ./make_all_images.rb
35
+ - ./peekbot.rb
36
+ - ./peekbot_helper.rb
37
+ - ./static
38
+ - ./static/changes.txt
39
+ - ./static/peekbot.png
40
+ - ./static/style.css
41
+ - static/changes.txt
42
+ - static/peekbot.png
43
+ - static/style.css
44
+ test_files: []
45
+
46
+ rdoc_options: []
47
+
48
+ extra_rdoc_files: []
49
+
50
+ executables:
51
+ - peekbot.rb
52
+ extensions: []
53
+
54
+ requirements: []
55
+
56
+ dependencies:
57
+ - !ruby/object:Gem::Dependency
58
+ name: hpricot
59
+ version_requirement:
60
+ version_requirements: !ruby/object:Gem::Version::Requirement
61
+ requirements:
62
+ - - ">"
63
+ - !ruby/object:Gem::Version
64
+ version: 0.0.0
65
+ version:
66
+ - !ruby/object:Gem::Dependency
67
+ name: ripxplore
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Version::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.0.3
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: camping
77
+ version_requirement:
78
+ version_requirements: !ruby/object:Gem::Version::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.0
83
+ version:
84
+ - !ruby/object:Gem::Dependency
85
+ name: mongrel
86
+ version_requirement:
87
+ version_requirements: !ruby/object:Gem::Version::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 0.3.10
92
+ version: