Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
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