2018-11-17 23:19:17 +03:00
|
|
|
import json
|
2017-11-12 22:23:12 +03:00
|
|
|
import os
|
|
|
|
|
import socket
|
|
|
|
|
import time
|
2018-11-17 23:19:17 +03:00
|
|
|
import urllib
|
2017-11-06 22:17:43 +03:00
|
|
|
from enum import Enum
|
2018-01-20 14:04:07 +03:00
|
|
|
from ftplib import FTP, error_perm
|
2017-11-12 22:23:12 +03:00
|
|
|
from telnetlib import Telnet
|
2018-11-08 13:07:24 +03:00
|
|
|
from urllib.error import HTTPError, URLError
|
|
|
|
|
from urllib.parse import urlencode
|
2018-11-11 18:35:45 +03:00
|
|
|
from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener
|
2017-11-06 22:17:43 +03:00
|
|
|
|
2018-10-14 20:27:06 +03:00
|
|
|
from app.commons import log
|
2018-01-07 16:33:18 +03:00
|
|
|
from app.properties import Profile
|
|
|
|
|
|
2018-11-11 18:35:45 +03:00
|
|
|
_BQ_FILES_LIST = ("tv", "radio", # enigma 2
|
|
|
|
|
"myservices.xml", "bouquets.xml", "ubouquets.xml") # neutrino
|
|
|
|
|
|
|
|
|
|
_DATA_FILES_LIST = ("lamedb", "lamedb5", "services.xml", "blacklist", "whitelist",)
|
2017-10-14 12:24:59 +03:00
|
|
|
|
2018-08-04 11:38:38 +03:00
|
|
|
_SAT_XML_FILE = "satellites.xml"
|
2018-02-12 13:34:00 +03:00
|
|
|
_WEBTV_XML_FILE = "webtv.xml"
|
|
|
|
|
|
2017-10-14 12:24:59 +03:00
|
|
|
|
2018-08-04 11:38:38 +03:00
|
|
|
class DownloadType(Enum):
|
2017-11-06 22:17:43 +03:00
|
|
|
ALL = 0
|
|
|
|
|
BOUQUETS = 1
|
|
|
|
|
SATELLITES = 2
|
2018-01-16 18:51:08 +03:00
|
|
|
PICONS = 3
|
2018-02-12 13:34:00 +03:00
|
|
|
WEBTV = 4
|
2017-11-06 22:17:43 +03:00
|
|
|
|
|
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
class HttpRequestType(Enum):
|
|
|
|
|
ZAP = "zap?sRef="
|
|
|
|
|
INFO = "about"
|
|
|
|
|
SIGNAL = "tunersignal"
|
|
|
|
|
|
|
|
|
|
|
2018-11-08 13:07:24 +03:00
|
|
|
class TestException(Exception):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2018-08-04 11:38:38 +03:00
|
|
|
def download_data(*, properties, download_type=DownloadType.ALL, callback=None):
|
2018-11-06 23:43:13 +03:00
|
|
|
with FTP(host=properties["host"], user=properties["user"], passwd=properties["password"]) as ftp:
|
2018-02-05 18:24:49 +03:00
|
|
|
ftp.encoding = "utf-8"
|
2018-11-06 23:43:13 +03:00
|
|
|
callback("FTP OK.\n")
|
2017-10-14 12:24:59 +03:00
|
|
|
save_path = properties["data_dir_path"]
|
2017-12-30 22:58:47 +03:00
|
|
|
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
2017-10-14 12:24:59 +03:00
|
|
|
files = []
|
2017-11-06 22:17:43 +03:00
|
|
|
# bouquets section
|
2018-08-04 11:38:38 +03:00
|
|
|
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
|
2017-11-06 22:17:43 +03:00
|
|
|
ftp.cwd(properties["services_path"])
|
|
|
|
|
ftp.dir(files.append)
|
2018-11-11 18:35:45 +03:00
|
|
|
file_list = _BQ_FILES_LIST + _DATA_FILES_LIST if download_type is DownloadType.ALL else _BQ_FILES_LIST
|
2017-11-06 22:17:43 +03:00
|
|
|
for file in files:
|
|
|
|
|
name = str(file).strip()
|
2018-11-11 18:35:45 +03:00
|
|
|
if name.endswith(file_list):
|
2017-11-06 22:17:43 +03:00
|
|
|
name = name.split()[-1]
|
2018-11-06 23:43:13 +03:00
|
|
|
download_file(ftp, name, save_path, callback)
|
2018-02-12 13:34:00 +03:00
|
|
|
# satellites.xml and webtv section
|
2018-11-08 13:07:24 +03:00
|
|
|
if download_type in (DownloadType.ALL, DownloadType.SATELLITES, DownloadType.WEBTV):
|
2017-11-06 22:17:43 +03:00
|
|
|
ftp.cwd(properties["satellites_xml_path"])
|
|
|
|
|
files.clear()
|
|
|
|
|
ftp.dir(files.append)
|
|
|
|
|
|
|
|
|
|
for file in files:
|
|
|
|
|
name = str(file).strip()
|
2018-08-04 11:38:38 +03:00
|
|
|
if download_type in (DownloadType.ALL, DownloadType.SATELLITES) and name.endswith(_SAT_XML_FILE):
|
2018-11-06 23:43:13 +03:00
|
|
|
download_file(ftp, _SAT_XML_FILE, save_path, callback)
|
2018-11-08 13:07:24 +03:00
|
|
|
if download_type in (DownloadType.ALL, DownloadType.WEBTV) and name.endswith(_WEBTV_XML_FILE):
|
2018-11-06 23:43:13 +03:00
|
|
|
download_file(ftp, _WEBTV_XML_FILE, save_path, callback)
|
2017-10-26 01:23:05 +03:00
|
|
|
|
2018-01-16 18:51:08 +03:00
|
|
|
if callback is not None:
|
2018-11-06 23:43:13 +03:00
|
|
|
callback("\nDone.\n")
|
2017-10-14 12:24:59 +03:00
|
|
|
|
2018-01-16 18:51:08 +03:00
|
|
|
|
2018-08-04 11:38:38 +03:00
|
|
|
def upload_data(*, properties, download_type=DownloadType.ALL, remove_unused=False, profile=Profile.ENIGMA_2,
|
2018-11-11 18:35:45 +03:00
|
|
|
callback=None, done_callback=None, use_http=False):
|
2017-11-10 13:38:03 +03:00
|
|
|
data_path = properties["data_dir_path"]
|
2017-11-12 22:23:12 +03:00
|
|
|
host = properties["host"]
|
2018-11-17 23:19:17 +03:00
|
|
|
base_url = "http://{}:{}/api/".format(host, properties.get("http_port", "80"))
|
2018-11-13 14:17:59 +03:00
|
|
|
tn, ht = None, None # telnet, http
|
2017-11-10 13:38:03 +03:00
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
try:
|
|
|
|
|
if profile is Profile.ENIGMA_2 and use_http:
|
|
|
|
|
ht = http(properties.get("http_user", ""), properties.get("http_password", ""), base_url, callback)
|
|
|
|
|
next(ht)
|
|
|
|
|
message = ""
|
|
|
|
|
if download_type is DownloadType.BOUQUETS:
|
|
|
|
|
message = "User bouquets will be updated!"
|
|
|
|
|
elif download_type is DownloadType.ALL:
|
|
|
|
|
message = "All user data will be reloaded!"
|
|
|
|
|
elif download_type is DownloadType.SATELLITES:
|
|
|
|
|
message = "Satellites.xml file will be updated!"
|
|
|
|
|
|
|
|
|
|
params = urlencode({"text": message, "type": 2, "timeout": 5})
|
|
|
|
|
url = base_url + "message?{}".format(params)
|
|
|
|
|
ht.send(url)
|
|
|
|
|
|
|
|
|
|
if download_type is DownloadType.ALL:
|
|
|
|
|
time.sleep(5)
|
|
|
|
|
ht.send(base_url + "/powerstate?newstate=0")
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
else:
|
|
|
|
|
# telnet
|
|
|
|
|
tn = telnet(host=host, user=properties.get("telnet_user", "root"),
|
|
|
|
|
password=properties.get("telnet_password", ""),
|
|
|
|
|
timeout=properties.get("telnet_timeout", 5))
|
|
|
|
|
next(tn)
|
|
|
|
|
# terminate enigma or neutrino
|
|
|
|
|
tn.send("init 4")
|
|
|
|
|
|
|
|
|
|
with FTP(host=host, user=properties["user"], passwd=properties["password"]) as ftp:
|
|
|
|
|
ftp.encoding = "utf-8"
|
|
|
|
|
callback("FTP OK.\n")
|
|
|
|
|
sat_xml_path = properties["satellites_xml_path"]
|
|
|
|
|
services_path = properties["services_path"]
|
2017-11-10 13:38:03 +03:00
|
|
|
|
2018-08-04 11:38:38 +03:00
|
|
|
if download_type is DownloadType.SATELLITES:
|
2018-11-13 14:17:59 +03:00
|
|
|
upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback)
|
|
|
|
|
|
|
|
|
|
if profile is Profile.NEUTRINO_MP and download_type is DownloadType.WEBTV:
|
|
|
|
|
upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback)
|
|
|
|
|
|
|
|
|
|
if download_type is DownloadType.BOUQUETS:
|
|
|
|
|
ftp.cwd(services_path)
|
|
|
|
|
upload_bouquets(ftp, data_path, remove_unused, callback)
|
|
|
|
|
|
|
|
|
|
if download_type is DownloadType.ALL:
|
|
|
|
|
upload_xml(ftp, data_path, sat_xml_path, _SAT_XML_FILE, callback)
|
|
|
|
|
if profile is Profile.NEUTRINO_MP:
|
|
|
|
|
upload_xml(ftp, data_path, sat_xml_path, _WEBTV_XML_FILE, callback)
|
|
|
|
|
|
|
|
|
|
ftp.cwd(services_path)
|
|
|
|
|
upload_bouquets(ftp, data_path, remove_unused, callback)
|
|
|
|
|
upload_files(ftp, data_path, _DATA_FILES_LIST, callback)
|
|
|
|
|
|
|
|
|
|
if download_type is DownloadType.PICONS:
|
|
|
|
|
upload_picons(ftp, properties.get("picons_dir_path"), properties.get("picons_path"))
|
|
|
|
|
|
|
|
|
|
if tn and not use_http:
|
|
|
|
|
# resume enigma or restart neutrino
|
2018-02-12 13:34:00 +03:00
|
|
|
tn.send("init 3" if profile is Profile.ENIGMA_2 else "init 6")
|
2018-11-13 14:17:59 +03:00
|
|
|
elif ht and use_http:
|
|
|
|
|
if download_type is DownloadType.BOUQUETS:
|
|
|
|
|
ht.send(base_url + "/servicelistreload?mode=2")
|
|
|
|
|
elif download_type is DownloadType.ALL:
|
|
|
|
|
ht.send(base_url + "/servicelistreload?mode=0")
|
|
|
|
|
ht.send(base_url + "/powerstate?newstate=4")
|
2018-02-12 13:34:00 +03:00
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
if done_callback is not None:
|
|
|
|
|
done_callback()
|
|
|
|
|
finally:
|
|
|
|
|
if tn:
|
|
|
|
|
tn.close()
|
|
|
|
|
if ht:
|
|
|
|
|
ht.close()
|
2017-11-10 13:38:03 +03:00
|
|
|
|
|
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
def upload_bouquets(ftp, data_path, remove_unused, callback):
|
|
|
|
|
if remove_unused:
|
|
|
|
|
remove_unused_bouquets(ftp, callback)
|
|
|
|
|
upload_files(ftp, data_path, _BQ_FILES_LIST, callback)
|
2018-01-16 18:51:08 +03:00
|
|
|
|
|
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
def upload_files(ftp, data_path, file_list, callback):
|
|
|
|
|
for file_name in os.listdir(data_path):
|
|
|
|
|
if file_name == _SAT_XML_FILE or file_name == _WEBTV_XML_FILE:
|
|
|
|
|
continue
|
|
|
|
|
if file_name.endswith(file_list):
|
|
|
|
|
send_file(file_name, data_path, ftp, callback)
|
2017-11-10 13:38:03 +03:00
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
|
|
|
|
|
def remove_unused_bouquets(ftp, callback):
|
|
|
|
|
files = []
|
|
|
|
|
ftp.dir(files.append)
|
|
|
|
|
for file in files:
|
|
|
|
|
name = str(file).strip()
|
|
|
|
|
if name.endswith(("tv", "radio", "bouquets.xml", "ubouquets.xml")):
|
|
|
|
|
name = name.split()[-1]
|
|
|
|
|
callback("Deleting file: {}. Status: {}\n".format(name, ftp.delete(name)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upload_xml(ftp, data_path, xml_path, xml_file, callback):
|
|
|
|
|
""" Used for transfer satellites.xml or webtv.xml files """
|
|
|
|
|
ftp.cwd(xml_path)
|
|
|
|
|
send_file(xml_file, data_path, ftp, callback)
|
2018-01-16 18:51:08 +03:00
|
|
|
|
2017-11-10 13:38:03 +03:00
|
|
|
|
2018-11-11 18:35:45 +03:00
|
|
|
def upload_picons(ftp, src, dest):
|
|
|
|
|
try:
|
|
|
|
|
ftp.cwd(dest)
|
|
|
|
|
except error_perm as e:
|
|
|
|
|
if str(e).startswith("550"):
|
|
|
|
|
ftp.mkd(dest) # if not exist
|
|
|
|
|
ftp.cwd(dest)
|
|
|
|
|
files = []
|
|
|
|
|
ftp.dir(files.append)
|
|
|
|
|
picons_suf = (".jpg", ".png")
|
|
|
|
|
for file in files:
|
|
|
|
|
name = str(file).strip()
|
|
|
|
|
if name.endswith(picons_suf):
|
|
|
|
|
name = name.split()[-1]
|
|
|
|
|
ftp.delete(name)
|
|
|
|
|
for file_name in os.listdir(src):
|
|
|
|
|
if file_name.endswith(picons_suf):
|
|
|
|
|
send_file(file_name, src, ftp)
|
|
|
|
|
|
|
|
|
|
|
2018-11-06 23:43:13 +03:00
|
|
|
def download_file(ftp, name, save_path, callback):
|
2018-02-12 13:34:00 +03:00
|
|
|
with open(save_path + name, "wb") as f:
|
2018-11-13 14:17:59 +03:00
|
|
|
callback("Downloading file: {}. Status: {}\n".format(name, str(ftp.retrbinary("RETR " + name, f.write))))
|
2018-10-14 12:45:12 +03:00
|
|
|
|
2018-02-12 13:34:00 +03:00
|
|
|
|
2018-11-11 18:35:45 +03:00
|
|
|
def send_file(file_name, path, ftp, callback):
|
2017-11-10 13:38:03 +03:00
|
|
|
""" Opens the file in binary mode and transfers into receiver """
|
|
|
|
|
with open(path + file_name, "rb") as f:
|
2018-11-13 14:17:59 +03:00
|
|
|
callback("Uploading file: {}. Status: {}\n".format(file_name, str(ftp.storbinary("STOR " + file_name, f))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def http(user, password, url, callback):
|
|
|
|
|
init_auth(user, password, url)
|
|
|
|
|
while True:
|
|
|
|
|
url = yield
|
|
|
|
|
with urlopen(url, timeout=5) as f:
|
2018-11-17 23:19:17 +03:00
|
|
|
msg = json.load(f).get("message", None)
|
2018-11-13 14:17:59 +03:00
|
|
|
if msg:
|
|
|
|
|
callback("HTTP: {}\n".format(msg))
|
2017-11-10 13:38:03 +03:00
|
|
|
|
|
|
|
|
|
2018-01-18 00:57:58 +03:00
|
|
|
def telnet(host, port=23, user="", password="", timeout=5):
|
2017-11-12 22:23:12 +03:00
|
|
|
try:
|
|
|
|
|
tn = Telnet(host=host, port=port, timeout=timeout)
|
|
|
|
|
except socket.timeout:
|
2018-01-07 16:33:18 +03:00
|
|
|
log("telnet error: socket timeout")
|
2017-11-12 22:23:12 +03:00
|
|
|
else:
|
2017-11-14 19:20:16 +03:00
|
|
|
time.sleep(1)
|
2017-11-12 22:23:12 +03:00
|
|
|
command = yield
|
2018-01-18 00:57:58 +03:00
|
|
|
if user != "":
|
2018-01-07 16:33:18 +03:00
|
|
|
tn.read_until(b"login: ")
|
|
|
|
|
tn.write(user.encode("utf-8") + b"\n")
|
|
|
|
|
time.sleep(timeout)
|
2018-01-18 00:57:58 +03:00
|
|
|
if password != "":
|
2018-01-07 16:33:18 +03:00
|
|
|
tn.read_until(b"Password: ")
|
|
|
|
|
tn.write(password.encode("utf-8") + b"\n")
|
|
|
|
|
time.sleep(timeout)
|
2017-11-12 22:23:12 +03:00
|
|
|
tn.write("{}\r\n".format(command).encode("utf-8"))
|
2017-11-14 19:20:16 +03:00
|
|
|
time.sleep(timeout)
|
2017-11-12 22:23:12 +03:00
|
|
|
command = yield
|
|
|
|
|
time.sleep(timeout)
|
|
|
|
|
tn.write("{}\r\n".format(command).encode("utf-8"))
|
|
|
|
|
time.sleep(timeout)
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
|
2018-11-17 23:19:17 +03:00
|
|
|
# ***************** http api *******************#
|
|
|
|
|
|
|
|
|
|
def http_request(host, port, user, password):
|
|
|
|
|
base_url = "http://{}:{}/api/".format(host, port)
|
|
|
|
|
init_auth(user, password, base_url)
|
|
|
|
|
while True:
|
|
|
|
|
req_type, ref = yield
|
|
|
|
|
url = base_url
|
|
|
|
|
if req_type is HttpRequestType.ZAP:
|
|
|
|
|
url = base_url + "zap?sRef={}".format(urllib.parse.quote(ref))
|
|
|
|
|
elif req_type is HttpRequestType.INFO:
|
|
|
|
|
url = base_url + HttpRequestType.INFO.value
|
|
|
|
|
elif req_type is HttpRequestType.SIGNAL:
|
|
|
|
|
url = base_url + HttpRequestType.SIGNAL.value
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with urlopen(url, timeout=5) as f:
|
|
|
|
|
yield json.load(f)
|
|
|
|
|
except (URLError, HTTPError):
|
|
|
|
|
yield None
|
|
|
|
|
|
2018-11-08 13:07:24 +03:00
|
|
|
# ***************** Connections testing *******************#
|
|
|
|
|
|
|
|
|
|
|
2018-11-09 12:41:36 +03:00
|
|
|
def test_ftp(host, port, user, password, timeout=5):
|
2018-11-08 13:07:24 +03:00
|
|
|
try:
|
|
|
|
|
with FTP(host=host, user=user, passwd=password, timeout=timeout) as ftp:
|
|
|
|
|
return ftp.getwelcome()
|
|
|
|
|
except (error_perm, ConnectionRefusedError, OSError) as e:
|
|
|
|
|
raise TestException(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_http(host, port, user, password, timeout=5):
|
|
|
|
|
try:
|
|
|
|
|
params = urlencode({"text": "Connection test", "type": 2, "timeout": timeout})
|
2018-11-17 23:19:17 +03:00
|
|
|
url = "http://{}:{}/api/message?{}".format(host, port, params)
|
2018-11-09 12:41:36 +03:00
|
|
|
# authentication
|
2018-11-13 14:17:59 +03:00
|
|
|
init_auth(user, password, url)
|
2018-11-09 12:41:36 +03:00
|
|
|
|
|
|
|
|
with urlopen(url, timeout=5) as f:
|
2018-11-17 23:19:17 +03:00
|
|
|
return json.load(f).get("message", "")
|
2018-11-08 13:07:24 +03:00
|
|
|
except (URLError, HTTPError) as e:
|
|
|
|
|
raise TestException(e)
|
|
|
|
|
|
|
|
|
|
|
2018-11-13 14:17:59 +03:00
|
|
|
def init_auth(user, password, url):
|
2018-11-11 18:35:45 +03:00
|
|
|
""" Init authentication """
|
|
|
|
|
pass_mgr = HTTPPasswordMgrWithDefaultRealm()
|
|
|
|
|
pass_mgr.add_password(None, url, user, password)
|
|
|
|
|
auth_handler = HTTPBasicAuthHandler(pass_mgr)
|
|
|
|
|
opener = build_opener(auth_handler)
|
|
|
|
|
install_opener(opener)
|
|
|
|
|
|
|
|
|
|
|
2018-11-09 12:41:36 +03:00
|
|
|
def test_telnet(host, port, user, password, timeout=5):
|
2018-11-08 13:07:24 +03:00
|
|
|
try:
|
|
|
|
|
gen = telnet_test(host, port, user, password, timeout)
|
|
|
|
|
res = next(gen)
|
|
|
|
|
print(res)
|
|
|
|
|
res = next(gen)
|
|
|
|
|
return res
|
|
|
|
|
except (socket.timeout, OSError) as e:
|
|
|
|
|
raise TestException(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def telnet_test(host, port, user, password, timeout):
|
2018-11-04 00:33:50 +03:00
|
|
|
tn = Telnet(host=host, port=port, timeout=timeout)
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
tn.read_until(b"login: ", timeout=2)
|
|
|
|
|
tn.write(user.encode("utf-8") + b"\n")
|
|
|
|
|
time.sleep(timeout)
|
|
|
|
|
tn.read_until(b"Password: ", timeout=2)
|
|
|
|
|
tn.write(password.encode("utf-8") + b"\n")
|
|
|
|
|
time.sleep(timeout)
|
|
|
|
|
yield tn.read_very_eager()
|
|
|
|
|
tn.close()
|
|
|
|
|
yield "Done"
|
|
|
|
|
|
|
|
|
|
|
2017-11-12 22:23:12 +03:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
pass
|