-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathlisting27-2.py
More file actions
127 lines (108 loc) · 3.34 KB
/
listing27-2.py
File metadata and controls
127 lines (108 loc) · 3.34 KB
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
from xmlrpclib import ServerProxy, Fault
from os.path import join, abspath, isfile
from SimpleXMLRPCServer import SimpleXMLRPCServer
from urlparse import urlparse
import sys
SimpleXMLRPCServer.allow_reuse_address = 1
MAX_HISTORY_LENGTH = 6
UNHANDLED = 100
ACCESS_DENIED = 200
class UnhandledQuery(Fault):
"""
An exception that represents an unhandled query.
"""
def __init__(self, message="Couldn't handle the query"):
Fault.__init__(self, UNHANDLED, message)
class AccessDenied(Fault):
"""
An exception that is raised if a user tries to access a
resource for which he or she is not authorized.
"""
def __init__(self, message="Access denied"):
Fault.__init__(self, ACCESS_DENIED, message)
def inside(dir, name):
"""
Checks whether a given file name lies within a given directory.
"""
dir = abspath(dir)
name = abspath(name)
return name.startswith(join(dir, ''))
def getPort(url):
"""
Extracts the port number from a URL.
"""
name = urlparse(url)[1]
parts = name.split(':')
return int(parts[-1])
class Node:
"""
A node in a peer-to-peer network.
"""
def __init__(self, url, dirname, secret):
self.url = url
self.dirname = dirname
self.secret = secret
self.known = set()
def query(self, query, history=[]):
"""
Performs a query for a file, possibly asking other known Nodes for
help. Returns the file as a string.
"""
try:
return self._handle(query)
except UnhandledQuery:
history = history + [self.url]
if len(history) >= MAX_HISTORY_LENGTH: raise
return self._broadcast(query, history)
def hello(self, other):
"""
Used to introduce the Node to other Nodes.
"""
self.known.add(other)
return 0
def fetch(self, query, secret):
"""
Used to make the Node find a file and download it.
"""
if secret != self.secret: raise AccessDenied
result = self.query(query)
f = open(join(self.dirname, query), 'w')
f.write(result)
f.close()
return 0
def _start(self):
"""
Used internally to start the XML-RPC server.
"""
s = SimpleXMLRPCServer(("", getPort(self.url)), logRequests=False)
s.register_instance(self)
s.serve_forever()
def _handle(self, query):
"""
Used internally to handle queries.
"""
dir = self.dirname
name = join(dir, query)
if not isfile(name): raise UnhandledQuery
if not inside(dir, name): raise AccessDenied
return open(name).read()
def _broadcast(self, query, history):
"""
Used internally to broadcast a query to all known Nodes.
"""
for other in self.known.copy():
if other in history: continue
try:
s = ServerProxy(other)
return s.query(query, history)
except Fault, f:
if f.faultCode == UNHANDLED: pass
else: self.known.remove(other)
except:
self.known.remove(other)
raise UnhandledQuery
def main():
url, directory, secret = sys.argv[1:]
n = Node(url, directory, secret)
n._start()
if __name__ == '__main__': main()