36 |
kaklik |
1 |
# Written by John Hoffman
|
|
|
2 |
# see LICENSE.txt for license information
|
|
|
3 |
|
|
|
4 |
from Rerequester import Rerequester
|
|
|
5 |
from urllib import quote
|
|
|
6 |
from threading import Event
|
|
|
7 |
from random import randrange
|
|
|
8 |
from string import lower
|
|
|
9 |
import sys
|
|
|
10 |
import __init__
|
|
|
11 |
try:
|
|
|
12 |
True
|
|
|
13 |
except:
|
|
|
14 |
True = 1
|
|
|
15 |
False = 0
|
|
|
16 |
|
|
|
17 |
DEBUG = True
|
|
|
18 |
|
|
|
19 |
|
|
|
20 |
def excfunc(x):
|
|
|
21 |
print x
|
|
|
22 |
|
|
|
23 |
class T2TConnection:
|
|
|
24 |
def __init__(self, myid, tracker, hash, interval, peers, timeout,
|
|
|
25 |
rawserver, disallow, isdisallowed):
|
|
|
26 |
self.tracker = tracker
|
|
|
27 |
self.interval = interval
|
|
|
28 |
self.hash = hash
|
|
|
29 |
self.operatinginterval = interval
|
|
|
30 |
self.peers = peers
|
|
|
31 |
self.rawserver = rawserver
|
|
|
32 |
self.disallow = disallow
|
|
|
33 |
self.isdisallowed = isdisallowed
|
|
|
34 |
self.active = True
|
|
|
35 |
self.busy = False
|
|
|
36 |
self.errors = 0
|
|
|
37 |
self.rejected = 0
|
|
|
38 |
self.trackererror = False
|
|
|
39 |
self.peerlists = []
|
|
|
40 |
|
|
|
41 |
self.rerequester = Rerequester([[tracker]], interval,
|
|
|
42 |
rawserver.add_task, lambda: 0, peers, self.addtolist,
|
|
|
43 |
rawserver.add_task, lambda: 1, 0, 0, 0, '',
|
|
|
44 |
myid, hash, timeout, self.errorfunc, excfunc, peers, Event(),
|
|
|
45 |
lambda: 0, lambda: 0)
|
|
|
46 |
|
|
|
47 |
if self.isactive():
|
|
|
48 |
rawserver.add_task(self.refresh, randrange(int(self.interval/10), self.interval))
|
|
|
49 |
# stagger announces
|
|
|
50 |
|
|
|
51 |
def isactive(self):
|
|
|
52 |
if self.isdisallowed(self.tracker): # whoops!
|
|
|
53 |
self.deactivate()
|
|
|
54 |
return self.active
|
|
|
55 |
|
|
|
56 |
def deactivate(self):
|
|
|
57 |
self.active = False
|
|
|
58 |
|
|
|
59 |
def refresh(self):
|
|
|
60 |
if not self.isactive():
|
|
|
61 |
return
|
|
|
62 |
self.lastsuccessful = True
|
|
|
63 |
self.newpeerdata = []
|
|
|
64 |
if DEBUG:
|
|
|
65 |
print 'contacting %s for info_hash=%s' % (self.tracker, quote(self.hash))
|
|
|
66 |
self.rerequester.snoop(self.peers, self.callback)
|
|
|
67 |
|
|
|
68 |
def callback(self):
|
|
|
69 |
self.busy = False
|
|
|
70 |
if self.lastsuccessful:
|
|
|
71 |
self.errors = 0
|
|
|
72 |
self.rejected = 0
|
|
|
73 |
if self.rerequester.announce_interval > (3*self.interval):
|
|
|
74 |
# I think I'm stripping from a regular tracker; boost the number of peers requested
|
|
|
75 |
self.peers = int(self.peers * (self.rerequester.announce_interval / self.interval))
|
|
|
76 |
self.operatinginterval = self.rerequester.announce_interval
|
|
|
77 |
if DEBUG:
|
|
|
78 |
print ("%s with info_hash=%s returned %d peers" %
|
|
|
79 |
(self.tracker, quote(self.hash), len(self.newpeerdata)))
|
|
|
80 |
self.peerlists.append(self.newpeerdata)
|
|
|
81 |
self.peerlists = self.peerlists[-10:] # keep up to the last 10 announces
|
|
|
82 |
if self.isactive():
|
|
|
83 |
self.rawserver.add_task(self.refresh, self.operatinginterval)
|
|
|
84 |
|
|
|
85 |
def addtolist(self, peers):
|
|
|
86 |
for peer in peers:
|
|
|
87 |
self.newpeerdata.append((peer[1],peer[0][0],peer[0][1]))
|
|
|
88 |
|
|
|
89 |
def errorfunc(self, r):
|
|
|
90 |
self.lastsuccessful = False
|
|
|
91 |
if DEBUG:
|
|
|
92 |
print "%s with info_hash=%s gives error: '%s'" % (self.tracker, quote(self.hash), r)
|
|
|
93 |
if r == self.rerequester.rejectedmessage + 'disallowed': # whoops!
|
|
|
94 |
if DEBUG:
|
|
|
95 |
print ' -- disallowed - deactivating'
|
|
|
96 |
self.deactivate()
|
|
|
97 |
self.disallow(self.tracker) # signal other torrents on this tracker
|
|
|
98 |
return
|
|
|
99 |
if lower(r[:8]) == 'rejected': # tracker rejected this particular torrent
|
|
|
100 |
self.rejected += 1
|
|
|
101 |
if self.rejected == 3: # rejected 3 times
|
|
|
102 |
if DEBUG:
|
|
|
103 |
print ' -- rejected 3 times - deactivating'
|
|
|
104 |
self.deactivate()
|
|
|
105 |
return
|
|
|
106 |
self.errors += 1
|
|
|
107 |
if self.errors >= 3: # three or more errors in a row
|
|
|
108 |
self.operatinginterval += self.interval # lengthen the interval
|
|
|
109 |
if DEBUG:
|
|
|
110 |
print ' -- lengthening interval to '+str(self.operatinginterval)+' seconds'
|
|
|
111 |
|
|
|
112 |
def harvest(self):
|
|
|
113 |
x = []
|
|
|
114 |
for list in self.peerlists:
|
|
|
115 |
x += list
|
|
|
116 |
self.peerlists = []
|
|
|
117 |
return x
|
|
|
118 |
|
|
|
119 |
|
|
|
120 |
class T2TList:
|
|
|
121 |
def __init__(self, enabled, trackerid, interval, maxpeers, timeout, rawserver):
|
|
|
122 |
self.enabled = enabled
|
|
|
123 |
self.trackerid = trackerid
|
|
|
124 |
self.interval = interval
|
|
|
125 |
self.maxpeers = maxpeers
|
|
|
126 |
self.timeout = timeout
|
|
|
127 |
self.rawserver = rawserver
|
|
|
128 |
self.list = {}
|
|
|
129 |
self.torrents = {}
|
|
|
130 |
self.disallowed = {}
|
|
|
131 |
self.oldtorrents = []
|
|
|
132 |
|
|
|
133 |
def parse(self, allowed_list):
|
|
|
134 |
if not self.enabled:
|
|
|
135 |
return
|
|
|
136 |
|
|
|
137 |
# step 1: Create a new list with all tracker/torrent combinations in allowed_dir
|
|
|
138 |
newlist = {}
|
|
|
139 |
for hash, data in allowed_list.items():
|
|
|
140 |
if data.has_key('announce-list'):
|
|
|
141 |
for tier in data['announce-list']:
|
|
|
142 |
for tracker in tier:
|
|
|
143 |
self.disallowed.setdefault(tracker, False)
|
|
|
144 |
newlist.setdefault(tracker, {})
|
|
|
145 |
newlist[tracker][hash] = None # placeholder
|
|
|
146 |
|
|
|
147 |
# step 2: Go through and copy old data to the new list.
|
|
|
148 |
# if the new list has no place for it, then it's old, so deactivate it
|
|
|
149 |
for tracker, hashdata in self.list.items():
|
|
|
150 |
for hash, t2t in hashdata.items():
|
|
|
151 |
if not newlist.has_key(tracker) or not newlist[tracker].has_key(hash):
|
|
|
152 |
t2t.deactivate() # this connection is no longer current
|
|
|
153 |
self.oldtorrents += [t2t]
|
|
|
154 |
# keep it referenced in case a thread comes along and tries to access.
|
|
|
155 |
else:
|
|
|
156 |
newlist[tracker][hash] = t2t
|
|
|
157 |
if not newlist.has_key(tracker):
|
|
|
158 |
self.disallowed[tracker] = False # reset when no torrents on it left
|
|
|
159 |
|
|
|
160 |
self.list = newlist
|
|
|
161 |
newtorrents = {}
|
|
|
162 |
|
|
|
163 |
# step 3: If there are any entries that haven't been initialized yet, do so.
|
|
|
164 |
# At the same time, copy all entries onto the by-torrent list.
|
|
|
165 |
for tracker, hashdata in newlist.items():
|
|
|
166 |
for hash, t2t in hashdata.items():
|
|
|
167 |
if t2t is None:
|
|
|
168 |
hashdata[hash] = T2TConnection(self.trackerid, tracker, hash,
|
|
|
169 |
self.interval, self.maxpeers, self.timeout,
|
|
|
170 |
self.rawserver, self._disallow, self._isdisallowed)
|
|
|
171 |
newtorrents.setdefault(hash,[])
|
|
|
172 |
newtorrents[hash] += [hashdata[hash]]
|
|
|
173 |
|
|
|
174 |
self.torrents = newtorrents
|
|
|
175 |
|
|
|
176 |
# structures:
|
|
|
177 |
# list = {tracker: {hash: T2TConnection, ...}, ...}
|
|
|
178 |
# torrents = {hash: [T2TConnection, ...]}
|
|
|
179 |
# disallowed = {tracker: flag, ...}
|
|
|
180 |
# oldtorrents = [T2TConnection, ...]
|
|
|
181 |
|
|
|
182 |
def _disallow(self,tracker):
|
|
|
183 |
self.disallowed[tracker] = True
|
|
|
184 |
|
|
|
185 |
def _isdisallowed(self,tracker):
|
|
|
186 |
return self.disallowed[tracker]
|
|
|
187 |
|
|
|
188 |
def harvest(self,hash):
|
|
|
189 |
harvest = []
|
|
|
190 |
if self.enabled:
|
|
|
191 |
for t2t in self.torrents[hash]:
|
|
|
192 |
harvest += t2t.harvest()
|
|
|
193 |
return harvest
|