snoopy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-24.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ require "rubygems"
7
+ require "snoopy"
8
+
9
+ ##################################################################################
10
+
11
+ opts = Trollop::options do
12
+ version "snoopy 0.0.4 (c) Ari Brown"
13
+ banner <<-EOS
14
+ snoopy is a multithreaded golden retriever for the internet
15
+ --> handles http, ftp (single threaded), and torrent
16
+
17
+ Usage:
18
+ snoopy <URI of file> [options]
19
+ Example:
20
+ snoopy http://www.lighttpd.net -t 5 option
21
+ where [options] are:
22
+ EOS
23
+
24
+ opt :verbose, "Be as loud as possible"
25
+ opt :clean, "Remove the out file after finishing (debugging)"
26
+ opt :out, "Where to save the file", :type => String
27
+ opt :threads, "Set number of threads to use", :default => 5
28
+ opt :dots, "Display a dot every X times a block comes in", :default => 10
29
+ end
30
+ Trollop::die "need a file to download" if ARGV.empty?
31
+
32
+ ##################################################################################
33
+
34
+ opts[:in] = ARGV[0]
35
+
36
+ url = URI.parse(opts[:in])
37
+
38
+ ball = Source.new(url, opts)
39
+
40
+ def start_report(opts)
41
+ puts Time.now
42
+ puts "Downloading #{opts[:in]}"
43
+ puts "with #{opts[:threads]} thread" + (opts[:threads] > 1 ? "s" : '')
44
+ puts
45
+ end
46
+
47
+ def determine_size(value)
48
+ if value >= 1073741824
49
+ end_value = value / 1073741824
50
+ unit = 'GB'
51
+ elsif value >= 1048576 and value < 1073741824
52
+ end_value = value / 1048576
53
+ unit = 'MB'
54
+ elsif value >= 1024 and value < 1048576
55
+ end_value = value / 1024
56
+ unit = 'KB'
57
+ else
58
+ end_value = value
59
+ unit = 'B'
60
+ end
61
+
62
+ [end_value, unit, value]
63
+ end
64
+
65
+ def end_report(source, opts)
66
+ puts
67
+ puts "Download file written to " + opts[:out] + (opts[:clean] ? ", and then deleted" : '')
68
+ puts "Elapsed time: " + $elapsed_time.to_s + " seconds"
69
+
70
+ end_value, unit, actual = determine_size(source.length)
71
+
72
+ if end_value.zero?
73
+ end_value, unit, actual = determine_size(File.size(opts[:out]))
74
+ end
75
+
76
+ puts "Total downloaded: " + end_value.to_s + " " + unit
77
+ res = determine_size(actual/$elapsed_time)
78
+ puts "At %.2f%s/s" % [res[0], res[1]]
79
+
80
+ source.finish
81
+
82
+
83
+ end
84
+
85
+
86
+
87
+ start_report(opts)
88
+ ##################################################################################
89
+ $start_time = Time.now
90
+ ##################################################################################
91
+
92
+ ball.roll
93
+
94
+ ##################################################################################
95
+ $elapsed_time = Time.now - $start_time
96
+ ##################################################################################
97
+ end_report(ball, opts)
@@ -0,0 +1,22 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module FTP
7
+
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ end
11
+
12
+ def prep
13
+ end
14
+
15
+ def fetch
16
+ Net::FTP.open(@opts[:in]) {|ftp| ftp.getbinaryfile(@opts[:in], @opts[:out]) }
17
+ end
18
+
19
+ def clean
20
+ end
21
+
22
+ end
@@ -0,0 +1,94 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module HTTP
7
+ class << self
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ @url = uri
11
+ end
12
+
13
+ def prep
14
+ req = Net::HTTP.new(@url.host, @url.port)
15
+ len = req.request_head(@url.page)['content-length']
16
+ @url.length = len.to_i
17
+
18
+ if len.nil? or @url.length == 0
19
+ @opts[:threads] = 1
20
+ puts "--> Uhoh! Server isn't cooperating..."
21
+ puts "--> Changing threadcount to 1"
22
+ alias_method :fetch_multi, :fetch
23
+ alias_method :fetch, :fetch_single
24
+ end
25
+ end
26
+
27
+ def fetch_single
28
+ p single
29
+ open(@opts[:out], "w") do |f|
30
+ http = Net::HTTP.new(@url.host, @url.port)
31
+ http.get(@url.page) do |str|
32
+ f.write str
33
+ end
34
+ end
35
+ end
36
+
37
+ def fetch
38
+ i = 0
39
+ array = []
40
+ start = 0
41
+ jump = @url.length / @opts[:threads]
42
+ finish = -1 # -1 so that start becomes 0
43
+
44
+ @opts[:threads].times do |t|
45
+ start = finish + 1
46
+ finish = (start + jump) - 1
47
+ array << [start, finish, t + 1]
48
+ end
49
+
50
+ array.each_simultaneously { |d, b, w|
51
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
52
+ open(@opts[:out], "a+") do |f|
53
+ begin
54
+ f.seek(d)
55
+ Net::HTTP.new(@url.host, @url.port).start do |http|
56
+ http.get(@url.page, {'range' => "bytes=#{d}-#{b}"}) do |str|
57
+ i += 1
58
+ print '.' if i % @opts[:dots] == 0
59
+ STDOUT.flush
60
+ f.write str if str
61
+ end
62
+ end
63
+ rescue Timeout::Error => e
64
+ print "\n[Error] Error in server response in thread #{w}. Retrying!\n"
65
+ puts e
66
+ STDOUT.flush
67
+ sleep 5
68
+ retry
69
+ rescue => e
70
+ print "\n[Error] Error in server response in thread #{w}. Retrying!\n"
71
+ puts e
72
+ STDOUT.flush
73
+ sleep 5
74
+ retry
75
+ end
76
+ end
77
+ puts "[LOG] Thread #{w} finished!" if @opts[:verbose]
78
+ }
79
+ end
80
+
81
+ def clean
82
+ remove_extra_files
83
+ end
84
+
85
+ def remove_extra_files
86
+ puts "[LOG] Writing #{@opts[:out]}" if @opts[:verbose]
87
+
88
+ if @opts[:clean]
89
+ File.delete(@opts[:out])
90
+ puts "[LOG] Removing #{@opts[:out]}" if @opts[:verbose]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,113 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module HTTPS
7
+ class << self
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ @url = uri
11
+ end
12
+
13
+ def prep
14
+ req = Net::HTTP.new(@url.host, @url.port)
15
+ req.use_ssl = true
16
+ len = req.request_head(@url.page)['content-length']
17
+ @url.length = len.to_i
18
+
19
+ if len.nil?
20
+ @opts[:threads] = 1
21
+ puts "--> Uhoh! Server isn't cooperating..."
22
+ puts "--> Changing threadcount to 1"
23
+ end
24
+ end
25
+
26
+ def fetch
27
+ array = []
28
+ start = 0
29
+ jump = @url.length / @opts[:threads]
30
+ finish = -1 # -1 so that start becomes 0
31
+
32
+ @opts[:threads].times do |t|
33
+ start = finish + 1
34
+ finish = (start + jump) - 1
35
+ array << [start, finish, t + 1]
36
+ end
37
+
38
+ array.each_simultaneously { |d, b, w|
39
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
40
+ p @opts[:out]
41
+ open(@opts[:out], "a+") do |f|
42
+ f.seek(d)
43
+ http = Net::HTTP.new(@url.host, @url.port)
44
+ http.use_ssl = true
45
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
46
+ c = http.start do |http|
47
+ a = Net::HTTP::Get.new(@url.page)
48
+ a.range = (d..b)
49
+ http.request(a)
50
+ end
51
+ f.write(c.body)
52
+ end
53
+ }
54
+ end
55
+ =begin
56
+ def fetch_fat
57
+ array = []
58
+ start = 0
59
+ jump = @url.length / @opts[:threads]
60
+ finish = -1 # -1 so that start becomes 0
61
+
62
+ @opts[:threads].times do |t|
63
+ start = finish + 1
64
+ finish = (start + jump) - 1
65
+ array << [start, finish, t + 1]
66
+ end
67
+
68
+ array.each_simultaneously { |d, b, w|
69
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
70
+ open("/tmp/part#{w}", "w") do |f|
71
+ http = Net::HTTP.new(@url.host, @url.port)
72
+ c = http.start do |http|
73
+ a = Net::HTTP::Get.new(@url.page)
74
+ a.range = (d..b)
75
+ http.request(a)
76
+ end
77
+ f.write(c.body)
78
+ end
79
+ }
80
+ end
81
+ =end
82
+ def clean
83
+ mash_files
84
+ remove_extra_files
85
+ end
86
+
87
+ def remove_extra_files
88
+ puts "[LOG] Writing #{@opts[:out]}" if @opts[:verbose]
89
+
90
+ Dir["/tmp/part*"].each do |part|
91
+ File.delete(part)
92
+ puts "[LOG] Removing #{part}" if @opts[:verbose]
93
+ end
94
+
95
+ if @opts[:clean]
96
+ File.delete(@opts[:out])
97
+ puts "[LOG] Removing #{@opts[:out]}" if @opts[:verbose]
98
+ end
99
+ end
100
+
101
+ def mash_files
102
+ open(@opts[:out], "a") do |f|
103
+ files = Dir["/tmp/part*"].sort_by { |word|
104
+ word.scan(/\d+/)[0].to_i
105
+ }
106
+ files.each do |part|
107
+ puts "[LOG] Reading #{part}" if @opts[:verbose]
108
+ f.print open(part, "r").read
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,22 @@
1
+ require "rubygems"
2
+ require "trollop"
3
+ require "net/http"
4
+ require "net/https"
5
+ require "net/ftp"
6
+ require "rubytorrent"
7
+ require "tempfile"
8
+ require "open-uri"
9
+
10
+ require File.join(File.dirname(__FILE__), "source")
11
+ require File.join(File.dirname(__FILE__), "http")
12
+ require File.join(File.dirname(__FILE__), "https")
13
+ require File.join(File.dirname(__FILE__), "ftp")
14
+
15
+
16
+ module Enumerable
17
+ def each_simultaneously
18
+ threads = []
19
+ each { |e, f, i| threads << Thread.new { yield e, f, i } }
20
+ return threads.each {|t| t.join }
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ class Source
7
+
8
+ attr_accessor :port, :scheme, :host, :path, :length, :page, :full
9
+
10
+ def initialize(uri_obj, opts)
11
+ @uri = uri_obj
12
+ @host = uri_obj.host
13
+ @port = uri_obj.port
14
+ @scheme = uri_obj.scheme
15
+ @page = (uri_obj.path =~ /^$/ ? "/" : uri_obj.path)
16
+ @full = @scheme + '://' + @host + @page
17
+ @in = opts[:in]
18
+ @out = opts[:out]
19
+ opts[:out] = opts[:out] || (File.basename(@page) =~ /^\/$/ ? 'default' : File.basename(@page))
20
+ opts[:out] = (File.directory?(opts[:out]) ? File.join(opts[:out], File.basename(@page)) : opts[:out])
21
+
22
+ case @scheme
23
+ when "http" then @prefix = HTTP
24
+ when "https" then @prefix = HTTPS
25
+ when "ftp" then @prefix = FTP
26
+ when "torrent" then @prefix = Torrent
27
+ else
28
+ abort "Unknown URL scheme!\nsnoopy --help for help and usage"
29
+ end
30
+
31
+ prep(opts)
32
+
33
+ end
34
+
35
+ def start
36
+ @prefix.prep
37
+ @prefix.fetch
38
+ end
39
+ alias_method :roll, :start
40
+
41
+ def finish
42
+ @prefix.clean
43
+ end
44
+
45
+ private
46
+ def prep(opts)
47
+ @prefix.setup(opts, self)
48
+ end
49
+
50
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module Torrent
7
+ class << self
8
+
9
+ def setup(opts, uri)
10
+ @opts = opts
11
+ @url = uri
12
+ end
13
+
14
+ def prep
15
+ begin
16
+ mi = RubyTorrent::MetaInfo.from_location(File.join(@url.host, @url.path), ENV["http_proxy"])
17
+ rescue RubyTorrent::MetaInfoFormatError, RubyTorrent::BEncodingError => e
18
+ abort "[Error] Can't parse metainfo file '#{torrent}' -- maybe not a .torrent?"
19
+ rescue RubyTorrent::TypedStructError => e
20
+ raise e
21
+ rescue IOError, SystemCallError => e
22
+ abort "[Error] Can't read file '#{torrent}': #{e.message}"
23
+ end
24
+
25
+
26
+ if FileTest.directory?(@opts[:out]) && mi.info.single?
27
+ dest = File.join(@opts[:out], mi.info.name)
28
+ elsif FileTest.file?(@opts[:out]) && mi.info.multiple?
29
+ abort "[Error] .torrent contains multiple files, but '#{dest}' is a single file (must be a directory)"
30
+ end
31
+ dest ||= @opts[:out]
32
+
33
+ print "[LOG] Checking file status: " if @opts[:verbose]
34
+ package = RubyTorrent::Package.new(mi, dest) do |piece|
35
+ print(piece.complete? && piece.valid? ? "#" : ".") if @opts[:verbose]
36
+ end
37
+ puts " done" if @opts[:verbose]
38
+ end
39
+
40
+ def fetch
41
+ puts "[Log] Starting peer..." if @opts[:verbose]
42
+ bt = RubyTorrent::BitTorrent.new(mi, package, :http_proxy => proxy)
43
+
44
+ if @opts[:verbose]
45
+ bt.on_event(self, :added_peer) { |s, p| puts "[Log] Connected to peer #{p}" }
46
+ bt.on_event(self, :received_block) { |s, b, peer| puts "<-- got block #{b} from peer #{peer}, now #{package.pieces[b.pindex].percent_done.f}% done and #{package.pieces[b.pindex].percent_claimed.f}% claimed" }
47
+ bt.on_event(self, :sent_block) { |s, b, peer| puts "--> Sent block #{b} to peer #{peer}" }
48
+ bt.on_event(self, :discarded_piece) { |s, p| puts "[Log] Checksum error on piece #{p}, discarded" }
49
+ bt.on_event(self, :tracker_connected) { |s, url| puts "--> Connected to tracker #{url}" }
50
+ bt.on_event(self, :tracker_lost) { |s, url| puts "--> Couldn't connect to tracker #{url}" }
51
+ end
52
+
53
+ # Always register this
54
+ bt.on_event(self, :complete) do
55
+ @url.length = bt.dlamt
56
+ puts "--> Download complete!"
57
+ end
58
+
59
+ puts "[Log] Listening on #{bt.ip}:#{bt.port}" if @opts[:verbose]
60
+
61
+
62
+ thread = Thread.new do
63
+ until bt.complete?
64
+ previous = ''
65
+
66
+ if bt.tracker
67
+
68
+ current = '%s: %dk of %dk (%.2f%% complete)' % [Time.now,
69
+ bt.bytes_completed / 1024,
70
+ bt.total_bytes / 1024,
71
+ bt.percent_completed]
72
+ print "\b" * previous.size
73
+ print curent
74
+ previous = ''
75
+
76
+ sleep(5)
77
+ else
78
+ sleep(5)
79
+ end
80
+ end
81
+ end
82
+
83
+ thread.join
84
+ end
85
+
86
+ def clean
87
+ end
88
+
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snoopy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ari Brown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-01-14 00:00:00 -05:00
13
+ default_executable: snoopy
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubytorrent
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0.3"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: trollop
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.0.1
32
+ version:
33
+ description: Segmented downloader for HTTP(S)/FTP/Bit Torrent.
34
+ email: ari@aribrown.com
35
+ executables:
36
+ - snoopy
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/ftp.rb
43
+ - lib/http.rb
44
+ - lib/https.rb
45
+ - lib/snoopy.rb
46
+ - lib/source.rb
47
+ - lib/torrent.rb
48
+ - bin/snoopy
49
+ has_rdoc: false
50
+ homepage: http://snoopy.rubyforge.org/
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.0.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: Snoopy is a golden retriever for the internet. It's a great big world and there... And you need a great big dog to do your bidding.
75
+ test_files: []
76
+