This file is indexed.

/usr/lib/ruby/vendor_ruby/rubytorrent/metainfo.rb is in ruby-rubytorrent 0.3-5.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
## metainfo.rb -- parsed .torrent file
## Copyright 2004 William Morgan.
##
## This file is part of RubyTorrent. RubyTorrent is free software;
## you can redistribute it and/or modify it under the terms of version
## 2 of the GNU General Public License as published by the Free
## Software Foundation.
##
## RubyTorrent is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License (in the file COPYING) for more details.

require "rubytorrent/typedstruct"
require 'uri'
require 'open-uri'
require 'digest/sha1'

## MetaInfo file is the parsed form of the .torrent file that people
## send around. It contains a MetaInfoInfo and possibly some
## MetaInfoInfoFile objects.
module RubyTorrent

class MetaInfoFormatError < StandardError; end

class MetaInfoInfoFile
  def initialize(dict=nil)
    @s = TypedStruct.new do |s|
      s.field :length => Integer, :md5sum => String, :sha1 => String,
              :path => String
      s.array :path
      s.required :length, :path
    end

    @dict = dict
    unless dict.nil?
      @s.parse dict
      check
    end
  end

  def method_missing(meth, *args)
    @s.send(meth, *args)
  end

  def check
    raise MetaInfoFormatError, "invalid file length" unless @s.length >= 0
  end

  def to_bencoding
    check
    (@dict || @s).to_bencoding
  end
end

class MetaInfoInfo
  def initialize(dict=nil)
    @s = TypedStruct.new do |s|
      s.field :length => Integer, :md5sum => String, :name => String,
              :piece_length => Integer, :pieces => String,
              :files => MetaInfoInfoFile, :sha1 => String
      s.label :piece_length => "piece length"
      s.required :name, :piece_length, :pieces
      s.array :files
      s.coerce :files => lambda { |x| x.map { |y| MetaInfoInfoFile.new(y) } }
    end

    @dict = dict
    unless dict.nil?
      @s.parse dict
      check
      if dict["sha1"]
        ## this seems to always be off. don't know how it's supposed
        ## to be calculated, so fuck it.
#        puts "we have #{sha1.inspect}, they have #{dict['sha1'].inspect}"
#        rt_warning "info hash SHA1 mismatch" unless dict["sha1"] == sha1
#        raise MetaInfoFormatError, "info hash SHA1 mismatch" unless dict["sha1"] == sha1
      end
    end
  end

  def check
    raise MetaInfoFormatError, "invalid file length" unless @s.length.nil? || @s.length >= 0
    raise MetaInfoFormatError, "one (and only one) of 'length' (single-file torrent) or 'files' (multi-file torrent) must be specified" if (@s.length.nil? && @s.files.nil?) || (!@s.length.nil? && !@s.files.nil?)
    if single?
      length = @s.length
    else
      length = @s.files.inject(0) { |s, x| s + x.length }
    end
    raise MetaInfoFormatError, "invalid metainfo file: length #{length} > (#{@s.pieces.length / 20} pieces * #{@s.piece_length})" unless length <= (@s.pieces.length / 20) * @s.piece_length
    raise MetaInfoFormatError, "invalid metainfo file: pieces length = #{@s.pieces.length} not a multiple of 20" unless (@s.pieces.length % 20) == 0
  end

  def to_bencoding
    check
    (@dict || @s).to_bencoding
  end

  def sha1
    if @s.dirty
      @sha1 = Digest::SHA1.digest(self.to_bencoding)
      @s.dirty = false
    end
    @sha1
  end

  def single?
    !length.nil?
  end

  def multiple?
    length.nil?
  end

  def total_length
    if single?
      length
    else
      files.inject(0) { |a, f| a + f.length }
    end
  end

  def num_pieces
    pieces.length / 20
  end

  def method_missing(meth, *args)
    @s.send(meth, *args)
  end
end

class MetaInfo
  def initialize(dict=nil)
    raise TypeError, "argument must be a Hash (maybe see MetaInfo.from_location)" unless dict.is_a? Hash
    @s = TypedStruct.new do |s|
      s.field :info => MetaInfoInfo, :announce => URI::HTTP,
              :announce_list => Array, :creation_date => Time,
              :comment => String, :created_by => String, :encoding => String
      s.label :announce_list => "announce-list", :creation_date => "creation date",
              :created_by => "created by"
      s.array :announce_list
      s.coerce :info => lambda { |x| MetaInfoInfo.new(x) },
               :creation_date => lambda { |x| Time.at(x) },
               :announce => lambda { |x| URI.parse(x) },
               :announce_list => lambda { |x| x.map { |y| y.map { |z| URI.parse(z) } } }
    end

    @dict = dict
    unless dict.nil?
      @s.parse dict
      check
    end
  end

  def single?; info.single?; end
  def multiple?; info.multiple?; end

  def check
    if @s.announce_list
      @s.announce_list.each do |tier|
        tier.each { |track| raise MetaInfoFormatError, "expecting HTTP URL in announce-list, got #{track} instead" unless track.is_a? URI::HTTP }
      end
    end
  end

  def self.from_bstream(bs)
    dict = nil
    bs.each do |e|
      if dict == nil
        dict = e
      else
        raise MetaInfoFormatError, "too many bencoded elements for metainfo file (just need one)"
      end
    end

    raise MetaInfoFormatError, "bencoded element must be a dictionary, got a #{dict.class}" unless dict.kind_of? ::Hash

    MetaInfo.new(dict)
  end

  ## either a filename or a URL
  def self.from_location(fn, http_proxy=ENV["http_proxy"])
    if http_proxy # lame!
      open(fn, "rb", :proxy => http_proxy) { |f| from_bstream(BStream.new(f)) }
    else
      open(fn, "rb") { |f| from_bstream(BStream.new(f)) }
    end
  end

  def self.from_stream(s)
    from_bstream(BStream.new(s))
  end

  def method_missing(meth, *args)
    @s.send(meth, *args)
  end

  def to_bencoding
    check
    (@dict || @s).to_bencoding
  end

  def trackers
    if announce_list && (announce_list.length > 0)
      announce_list.map do |tier|
        tier.extend(ArrayShuffle).shuffle
      end.flatten
    else
      [announce]
    end
  end
end

end