-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
260 lines (208 loc) · 8.5 KB
/
main.py
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import argparse
import os
import sys
import time
from math import floor, log
from watchdog.events import PatternMatchingEventHandler
from watchdog.observers import Observer
from progress.bar import Bar
from BloomFilter import BloomFilter
from P2P.Server import NetworkManager
from P2P import utils
import Synchronizer
# Create the parser
my_parser = argparse.ArgumentParser(description='Sync two files')
# Add the arguments
my_parser.add_argument('Path',
metavar='path',
type=str,
help='the file path to sync')
my_parser.add_argument('--host', action='store_true',
help='sets the current the user as host')
# Execute the parse_args() method
args = my_parser.parse_args()
input_path = args.Path
input_path = os.path.abspath(input_path)
role = 1 if args.host else 0
my_missing_content = {}
should_trigger_modified = True
class RequestReceivedHandler:
def handle_request(self, request):
global my_missing_content, should_trigger_modified
if(request.get_type() == utils.Request.REQUEST_TYPE_BLOOMFILTER):
# The opposite party has sent its bloom filter and now requesting ours
# We send it now
print("\n\nThe other user has modified his file, syncing...")
print("Received the bloom filter")
my_missing_content = getMissingContent(getNFromSize(
request.get_message_size()), request.get_message_bytes())
print("Acknowleding and transmitting the bloom filter...")
bf = computeBloomFilter()
req = utils.Request(
utils.Request.REQUEST_TYPE_REPLY_SLAVE_BLOOMFILTER, bf.getAsBytes())
p2p.send_request(req)
elif(request.get_type() == utils.Request.REQUEST_TYPE_REPLY_SLAVE_BLOOMFILTER):
print(
"Request was acknowledged by the other peer and has given the other bloom filter")
my_missing_content = getMissingContent(getNFromSize(
request.get_message_size()), request.get_message_bytes())
# Send the missing contents computed to the other user
print("Sending the actual changed lines ...")
req = utils.Request(
utils.Request.REQUEST_SEND_ACTUAL_LINES, str(my_missing_content))
p2p.send_request(req)
elif(request.get_type() == utils.Request.REQUEST_SEND_ACTUAL_LINES):
print("Received the actual missing lines...")
missing_dict = eval(request.actual_message())
should_trigger_modified = False
print("Syncing the file...")
Synchronizer.syncFile(
input_path, my_missing_content, missing_dict)
print("Verifying that the file is up-to-date ...")
print("Verifying hash...")
final_hash = Synchronizer.computeHash(input_path)
req = utils.Request(
utils.Request.REQUEST_SEND_ENTIRE_FILE_HASH, final_hash)
p2p.send_request(req)
print("Done.")
time.sleep(1)
should_trigger_modified = True
elif(request.get_type() == utils.Request.REQUEST_SEND_ENTIRE_FILE_HASH):
print("Received the hash from the other side ...")
print("Verifying hash...")
hash_from_other_user = request.get_message_bytes()
our_hash = Synchronizer.computeHash(input_path)
if hash_from_other_user == our_hash:
print("Done verifying hash.")
else:
req = utils.Request(
utils.Request.REQUEST_SEND_ENTIRE_FILE, read_entire_file())
p2p.send_request(req)
print("Done.")
elif(request.get_type() == utils.Request.REQUEST_SEND_ENTIRE_FILE):
file_content_from_other_user = request.actual_message()
should_trigger_modified = False
with open(input_path, 'w') as f:
f.write(file_content_from_other_user)
time.sleep(1)
should_trigger_modified = True
print("Done.")
rh = RequestReceivedHandler()
p2p = NetworkManager(rh)
if not os.path.isfile(input_path):
print('\n', input_path, '- Not a valid file to stage for syncing')
sys.exit()
def on_modified(event):
if (os.path.abspath(event.src_path) == input_path) and should_trigger_modified:
# Detect changes from only the given path.
# Ignore all other changes
print("\n\nDetected changes - ",
event.src_path, "py has been modified...")
initiateSync()
class FileEventHandler(PatternMatchingEventHandler):
def __init__(self, patterns=None, ignore_patterns=None, ignore_directories=False, case_sensitive=False,
on_modified_callback=on_modified):
self.on_modified_callback = on_modified_callback
self.last_modified = time.time()
return super().__init__(patterns=patterns, ignore_patterns=ignore_patterns,
ignore_directories=ignore_directories, case_sensitive=case_sensitive)
def on_modified(self, event):
if(time.time() - self.last_modified) > 1:
self.on_modified_callback(event)
self.last_modified = time.time()
return super().on_modified(event)
# Use this func to find n required for BloomFilter
# Size is the len of bloomfilter bit array
def getNFromSize(size):
return(floor(size*-1*(log(2)**2)/log(0.05)))
def main():
print("\nInteract [Version 1.0]")
print("GNU GENERAL PUBLIC LICENSE\nVersion 3, 23 Oct 2019\n")
patterns = "*"
ignore_patterns = ["*.save"]
ignore_directories = True
case_sensitive = True
file_event_handler = FileEventHandler(patterns, ignore_patterns, ignore_directories,
case_sensitive, on_modified_callback=on_modified)
path = "."
go_recursively = True
my_observer = Observer()
my_observer.schedule(file_event_handler, path, recursive=go_recursively)
my_observer.start()
if role == 1:
progress = Bar("Creating Server")
for i in range(10):
time.sleep(0.05)
progress.next(10)
progress.finish()
p2p.create_host()
initiateSync()
else:
ip = input("Enter the IP of the host: ")
port = int(input("Enter the PORT of the host: "))
progress = Bar("Initiating Connection")
for i in range(10):
# Do some work
time.sleep(0.05)
progress.next(10)
progress.finish()
p2p.create_client(ip, port)
print("Observing ", input_path, "for changes...")
try:
while True:
# This handles the request appropriately
a = p2p.check_if_incoming_data()
# request_type, request_data = a[0], a[1]
time.sleep(1)
except KeyboardInterrupt:
my_observer.stop()
my_observer.join()
def computeBloomFilter():
filename = input_path
# total # of line in the file
user_file_NOL = 0
# content frequency
user_file_content = {}
with open(filename) as user_file:
for line in user_file:
user_file_NOL += 1
# creates bloomfilter of required size
bloom_filter = BloomFilter(user_file_NOL)
# read contents of file and insert into BF
with open(filename) as user_file:
for line in user_file:
try:
user_file_content[line] += 1
except:
user_file_content[line] = 1
bloom_filter.insert(line, freq=user_file_content[line])
return bloom_filter
def getMissingContent(n, bloomfilter_bytes):
missing_content = {}
receivedBF = BloomFilter(n)
receivedBF.readBloomFilterFromBytes(bloomfilter_bytes)
user_file_content = {}
line_number = 0
with open(input_path) as user_file:
for line in user_file:
line_number += 1
try:
user_file_content[line] += 1
except:
user_file_content[line] = 1
if not receivedBF.validate(line, freq=user_file_content[line]):
missing_content[line_number] = line
return(missing_content)
def initiateSync():
print("Redrawing the bloom filter ...")
bf = computeBloomFilter()
print("Sending the bloom filter ...")
req = utils.Request(
utils.Request.REQUEST_TYPE_BLOOMFILTER, bf.getAsBytes())
p2p.send_request(req)
def read_entire_file():
with open(input_path) as f:
content = f.read()
return content
if __name__ == "__main__":
main()