munyal-client/client.py

360 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ftplib
import json
import os
import pathlib
import socket
import sys
from copy import deepcopy
from random import randint
from shutil import rmtree
from threading import Thread
from time import sleep
from tkinter import Button, Entry, Label, StringVar, Tk
from PIL import Image, ImageTk
from requests import post
# import rethinkdb as r
from compare_json import compare_json
from dir_to_json import get_json
# from tcping import Ping
#ORIGINAL = "/home/kirbylife/Proyectos/munyal_test/another"
ORIGINAL = "/home/kirbylife/Proyectos/munyal_test/"
IP = "localhost"
USERNAME = "munyal"
PASSWORD = "123"
HOSTNAME = socket.gethostname() + str(randint(1, 100000000))
SKIP_UPLOAD = []
FLAG_DOWNLOAD = False
pending_routes = []
def check_network(port):
ping = Ping(IP, port, 20)
ping.ping(1)
print(SKIP_UPLOAD)
return ping.result.rows[0].successed == 1
def watch_dir():
global pending_routes
folder = os.path.join(os.getenv("HOME"), ".munyal")
if not os.path.exists(folder):
pathlib.Path(folder).mkdir(parents=True)
actual_file = os.path.join(folder, "actual.json")
if not os.path.exists(actual_file):
with open(actual_file, "w") as f:
f.write(json.dumps([]))
pending_file = os.path.join(folder, "pending_routes.json")
if not os.path.exists(pending_file):
with open(pending_file, "w") as f:
f.write(json.dumps([]))
with open(actual_file, "r") as f:
actual = json.loads(f.read())
actual = get_json(ORIGINAL)
new = deepcopy(actual)
with open(pending_file, "r") as f:
pending_routes = json.loads(f.read())
new = get_json(ORIGINAL)
while True:
sleep(0.2)
while True:
try:
jsons = compare_json(deepcopy(actual), deepcopy(new))
except:
new = get_json(ORIGINAL)
else:
break
changes = get_changes(jsons)
pending_routes = pending_routes + changes
with open(pending_file, "w") as f:
f.write(json.dumps(pending_routes, indent=4))
actual = deepcopy(new)
with open(actual_file, "w") as f:
f.write(json.dumps(actual, indent=4))
while True:
try:
new = get_json(ORIGINAL)
except:
pass
else:
break
def need_deleted(items, route):
out = []
for item in items:
if item.get("is_file"):
out.append({
"action": "delete",
"route": os.path.join(route, item.get('name'))
})
else:
if item.get('content'):
out = out + need_deleted(item.get("content"),
os.path.join(route, item.get('name')))
else:
out.append({
"action": "delete_folder",
"route": os.path.join(route, item.get('name'))
})
return out
def need_added(items, route):
out = []
for item in items:
if item.get("is_file"):
out.append({
"action": "add",
"route": os.path.join(route, item.get('name'))
})
else:
if item.get('content'):
out = out + need_added(item.get("content"),
os.path.join(route, item.get('name')))
else:
out.append({
"action": "add_folder",
"route": os.path.join(route, item.get('name'))
})
return out
def get_changes(jsons, route=''):
delete, add = jsons
out = need_deleted(delete, route) + need_added(add, route)
return out
def _is_ftp_dir(ftp_handle, name):
original_cwd = ftp_handle.pwd()
try:
ftp_handle.cwd(name)
ftp_handle.cwd(original_cwd)
return True
except:
return False
def _make_parent_dir(fpath):
#dirname = os.path.dirname(fpath)
dirname = os.path.join(ORIGINAL, fpath)
while not os.path.exists(dirname):
try:
os.makedirs(dirname)
print("created {0}".format(dirname))
except:
_make_parent_dir(dirname)
def _download_ftp_file(ftp_handle, name, dest, overwrite):
if not os.path.exists(dest) or overwrite is True:
try:
with open(dest, 'wb') as f:
ftp_handle.retrbinary("RETR {0}".format(name), f.write)
print("downloaded: {0}".format(dest))
except FileNotFoundError:
print("FAILED: {0}".format(dest))
else:
print("already exists: {0}".format(dest))
def _mirror_ftp_dir(ftp_handle, name, overwrite):
for item in ftp_handle.nlst(name):
SKIP_UPLOAD.append(item)
if _is_ftp_dir(ftp_handle, item):
_make_parent_dir(item.lstrip("/"))
_mirror_ftp_dir(ftp_handle, os.path.join(name, item), overwrite)
else:
_download_ftp_file(ftp_handle, item, os.path.join(ORIGINAL, item),
overwrite)
def download_ftp_tree(overwrite=False):
FLAG_DOWNLOAD = True
ftp_handle = ftplib.FTP(IP, USERNAME, PASSWORD)
path = ""
original_directory = os.getcwd()
os.chdir(ORIGINAL)
_mirror_ftp_dir(ftp_handle, path, overwrite)
os.chdir(original_directory)
ftp_handle.close()
FLAG_DOWNLOAD = False
def upload(*args):
global SKIP_UPLOAD
global pending_routes
print("Modulo de subida listo")
while True:
sleep(0.1)
if check_network('21') and pending_routes:
change = pending_routes.pop(0)
if change['route'] not in SKIP_UPLOAD:
ftp = ftplib.FTP(IP, USERNAME, PASSWORD)
route = os.path.join(ORIGINAL, change['route'])
success = False
while not success:
try:
if change['action'] == 'add':
while FLAG_DOWNLOAD:
print("Wait")
print("Agregar archivo")
with open(route, "rb") as f:
ftp.storbinary("STOR /" + change['route'], f)
elif change['action'] == 'add_folder':
print("Agregar carpeta")
ftp.mkd(change['route'])
elif change['action'] == 'delete':
print("Borrar archivo")
ftp.delete(change['route'])
elif change['action'] == 'delete_folder':
print("Borrar carpeta")
ftp.rmd(change['route'])
else:
print("Unexpected action")
except:
print("Error uploading\n")
r = post("http://" + IP + ':8000/upload',
data={
'host': HOSTNAME,
'action': change['action'],
'route': change['route']
})
r = json.loads(r.text)
print(json.dumps(r, indent=4))
success = r['status'] == 'ok'
ftp.close()
else:
SKIP_UPLOAD.pop()
return 0
def download(*args):
global SKIP_UPLOAD
while True:
sleep(1)
if check_network(28015) and check_network(21):
try:
download_ftp_tree(overwrite=False)
print("Modulo de descarga listo")
print("Carpeta " + ORIGINAL)
r.connect(IP, 28015).repl()
cursor = r.table("changes").changes().run()
for document in cursor:
change = document['new_val']
#print(change)
if change['host'] != HOSTNAME:
route = os.path.join(ORIGINAL, change['route'])
SKIP_UPLOAD.append(change['route'])
try:
if change['action'] == 'add':
print("Agregar archivo")
FLAG_DOWNLOAD = True
ftp = ftplib.FTP(IP, USERNAME, PASSWORD)
with open(route, "wb") as f:
ftp.retrbinary("RETR /" + change['route'],
f.write)
ftp.close()
FLAG_DOWNLOAD = False
elif change['action'] == 'add_folder':
print("Agregar carpeta")
pathlib.Path(route).mkdir(parents=True)
elif change['action'] == 'delete':
print("Borrar archivo")
pathlib.Path(route).unlink()
elif change['action'] == 'delete_folder':
print("Borrar carpeta")
rmtree(route)
else:
print("Unexpected action")
except OSError as e:
print("Error en el sistema operativo")
except:
print("Error en el servidor FTP")
except r.errors.ReqlDriverError as e:
print("Conection refused with rethinkdb")
return 0
def run_client(window, password, username, host, folder):
global PASSWORD
global IP
global USERNAME
global ORIGINAL
PASSWORD = password.get()
USERNAME = username.get()
IP = host.get()
ORIGINAL = folder.get()
if not os.path.exists(ORIGINAL):
pathlib.Path(ORIGINAL).mkdir(parents=True)
download_thread = Thread(target=download, args=[window])
download_thread.setDaemon(True)
download_thread.start()
upload_thread = Thread(target=upload, args=[window])
upload_thread.setDaemon(True)
upload_thread.start()
watch_dir_thread = Thread(target=watch_dir)
watch_dir_thread.setDaemon(True)
watch_dir_thread.start()
def main(args):
root = Tk()
root.geometry("250x400")
img_logo = Image.open("img/logo.png")
img_tk = ImageTk.PhotoImage(img_logo.resize((200, 150), Image.ANTIALIAS))
host = StringVar()
host.set("localhost")
host_field = Entry(root, textvariable=host)
passwd = StringVar()
passwd_field = Entry(root, textvariable=passwd, show="*")
folder = StringVar()
folder.set(os.path.join(os.getenv("HOME"), "Munyal"))
folder_field = Entry(root, textvariable=folder)
connect = Button(root,
text="Conectar",
command=lambda: run_client(root, passwd, host, folder))
Label(root, text="MUNYAL").pack()
Label(root, image=img_tk).pack()
Label(root, text="").pack()
Label(root, text="Ruta del servidor").pack()
host_field.pack()
Label(root, text="").pack()
Label(root, text="Contraseña").pack()
passwd_field.pack()
Label(root, text="").pack()
Label(root, text="Carpeta a sincronizar").pack()
folder_field.pack()
Label(root, text="").pack()
connect.pack()
root.mainloop()
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))