Commit f672f0df by Luciano Barletta

improved everything

1 parent 6bf00945
......@@ -129,6 +129,8 @@ Hay un archivo que hace los [tests](/test_flask.py) automáticamente si se corre
Para agregar más tests, por cada uno debe existir una función y agregar un thread en main.
Para testear se debe tener una carpeta llamada 'testfolder' donde poder tirar los archivos para testeo.
## Mejoras no implementadas
......
from enums import Table, States, Services, Datatypes
from services import serviceFactory
import sqlite3, ipdb, json
class DBconnection:
......@@ -7,12 +8,13 @@ class DBconnection:
# Estructura de las tablas
structure = {
Table.id : "integer PRIMARY KEY",
Table.path : "text",
Table.serv : "text",
Table.dest : "text",
Table.type : "text",
Table.state : "text"
Table.id : "INTEGER PRIMARY KEY",
Table.path : "TEXT",
Table.serv : "TEXT",
Table.dest : "TEXT",
Table.type : "TEXT",
Table.info : "TEXT",
Table.state : "TEXT"
}
def __init__(self,db):
......@@ -48,11 +50,12 @@ class DBconnection:
return "La tabla " + table + " no existe o no está contemplada"
query = "INSERT INTO " + table + "("
values = " VALUES("
checked = self.check(insertions)
if type(checked) == str: # error
return checked
for column in insertions:
if not self.check(column,insertions[column]):
return "El dato '" + insertions[column] + "' no es valido"
query += column + ","
values += "'" + insertions[column] + "',"
values += "'" + str(insertions[column]) + "',"
query = query.strip(",") + ")"
values = values.strip(",") + ")"
con = sqlite3.connect(self.db)
......@@ -70,11 +73,12 @@ class DBconnection:
if not Table.validate(comparator[0]):
return "El comparador no es una columna valida"
query = "UPDATE " + table + " SET "
where = " WHERE " + comparator[0] + "=" + comparator[1]
where = " WHERE " + comparator[0] + "=" + str(comparator[1])
checked = self.check(alterations)
if type(checked) == str: # error
return checked
for column in alterations:
if not self.check(column,alterations[column]):
return "El dato '" + alterations[column] + "' no es valido"
query += column + "='" + alterations[column] + "',"
query += column + "='" + str(alterations[column]) + "',"
query = query.strip(",")
con = sqlite3.connect(self.db)
cursor = con.cursor()
......@@ -82,22 +86,29 @@ class DBconnection:
con.commit()
con.close()
def check(self,column,data):
if column == Table.path:
return True
if column == Table.serv:
return Services.validate(data)
if column == Table.dest:
return True
if column == Table.type:
data = json.loads(data)
def check(self,information):
valid = True
for column in information:
# columna no existe en la tabla
if not Table.validate(column):
return "La columna '" + column + "' no existe"
# chequeos de validez de datos
if column == Table.serv:
valid = Services.validate(information[column])
elif column == Table.type:
data = json.loads(information[column])
for key in data:
valid = valid and Datatypes.validate(data[key])
return valid
if column == Table.state:
return States.validate(data)
return False
elif column == Table.info:
return serviceFactory(information[Table.serv]).validateinfo(information[column])
elif column == Table.state:
valid = States.validate(information[column])
if not valid:
return "El dato '" + str(information[column]) + "' no es valido"
return None
@staticmethod
def parseToTable(rows):
......
......@@ -10,6 +10,10 @@ app = Flask(__name__)
# carpeta de los mensajes
msgfolder = "msg/"
# id mínimo para una tabla
minid = 1
# id máximo para una tabla
maxid = 2**63
# tiempo para reintentar mensajhes
retry_timer = 10
# tiempo para limpieza
......@@ -43,25 +47,28 @@ def data():
key = request.files.get('key')
if key:
request.files[file].save(path + "rand.key.enc")
request.files['key'].save(path + "rand.key.enc")
# denecripto llave simétrica con mi llave privada y la guardo en la carpeta del emisor
os.system("openssl rsautl -decrypt -inkey rsa_key.pri -in " + path + "rand.key.enc -out " + path + "rand.key")
os.remove(path + "rand.key.enc")
for file in request.files:
# si hay llave y no es esta
filepath = path + file
if key and file != "key":
request.files[file].save(filepath + ".enc")
# desencripto archivo con la llave simétrica y lo guardo en la carpeta del emisor
os.system("openssl enc -d -aes-256-cbc -in " + filepath + ".enc -out " + filepath + " -pass file:" + path + "rand.key")
os.remove(filepath + ".enc")
else:
request.files[file].save(path + request.files[file].filename)
request.files[file].save(filepath)
if key:
os.remove(path + "rand.key")
return str(process.datastore(path))
return str(process.datastore(
random.randrange(minid,maxid),
path))
# Devuelve un string al azar entre [0-9,a-z,A-Z]
def newprefix():
......@@ -86,7 +93,8 @@ def msg():
Table.id : id,
Table.serv : request.values['serv'],
Table.dest : request.values['dest'],
Table.type : request.values['type']
Table.type : request.values['type'],
Table.info : request.values.get('info')
}
state = process.paramstore(query)
return state
......@@ -118,6 +126,7 @@ def clean():
mtime = os.path.getmtime(folder)
# Si la carpeta existe por más de X segundos, borrala
if int(now.strftime("%Y%m%d%H%M%S")) - int(time.strftime("%Y%m%d%H%M%S")) > operation_timer:
if os.path.exists(folder):
os.system("rm -r " + folder)
threading.Timer(clean_timer, clean).start()
......
......@@ -4,6 +4,7 @@ class Table:
serv = "serv"
dest = "dest"
type = "type"
info = "info"
state = "state"
@staticmethod
......@@ -14,6 +15,7 @@ class States:
queued = "queued"
delivered = "delivered"
preprocess = "preprocess"
partial = "partially delivered"
@staticmethod
def validate(state):
......
......@@ -9,38 +9,43 @@ class Process:
self.db = db
self.conn = DBconnection(db)
# stores the data
def datastore(self,path):
# Guarda la data
def datastore(self,id,path):
entities = {
Table.id : id,
Table.path : path,
Table.state : States.preprocess
}
id = self.conn.insert("msg",entities)
return id
return self.conn.insert("msg",entities)
# stores the parameters
# Guarda los parámetros
def paramstore(self,query):
# service is wrong
row = self.lookup(query[Table.id])
# id no existe
if type(row) == str:
return row
# el servicio no existe
if not Services.validate(query[Table.serv]):
return "No existe el servicio '" + query[Table.serv] + "'"
types = json.loads(query[Table.type])
filelist = os.listdir(
self.lookup(
query[Table.id]
)[Table.path]
)
filelist = os.listdir(row[Table.path])
for file in types:
# files don't exist
# el archivo no existe
if file not in filelist:
return "El archivo '" + file + "' no existe"
# message can't be sent by this service
elif not serviceFactory(query[Table.serv]).validate(types[file]):
serv = serviceFactory(query[Table.serv])
# el tipo del mensaje no se puede enviar por el servicio
if not serv.validatetype(types[file]):
return "El servicio '" + query[Table.serv] + "' no puede enviar el tipo '" + types[file] + "' destinado al archivo '" + file + "'"
# el parametro no es valido
if not serv.validateinfo(query[Table.info]):
return "El servicio '" + query[Table.serv] + "' no cuenta con algún parámetro, sus parámetros son " + str(serv.Parameters)
entities = {
Table.dest : query[Table.dest],
Table.serv : query[Table.serv],
Table.type : query[Table.type],
Table.info : query[Table.info],
Table.state : States.queued
}
error = self.conn.update("msg",(Table.id,query[Table.id]),entities)
......@@ -48,22 +53,21 @@ class Process:
return error
return States.queued
# tries to send all messages available
# manda todos los mensajes no enviados
def send(self):
rows = self.conn.query("SELECT * FROM msg WHERE state = ?",(States.queued,))
for query in DBconnection.parseToTable(rows):
# if folder doesn't exist, erase the message request
# si no existe la carpeta borrar el mensaje
if not os.path.exists(query[Table.path]):
self.conn.query("DELETE FROM msg WHERE id = ?",(query[Table.id],))
continue
serv = serviceFactory(query[Table.serv])
success = serv.send(query)
if success:
# save as delivered
# gurdar como enviado
self.conn.query("UPDATE msg SET state = ? WHERE id = ?",(States.delivered,query[Table.id]))
# returns the state of a message given its id
# stores the message to history if delivered
# devuelve el estado de un mensaje, lo archiva si está enviado
def lookup(self,id):
rows = self.conn.query("SELECT * FROM msg WHERE id = ?",(id,))
if rows == []:
......@@ -82,6 +86,7 @@ class Process:
self.conn.insert("history",entities)
return row
# devuelve todas las carpetas de mensajes
def paths(self):
rows = self.conn.query("SELECT path FROM msg")
paths = []
......
......@@ -9,16 +9,26 @@ import requests, json, os, smtplib
class ServiceBase(ABC):
# Método para enviar un mensaje
@abstractmethod
def send(self,data):
pass
# Método para validar un tipo de dato
@abstractmethod
def validate(self,serv):
def validatetype(self,type):
pass
# Método para validar los parámetros
@abstractmethod
def validateinfo(self,info):
pass
class Wpp1(ServiceBase):
Allowed = [Datatypes.text, Datatypes.image, Datatypes.document, Datatypes.link]
Parameters = ["origin"]
URL = "https://www.waboxapp.com/api/send/"
URLmode = {
Datatypes.text : 'chat',
......@@ -39,6 +49,8 @@ class Wpp1(ServiceBase):
f = open(filepath)
text = f.read()
f.close()
if type(text) == bytes:
text = text.decode("utf-8")
result = requests.get(url = Wpp1.URL + Wpp1.URLmode[types[file]],params = {'token':Wpp1.token,'uid':Wpp1.uid,'to':data[Table.dest],'text':text})
succ = succ and result.json()['success']
else:
......@@ -47,12 +59,23 @@ class Wpp1(ServiceBase):
succ = succ and result.json()['success']
return succ
def validate(self,datatype):
return datatype == Datatypes.text or datatype == Datatypes.image or datatype == Datatypes.document or datatype == Datatypes.link
def validatetype(self,type):
return type in Wpp1.Allowed
def validateinfo(self,info):
if info == None:
return True
i = json.loads(info)
for param in i:
if param not in Wpp1.Parameters:
return False
return True
class Mail(ServiceBase):
Allowed = [Datatypes.text, Datatypes.image, Datatypes.document, Datatypes.link]
Parameters = ["origin", "subject"]
def __init__(self):
self.__username = "prueba@anacsoft.com"
self.__password = "prueba2019"
......@@ -61,51 +84,60 @@ class Mail(ServiceBase):
self.s.login(self.__username,self.__password)
def send(self,data):
types = json.loads(data[Table.type])
info = json.loads(data[Table.info])
msg = MIMEMultipart()
msg['From'] = self._Mail__username
msg['To'] = data[Table.dest]
msg['Subject'] = "Test"
types = json.loads(data[Table.type])
msg['Subject'] = info['subject']
for file in types:
filepath = data[Table.path] + file
MIME = self.MIMEmode(filepath, types[file])
MIME = self.MIMEmode(file, filepath, types[file])
msg.attach(MIME)
self.s.send_message(msg)
return True
def MIMEmode(self,path,type):
def MIMEmode(self,name,path,datatype):
mode = None
if type == Datatypes.text:
with open(path) as data:
mode = MIMEText(data.read(),'plain')
if type == Datatypes.html:
with open(path) as data:
mode = MIMEText(data.read(),'html')
if type == Datatypes.image:
with open(path, "rb") as data:
mode = MIMEImage(data.read())
if type == Datatypes.audio:
with open(path, "rb") as data:
mode = MIMEAudio(data.read())
if type == Datatypes.document:
with open(path, "rb") as data:
mode = MIMEApplication(data.read())
return mode
def validate(self,datatype):
if datatype == Datatypes.text:
return True
with open(path) as f:
data = f.read()
if type(data) == bytes:
data = data.decode("utf-8")
mode = MIMEText(data,'plain')
if datatype == Datatypes.html:
with open(path) as f:
data = f.read()
if type(data) == bytes:
data = data.decode("utf-8")
mode = MIMEText(data,'html')
if datatype == Datatypes.image:
return True
with open(path, "rb") as f:
mode = MIMEImage(f.read())
mode['Content-Disposition'] = 'attachment; filename=' + name
if datatype == Datatypes.audio:
return True
with open(path, "rb") as f:
mode = MIMEAudio(f.read())
mode['Content-Disposition'] = 'attachment; filename=' + name
if datatype == Datatypes.document:
return True
if datatype == Datatypes.html:
return True
with open(path, "rb") as f:
mode = MIMEApplication(f.read())
mode['Content-Disposition'] = 'attachment; filename=' + name
return mode
def validatetype(self,type):
return type in Mail.Allowed
def validateinfo(self,info):
if info == None:
return False
i = json.loads(info)
for param in i:
if param not in Mail.Parameters:
return False
return True
def serviceFactory(serv):
if serv == Services.wpp1:
......
import os, time, requests, ipdb, json, threading
from database import DBconnection
URL = "http://192.168.15.75:5000/"
TESTPHONE = "5493415959169"
......@@ -19,7 +20,7 @@ def wpp1(phone):
'serv' : "wpp1",
'dest' : phone,
'type' : json.dumps({
'wptext' : 'text',
'wpmsg' : 'text',
'wpimage' : 'image',
'wpmedia' : 'document',
'wplink' : 'link'
......@@ -44,9 +45,12 @@ def mail(mail):
'serv' : "mail",
'dest' : mail,
'type' : json.dumps({
'mailtext' : 'text',
'mailmsg' : 'text',
'mailimage' : 'image',
'maildocument' : 'document'
}),
'info' : json.dumps({
'subject' : 'Some Subject'
})
})
assert state.text == "queued" , "'" + state.text + "' no es igual a 'queued'"
......@@ -67,8 +71,9 @@ def encryption(phone):
os.system("openssl rsautl -encrypt -inkey " + FOLDER + "server_rsa_key.pub -pubin -in " + FOLDER + "rand.key -out " + FOLDER + "rand.key.enc")
id = requests.post(url = URL + "data", files = {
'plaintext.enc' : open(FOLDER + "plaintext.enc","rb"),
'plainimage.enc' : open(FOLDER + "plainimage.enc","rb")
'plaintext' : open(FOLDER + "plaintext.enc","rb"),
'plainimage' : open(FOLDER + "plainimage.enc","rb"),
'key' : open(FOLDER + "rand.key.enc","rb")
})
assert int(id.text) > 0 , id.text + "no es mayor a 0"
state = requests.post(url = URL + "msg", params = {
......@@ -87,13 +92,46 @@ def encryption(phone):
})
assert (state.text == "delivered") , "'" + state.text + "' no es igual a 'delivered'"
def errors():
# service not valid
# service unable to send type
# trying to set type of unexistent file
# trying to user unexistent table
# ill-formed comparator
# data doesn't pass the checks
def errors(phone):
# id no existente
error = requests.post(url = URL + "msg", params = {
'id' : "-",
'serv' : "-",
'dest' : "-",
'type' : "-"
})
assert error.text == "El id - no existe"
# servicio no valido
id = requests.post(url = URL + "data", files = {
"errortext" : open(FOLDER + "errortext","rb")
})
error = requests.post(url = URL + "msg", params = {
'id' : id.text,
'serv' : "-",
'dest' : "-",
'type' : "-"
})
assert error.text == "No existe el servicio '-'"
# archivo inexistente
error = requests.post(url = URL + "msg", params = {
'id' : id.text,
'serv' : "wpp1",
'dest' : phone,
'type' : json.dumps({
"-" : "-"
})
})
assert error.text == "El archivo '-' no existe"
# tipo incorrecto
error = requests.post(url = URL + "msg", params = {
'id' : id.text,
'serv' : "wpp1",
'dest' : phone,
'type' : json.dumps({
"errortext" : "-"
})
})
assert error.text == "El servicio 'wpp1' no puede enviar el tipo '-' destinado al archivo 'errortext'"
def main():
if os.path.exists("messages.db"):
......@@ -107,6 +145,7 @@ def main():
threads.append(threading.Thread(target = wpp1, args = (TESTPHONE,)))
threads.append(threading.Thread(target = encryption, args = (TESTPHONE,)))
threads.append(threading.Thread(target = mail, args = (TESTMAIL,)))
threads.append(threading.Thread(target = errors, args = (TESTPHONE,)))
for thread in threads:
thread.start()
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!