peekbot 0.0.1

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.
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: