mirror of
https://gitlab.os-k.eu/neox/CNIRevelator.git
synced 2023-08-25 14:03:10 +02:00
Big reorganization of the sources
This commit is contained in:
parent
a0692aa5ba
commit
2e93361728
110
CNIRevelator.py
Normal file
110
CNIRevelator.py
Normal file
@ -0,0 +1,110 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from CNI_GLOBALVAR import *
|
||||
try:
|
||||
os.remove('error.log')
|
||||
os.remove('conf.ig')
|
||||
except:
|
||||
print("pass log deletion")
|
||||
pass
|
||||
|
||||
if not os.path.exists(CST_FOLDER):
|
||||
try:
|
||||
os.makedirs(CST_FOLDER)
|
||||
except IOError:
|
||||
print("pass IO ERROR")
|
||||
pass
|
||||
|
||||
print("debug")
|
||||
import logging
|
||||
from logging import FileHandler
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
|
||||
error_handler = FileHandler((CST_FOLDER + '\\error.log'), mode='w', encoding='utf-8', delay=True)
|
||||
info_handler = FileHandler((CST_FOLDER + '\\cnirevelator.log'), mode='w', encoding='utf-8')
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
logger.addHandler(error_handler)
|
||||
info_handler.setLevel(logging.DEBUG)
|
||||
info_handler.setFormatter(formatter)
|
||||
logger.addHandler(info_handler)
|
||||
from CNI_classes import *
|
||||
from CNI_Update import *
|
||||
|
||||
def main(logger):
|
||||
logger.error('')
|
||||
logger.info('main() : **** Creating App_main() ****')
|
||||
main_w = App_main(logger)
|
||||
main_w.montext('* ' + CST_NAME + ' ' + CST_VER + ' ' + CST_TYPE + ' Revision ' + CST_REV + ' *\n')
|
||||
import CNI_pytesseract as pytesseract
|
||||
try:
|
||||
os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\'
|
||||
os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata'
|
||||
tesser_version = pytesseract.get_tesseract_version()
|
||||
except Exception as e:
|
||||
logger.error('main() : **** ERROR WITH TESSERACT MODULE ' + str(e) + ' ****')
|
||||
else:
|
||||
text = 'Tesseract version ' + str(tesser_version) + ' Licensed Apache 2004 successfully initiated\n'
|
||||
main_w.montext(text)
|
||||
main_w.montext('\n\nEntrez la première ligne de MRZ svp \n')
|
||||
if CST_CHANGELOG.isOn:
|
||||
showinfo('Changelog : résumé de mise à jour', ('Version du logiciel : ' + CST_VER + ' ' + CST_TYPE + ' Revision ' + CST_REV + '\n\n' + CST_CHANGELOG.text), parent=main_w)
|
||||
logger.info('main() : **** Launching App_main() ****')
|
||||
main_w.mainloop()
|
||||
logger.info('main() : **** Ending App_main() ****')
|
||||
|
||||
|
||||
logger.info('launcher : ' + CST_NAME + ' ' + CST_VER)
|
||||
logger.info('launcher : *****Hello World*****')
|
||||
logger.info('launcher : *****Launching SoftUpdate()*****')
|
||||
|
||||
logger.info('launcher : *****Launching main()*****')
|
||||
State = main(logger)
|
||||
|
||||
|
||||
logger.info('launcher : *****Ending main()*****')
|
||||
logger.info('launcher : *****Goodbye!*****')
|
||||
handlers = logger.handlers[:]
|
||||
for handler in handlers:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
|
||||
try:
|
||||
with open(CST_FOLDER + '\\error.log') as (echo):
|
||||
try:
|
||||
os.remove('error.log')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
from shutil import copyfile
|
||||
temptwo = str(echo.read())
|
||||
if len(temptwo) != 1:
|
||||
copyfile(CST_FOLDER + '\\cnirevelator.log', 'error.log')
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
print("exit")
|
||||
sys.exit(0)
|
1332
CNI_classes.py
Normal file
1332
CNI_classes.py
Normal file
File diff suppressed because it is too large
Load Diff
207
downloader.py
Normal file
207
downloader.py
Normal file
@ -0,0 +1,207 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher download stuff *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import base64, hashlib
|
||||
import os
|
||||
from pypac import PACSession
|
||||
from requests.auth import HTTPProxyAuth
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
from requests import Session
|
||||
from time import time
|
||||
|
||||
import logger # logger.py
|
||||
import globs # globs.py
|
||||
import ihm # ihm.py
|
||||
|
||||
class AESCipher(object):
|
||||
|
||||
def __init__(self, key):
|
||||
self.bs = 32
|
||||
self.key = hashlib.sha256(key.encode()).digest()
|
||||
|
||||
def encrypt(self, raw):
|
||||
raw = self._pad(raw)
|
||||
iv = Random.new().read(AES.block_size)
|
||||
cipher = AES.new(self.key, AES.MODE_CBC, iv)
|
||||
return base64.b64encode(iv + cipher.encrypt(raw))
|
||||
|
||||
def decrypt(self, enc):
|
||||
enc = base64.b64decode(enc)
|
||||
iv = enc[:AES.block_size]
|
||||
cipher = AES.new(self.key, AES.MODE_CBC, iv)
|
||||
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
|
||||
|
||||
def _pad(self, s):
|
||||
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
|
||||
|
||||
@staticmethod
|
||||
def _unpad(s):
|
||||
return s[:-ord(s[len(s) - 1:])]
|
||||
|
||||
|
||||
class newcredentials:
|
||||
def __init__(self):
|
||||
|
||||
logfile = logger.logCur
|
||||
|
||||
self.login = ''
|
||||
self.password = ''
|
||||
self.valid = False
|
||||
self.readInTheBooks = False
|
||||
self.trying = 0
|
||||
|
||||
while True:
|
||||
session = PACSession(proxy_auth=(HTTPProxyAuth(self.login, self.password)))
|
||||
self.trying += 1
|
||||
|
||||
try:
|
||||
sessionAnswer = session.get('https://www.google.com')
|
||||
except Exception as e:
|
||||
logfile.printerr('Network Error : ' + str(e))
|
||||
sessionAnswer = ''
|
||||
|
||||
logfile.printdbg("Session Answer : " + str(sessionAnswer))
|
||||
|
||||
if str(sessionAnswer) == '<Response [200]>':
|
||||
logfile.printdbg('Successfully connected to the Internet !')
|
||||
self.sessionHandler = session
|
||||
self.valid = True
|
||||
return
|
||||
|
||||
if str(sessionAnswer) != '<Response [407]>' and self.trying > 2:
|
||||
# because sometimes the proxy does not return an error (especially if we do not provide either credentials)
|
||||
logfile.printerr('Network Error, or need a proxy !')
|
||||
return
|
||||
|
||||
if self.trying > 4:
|
||||
logfile.printerr('Invalid credentials : access denied, a maximum of 3 trials have been raised !')
|
||||
return
|
||||
|
||||
logfile.printdbg('Invalid credentials : access denied')
|
||||
|
||||
# Deleting the root of Evil if needed
|
||||
if self.readInTheBooks:
|
||||
os.remove(globs.CNIRConfig)
|
||||
logfile.printdbg("Deleting the root of Evil")
|
||||
|
||||
try:
|
||||
with open(globs.CNIRConfig, 'rb') as (configFile):
|
||||
self.readInTheBooks = True
|
||||
# Decrypt the config file
|
||||
AESObj = AESCipher(globs.CNIRCryptoKey)
|
||||
try:
|
||||
# Reading it
|
||||
reading = AESObj.decrypt(configFile.read())
|
||||
# Parsing it
|
||||
if reading != '||':
|
||||
if reading.find('||') != -1:
|
||||
# TADAAA
|
||||
self.login, self.password = reading.split('||')[0:2]
|
||||
else:
|
||||
# UPS
|
||||
logfile.printerr('Cryptokey is bad !')
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
raise IOError(str(e))
|
||||
|
||||
except FileNotFoundError:
|
||||
logfile.printdbg('We will ask for credentials then')
|
||||
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
# Parameters for the password invite
|
||||
invite = ihm.LoginDialog(launcherWindow)
|
||||
invite.transient(launcherWindow)
|
||||
invite.grab_set()
|
||||
|
||||
launcherWindow.wait_window(invite)
|
||||
|
||||
# Getting the credentials
|
||||
self.login = invite.login
|
||||
self.password = invite.key
|
||||
|
||||
AESObj = AESCipher(globs.CNIRCryptoKey)
|
||||
with open(globs.CNIRConfig, 'wb+') as (configFile):
|
||||
logfile.printdbg('Saving credentials in encrypted config file')
|
||||
configFile.write(AESObj.encrypt(self.login + '||' + self.password))
|
||||
|
||||
|
||||
return
|
||||
|
||||
class newdownload:
|
||||
def __init__(self, credentials, urlFile, destinationFile):
|
||||
self.urlFile = urlFile
|
||||
self.destinationFile = destinationFile
|
||||
self.session = credentials.sessionHandler
|
||||
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
logfile.printdbg('Requesting download of {}'.format(self.urlFile))
|
||||
|
||||
self.handler = self.session.get(self.urlFile, stream=True, headers={'Connection' : 'close', "Cache-Control": "no-cache", "Pragma": "no-cache"})
|
||||
self.handler.raise_for_status()
|
||||
|
||||
self.filesize = int(self.handler.headers['Content-length'])
|
||||
self.chunksize = int(self.filesize / 7)
|
||||
self.count = 0
|
||||
|
||||
def download(self):
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
url = self.urlFile
|
||||
filename = self.destinationFile
|
||||
|
||||
reducedFilename = filename.split("\\")[-1]
|
||||
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text=('Downloading {}'.format(reducedFilename)))
|
||||
launcherWindow.progressBar.stop()
|
||||
launcherWindow.progressBar.configure(mode='determinate', value=0, maximum=100)
|
||||
|
||||
try:
|
||||
os.remove(filename)
|
||||
except:
|
||||
pass
|
||||
|
||||
with open(filename, 'wb') as fh:
|
||||
for chunk in self.handler.iter_content(chunk_size=self.chunksize):
|
||||
fh.write(chunk)
|
||||
|
||||
self.count = os.path.getsize(self.destinationFile)
|
||||
Percent = int(self.count / self.filesize * 100)
|
||||
|
||||
launcherWindow.progressBar.configure(mode='determinate', value=(int(Percent)))
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text=('Downloading {}'.format(reducedFilename) + ' : ' + str((Percent)) + ' %'))
|
||||
|
||||
launcherWindow.progressBar.configure(mode='indeterminate', value=0, maximum=20)
|
||||
launcherWindow.progressBar.start()
|
||||
|
||||
logfile.printdbg('Successful retrieved {}'.format(filename))
|
||||
|
||||
return filename
|
||||
|
44
globs.py
Normal file
44
globs.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher global variables *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
import os
|
||||
|
||||
# CNIRevelator version
|
||||
version = [3, 0, 0]
|
||||
verstring_full = "{}.{}.{}".format(version[0], version[1], version[2])
|
||||
verstring = "{}.{}".format(version[0], version[1])
|
||||
|
||||
changelog = "Mise-à-jour majeure avec corrections suivantes :\n- Renouvellement de la signature numérique de l'exécutable\n- Amélioration de présentation du log en cas d'erreur\n- Refonte totale du code source et désobfuscation\n- Téléchargements en HTTPS fiables avec somme de contrôle"
|
||||
|
||||
CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8'
|
||||
CNIRFolder = os.getcwd()
|
||||
CNIRLColor = "#006699"
|
||||
CNIRName = "CNIRevelator {}".format(verstring)
|
||||
CNIRCryptoKey = '82Xh!efX3#@P~2eG'
|
||||
|
||||
CNIRConfig = CNIRFolder + '\\conf.ig'
|
||||
CNIRErrLog = CNIRFolder + '\\error.log'
|
||||
CNIRLauncherLog = CNIRFolder + '\\launcher.log'
|
||||
CNIRUrlConfig = CNIRFolder + '\\urlconf.ig'
|
||||
CNIREnv = os.getenv('APPDATA') + '/CNIRevelator/'
|
116
ihm.py
Normal file
116
ihm.py
Normal file
@ -0,0 +1,116 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher graphical interface *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from tkinter import *
|
||||
from tkinter.messagebox import *
|
||||
from tkinter import filedialog
|
||||
from tkinter import ttk
|
||||
|
||||
import logger # logger.py
|
||||
import globs # globs.py
|
||||
|
||||
class LoginDialog(Toplevel):
|
||||
|
||||
def __init__(self, parent):
|
||||
self.key = ''
|
||||
self.login = ''
|
||||
super().__init__(parent)
|
||||
self.title('Connexion')
|
||||
Label(self, text='IPN : ').pack()
|
||||
self.entry_login = Entry(self)
|
||||
self.entry_login.insert(0, '')
|
||||
self.entry_login.pack()
|
||||
Label(self, text='Mot de passe : ').pack()
|
||||
self.entry_pass = Entry(self, show='*')
|
||||
self.entry_pass.insert(0, '')
|
||||
self.entry_pass.pack()
|
||||
Button(self, text='Connexion', command=(self.connecti)).pack()
|
||||
self.resizable(width=False, height=False)
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
w = hs / 10
|
||||
h = ws / 18
|
||||
self.update()
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
self.iconbitmap('id-card.ico')
|
||||
x = ws / 2 - w / 2
|
||||
y = hs / 2 - h / 2
|
||||
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||
|
||||
def connecti(self):
|
||||
self.login = self.entry_login.get().strip()
|
||||
self.key = self.entry_pass.get().strip()
|
||||
self.destroy()
|
||||
|
||||
class LauncherWindow(Tk):
|
||||
|
||||
def __init__(self):
|
||||
# Initialize the tkinter main class
|
||||
Tk.__init__(self)
|
||||
self.configure(bg=globs.CNIRLColor)
|
||||
self.resizable(width=False, height=False)
|
||||
self.queue = []
|
||||
|
||||
# Setting up the geometry
|
||||
ws = self.winfo_screenwidth()
|
||||
hs = self.winfo_screenheight()
|
||||
wheight = hs /4
|
||||
wwidth = ws /4
|
||||
|
||||
# Creating objects
|
||||
self.mainCanvas = Canvas(self, width=wwidth, height=wheight*9/10, bg=globs.CNIRLColor, highlightthickness=0)
|
||||
self.pBarZone = Canvas(self, width=wwidth, height=wheight/10, bg=globs.CNIRLColor)
|
||||
|
||||
self.progressBar = ttk.Progressbar(self.pBarZone, orient=HORIZONTAL, length=wwidth-10, mode='determinate')
|
||||
|
||||
self.mainCanvas.create_text((wwidth / 2), (wheight / 3), text=(globs.CNIRName), font='Helvetica 30', fill='white')
|
||||
self.msg = self.mainCanvas.create_text((wwidth / 2.05), (wheight / 1.20), text='Booting up...', font='Helvetica 9', fill='white')
|
||||
|
||||
self.wm_title(globs.CNIRName)
|
||||
|
||||
# Centering
|
||||
x = ws / 2 - wwidth / 2
|
||||
y = hs / 2 - wheight / 2
|
||||
self.geometry('%dx%d+%d+%d' % (wwidth, wheight, x, y))
|
||||
self.mainCanvas.grid()
|
||||
self.pBarZone.grid()
|
||||
self.progressBar.grid()
|
||||
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico')
|
||||
else:
|
||||
self.iconbitmap('id-card.ico')
|
||||
logfile = logger.logCur
|
||||
logfile.printdbg('Launcher IHM successful')
|
||||
self.protocol('WM_DELETE_WINDOW', lambda : self.destroy())
|
||||
|
||||
self.update()
|
||||
|
||||
## Global Handler
|
||||
launcherWindowCur = LauncherWindow()
|
||||
|
69
launcher.py
Normal file
69
launcher.py
Normal file
@ -0,0 +1,69 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher & updater main file *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import updater # updater.py
|
||||
import ihm # ihm.py
|
||||
import globs # globs.py
|
||||
import logger # logger.py
|
||||
|
||||
## Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
## Main function
|
||||
def main():
|
||||
|
||||
# Hello world
|
||||
logfile.printdbg('*** CNIRLauncher LOGFILE. Hello World ! ***')
|
||||
#logfile.printdbg('Files in directory : ' + str(os.listdir(globs.CNIRFolder)))
|
||||
|
||||
# Hello user
|
||||
launcherWindow.progressBar.configure(mode='indeterminate', value=0, maximum=20)
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text='Starting...')
|
||||
launcherWindow.progressBar.start()
|
||||
|
||||
# Starting the main update thread
|
||||
mainThread.start()
|
||||
|
||||
launcherWindow.mainloop()
|
||||
|
||||
logfile.printdbg('*** CNIRLauncher LOGFILE. Goodbye World ! ***')
|
||||
return
|
||||
|
||||
## Bootstrap
|
||||
try:
|
||||
mainThread = threading.Thread(target=updater.umain, daemon=False)
|
||||
main()
|
||||
except Exception:
|
||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
64
logger.py
Normal file
64
logger.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher logging stuff *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import globs # globs.py
|
||||
|
||||
## The logging class
|
||||
class NewLoggingSystem:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Deleting the error log
|
||||
try:
|
||||
os.remove(globs.CNIRErrLog)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# Create new logging handle
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO) # To make sure we can have a debug channel
|
||||
|
||||
# Create channels
|
||||
formatter = logging.Formatter("\n[ %(module)s/%(funcName)s ] %(asctime)s :: %(levelname)s :: %(message)s")
|
||||
error_handler = logging.FileHandler((globs.CNIRErrLog), mode='w', encoding='utf-8', delay=True)
|
||||
info_handler = logging.FileHandler((globs.CNIRLauncherLog), mode='w', encoding='utf-8')
|
||||
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
error_handler.setFormatter(formatter)
|
||||
logger.addHandler(error_handler)
|
||||
|
||||
info_handler.setLevel(logging.DEBUG)
|
||||
info_handler.setFormatter(formatter)
|
||||
logger.addHandler(info_handler)
|
||||
|
||||
self.logger = logger
|
||||
self.printerr = logger.error
|
||||
self.printdbg = logger.info
|
||||
|
||||
## Global Handler
|
||||
logCur = NewLoggingSystem()
|
326
pytesseract.py
Normal file
326
pytesseract.py
Normal file
@ -0,0 +1,326 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Pytesseract modification to comply with Pyinstaller *
|
||||
* *
|
||||
* Copyright © 2017-2018 Matthias A. Lee (madmaze) *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
from PIL import Image
|
||||
|
||||
import os, sys, subprocess, tempfile, shlex, string
|
||||
from glob import iglob
|
||||
from pkgutil import find_loader
|
||||
from distutils.version import LooseVersion
|
||||
from os.path import realpath, normpath, normcase
|
||||
numpy_installed = find_loader('numpy') is not None
|
||||
if numpy_installed:
|
||||
from numpy import ndarray
|
||||
tesseract_cmd = 'tesseract'
|
||||
RGB_MODE = 'RGB'
|
||||
OSD_KEYS = {'Page number':(
|
||||
'page_num', int),
|
||||
'Orientation in degrees':(
|
||||
'orientation', int),
|
||||
'Rotate':(
|
||||
'rotate', int),
|
||||
'Orientation confidence':(
|
||||
'orientation_conf', float),
|
||||
'Script':(
|
||||
'script', str),
|
||||
'Script confidence':(
|
||||
'script_conf', float)}
|
||||
|
||||
class Output:
|
||||
STRING = 'string'
|
||||
BYTES = 'bytes'
|
||||
DICT = 'dict'
|
||||
|
||||
|
||||
class TesseractError(RuntimeError):
|
||||
|
||||
def __init__(self, status, message):
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.args = (status, message)
|
||||
|
||||
|
||||
class TesseractNotFoundError(EnvironmentError):
|
||||
|
||||
def __init__(self):
|
||||
super(TesseractNotFoundError, self).__init__(tesseract_cmd + " is not installed or it's not in your path")
|
||||
|
||||
|
||||
class TSVNotSupported(EnvironmentError):
|
||||
|
||||
def __init__(self):
|
||||
super(TSVNotSupported, self).__init__('TSV output not supported. Tesseract >= 3.05 required')
|
||||
|
||||
|
||||
def run_once(func):
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
if wrapper._result is wrapper:
|
||||
wrapper._result = func(*args, **kwargs)
|
||||
return wrapper._result
|
||||
|
||||
wrapper._result = wrapper
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_errors(error_string):
|
||||
return ' '.join(line for line in error_string.decode('utf-8').splitlines()).strip()
|
||||
|
||||
|
||||
def cleanup(temp_name):
|
||||
""" Tries to remove files by filename wildcard path. """
|
||||
for filename in iglob(temp_name + '*' if temp_name else temp_name):
|
||||
try:
|
||||
os.remove(filename)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def prepare(image):
|
||||
if isinstance(image, Image.Image):
|
||||
return image
|
||||
if numpy_installed:
|
||||
if isinstance(image, ndarray):
|
||||
pass
|
||||
return Image.fromarray(image)
|
||||
raise TypeError('Unsupported image object')
|
||||
|
||||
|
||||
def save_image(image):
|
||||
temp_name = tempfile.mktemp(prefix='tess_')
|
||||
if isinstance(image, str):
|
||||
return (temp_name, realpath(normpath(normcase(image))))
|
||||
else:
|
||||
image = prepare(image)
|
||||
img_extension = image.format
|
||||
if image.format not in frozenset({'BMP', 'JPEG', 'GIF', 'TIFF', 'PNG'}):
|
||||
img_extension = 'PNG'
|
||||
if not image.mode.startswith(RGB_MODE):
|
||||
image = image.convert(RGB_MODE)
|
||||
if 'A' in image.getbands():
|
||||
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
|
||||
background.paste(image, (0, 0), image)
|
||||
image = background
|
||||
input_file_name = temp_name + os.extsep + img_extension
|
||||
(image.save)(input_file_name, format=img_extension, **image.info)
|
||||
return (
|
||||
temp_name, input_file_name)
|
||||
|
||||
|
||||
def subprocess_args(include_stdout=True):
|
||||
if hasattr(subprocess, 'STARTUPINFO'):
|
||||
si = subprocess.STARTUPINFO()
|
||||
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
env = os.environ
|
||||
else:
|
||||
si = None
|
||||
env = None
|
||||
if include_stdout:
|
||||
ret = {'stdout': subprocess.PIPE}
|
||||
else:
|
||||
ret = {}
|
||||
ret.update({'stdin':subprocess.PIPE, 'stderr':subprocess.PIPE,
|
||||
'startupinfo':si,
|
||||
'env':env})
|
||||
return ret
|
||||
|
||||
|
||||
def run_tesseract(input_filename, output_filename_base, extension, lang, config='', nice=0):
|
||||
cmd_args = []
|
||||
if not sys.platform.startswith('win32'):
|
||||
if nice != 0:
|
||||
cmd_args += ('nice', '-n', str(nice))
|
||||
cmd_args += (tesseract_cmd, input_filename, output_filename_base)
|
||||
if lang is not None:
|
||||
cmd_args += ('-l', lang)
|
||||
cmd_args += shlex.split(config)
|
||||
if extension not in ('box', 'osd', 'tsv'):
|
||||
cmd_args.append(extension)
|
||||
try:
|
||||
proc = (subprocess.Popen)(cmd_args, **subprocess_args())
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
status_code, error_string = proc.wait(), proc.stderr.read()
|
||||
proc.stderr.close()
|
||||
if status_code:
|
||||
raise TesseractError(status_code, get_errors(error_string))
|
||||
return True
|
||||
|
||||
|
||||
def run_and_get_output(image, extension, lang=None, config='', nice=0, return_bytes=False):
|
||||
temp_name, input_filename = ('', '')
|
||||
try:
|
||||
temp_name, input_filename = save_image(image)
|
||||
kwargs = {'input_filename':input_filename,
|
||||
'output_filename_base':temp_name + '_out',
|
||||
'extension':extension,
|
||||
'lang':lang,
|
||||
'config':config,
|
||||
'nice':nice}
|
||||
run_tesseract(**kwargs)
|
||||
filename = kwargs['output_filename_base'] + os.extsep + extension
|
||||
with open(filename, 'rb') as (output_file):
|
||||
if return_bytes:
|
||||
return output_file.read()
|
||||
return output_file.read().decode('utf-8').strip()
|
||||
finally:
|
||||
cleanup(temp_name)
|
||||
|
||||
|
||||
def file_to_dict(tsv, cell_delimiter, str_col_idx):
|
||||
result = {}
|
||||
rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
|
||||
if not rows:
|
||||
return result
|
||||
else:
|
||||
header = rows.pop(0)
|
||||
if len(rows[(-1)]) < len(header):
|
||||
rows[(-1)].append('')
|
||||
if str_col_idx < 0:
|
||||
str_col_idx += len(header)
|
||||
for i, head in enumerate(header):
|
||||
result[head] = [int(row[i]) if i != str_col_idx else row[i] for row in rows]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def is_valid(val, _type):
|
||||
if _type is int:
|
||||
return val.isdigit()
|
||||
else:
|
||||
if _type is float:
|
||||
pass
|
||||
try:
|
||||
float(val)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def osd_to_dict(osd):
|
||||
return {OSD_KEYS[kv[0]][0]:OSD_KEYS[kv[0]][1](kv[1]) for kv in (line.split(': ') for line in osd.split('\n')) if len(kv) == 2 if is_valid(kv[1], OSD_KEYS[kv[0]][1])}
|
||||
|
||||
|
||||
@run_once
|
||||
def get_tesseract_version():
|
||||
"""
|
||||
Returns LooseVersion object of the Tesseract version
|
||||
"""
|
||||
try:
|
||||
return LooseVersion((subprocess.check_output)([tesseract_cmd, '--version'], **subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:]))
|
||||
except OSError:
|
||||
raise TesseractNotFoundError()
|
||||
|
||||
|
||||
def image_to_string(image, lang=None, config='', nice=0, boxes=False, output_type=Output.STRING):
|
||||
"""
|
||||
Returns the result of a Tesseract OCR run on the provided image to string
|
||||
"""
|
||||
if boxes:
|
||||
print("\nWarning: Argument 'boxes' is deprecated and will be removed in future versions. Use function image_to_boxes instead.\n")
|
||||
return image_to_boxes(image, lang, config, nice, output_type)
|
||||
else:
|
||||
args = [
|
||||
image, 'txt', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return {'text': run_and_get_output(*args)}
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_boxes(image, lang=None, config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing recognized characters and their box boundaries
|
||||
"""
|
||||
config += ' batch.nochop makebox'
|
||||
args = [image, 'box', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
box_header = 'char left bottom right top page\n'
|
||||
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_data(image, lang=None, config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing box boundaries, confidences,
|
||||
and other information. Requires Tesseract 3.05+
|
||||
"""
|
||||
if get_tesseract_version() < '3.05':
|
||||
raise TSVNotSupported()
|
||||
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
|
||||
args = [image, 'tsv', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return file_to_dict(run_and_get_output(*args), '\t', -1)
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def image_to_osd(image, lang='osd', config='', nice=0, output_type=Output.STRING):
|
||||
"""
|
||||
Returns string containing the orientation and script detection (OSD)
|
||||
"""
|
||||
config = '{}-psm 0 {}'.format('' if get_tesseract_version() < '3.05' else '-', config.strip()).strip()
|
||||
args = [
|
||||
image, 'osd', lang, config, nice]
|
||||
if output_type == Output.DICT:
|
||||
return osd_to_dict(run_and_get_output(*args))
|
||||
else:
|
||||
if output_type == Output.BYTES:
|
||||
args.append(True)
|
||||
return run_and_get_output(*args)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 2:
|
||||
filename, lang = sys.argv[1], None
|
||||
else:
|
||||
if len(sys.argv) == 4:
|
||||
if sys.argv[1] == '-l':
|
||||
filename, lang = sys.argv[3], sys.argv[2]
|
||||
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n')
|
||||
exit(2)
|
||||
try:
|
||||
print(image_to_string((Image.open(filename)), lang=lang))
|
||||
except IOError:
|
||||
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
154
updater.py
Normal file
154
updater.py
Normal file
@ -0,0 +1,154 @@
|
||||
"""
|
||||
********************************************************************************
|
||||
* CNIRevelator *
|
||||
* *
|
||||
* Desc: Application launcher updating system *
|
||||
* *
|
||||
* Copyright © 2018-2019 Adrien Bourmault (neox95) *
|
||||
* *
|
||||
* This file is part of CNIRevelator. *
|
||||
* *
|
||||
* CNIRevelator is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* any later version. *
|
||||
* *
|
||||
* CNIRevelator is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY*without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with CNIRevelator. If not, see <https:*www.gnu.org/licenses/>. *
|
||||
********************************************************************************
|
||||
"""
|
||||
|
||||
from win32com.client import Dispatch
|
||||
import traceback
|
||||
import sys
|
||||
import time
|
||||
|
||||
import logger # logger.py
|
||||
import globs # globs.py
|
||||
import ihm # ihm.py
|
||||
import downloader # downloader.py
|
||||
|
||||
def createShortcut(path, target='', wDir='', icon=''):
|
||||
ext = path[-3:]
|
||||
if ext == 'url':
|
||||
shortcut = file(path, 'w')
|
||||
shortcut.write('[InternetShortcut]\n')
|
||||
shortcut.write('URL=%s' % target)
|
||||
shortcut.close()
|
||||
else:
|
||||
shell = Dispatch('WScript.Shell')
|
||||
shortcut = shell.CreateShortCut(path)
|
||||
shortcut.Targetpath = target
|
||||
shortcut.WorkingDirectory = wDir
|
||||
if icon == '':
|
||||
pass
|
||||
else:
|
||||
shortcut.IconLocation = icon
|
||||
shortcut.save()
|
||||
|
||||
## Main Batch Function
|
||||
def batch():
|
||||
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
credentials = downloader.newcredentials()
|
||||
|
||||
if not credentials.valid:
|
||||
return False
|
||||
|
||||
# First retrieving the urls !
|
||||
while True:
|
||||
try:
|
||||
# Open the config file
|
||||
logfile.printdbg('Reading urlconf.ig')
|
||||
with open(globs.CNIRUrlConfig, 'r') as (configFile):
|
||||
try:
|
||||
# Reading it
|
||||
reading = configFile.read()
|
||||
# Parsing it
|
||||
urlparsed = reading.split("\n")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
raise IOError(str(e))
|
||||
|
||||
except FileNotFoundError:
|
||||
logfile.printdbg('Recreate urlconf.ig')
|
||||
# Recreating the url file
|
||||
with open(globs.CNIRUrlConfig, 'w') as (configFile):
|
||||
configFile.write("https://raw.githubusercontent.com/neox95/CNIRevelator/master/VERSIONS.LST\n0\n0") #XXX
|
||||
|
||||
# Getting the list of versions of the software
|
||||
logfile.printdbg('Retrieving the software versions')
|
||||
getTheVersions = downloader.newdownload(credentials, urlparsed[0], globs.CNIRFolder + '\\versions.lst').download()
|
||||
|
||||
logfile.printdbg('Parsing the software versions')
|
||||
with open(globs.CNIRFolder + '\\versions.lst') as versionsFile:
|
||||
versionsTab = versionsFile.read().split("\n")[1].split("||")
|
||||
logfile.printdbg('Versions retrieved : {}'.format(versionsTab))
|
||||
# Choose the newer
|
||||
finalver = globs.version.copy()
|
||||
for entry in versionsTab:
|
||||
verstr, url, checksum = entry.split("|")
|
||||
# Calculating sum considering we can have 99 sub versions
|
||||
ver = verstr.split(".")
|
||||
ver = [int(i) for i in ver]
|
||||
finalsum = finalver[2] + finalver[1]*100 + finalver[0]*100*100
|
||||
sum = ver[2] + ver[1]*100 + ver[0]*100*100
|
||||
# Make a statement
|
||||
if sum > finalsum:
|
||||
finalver = ver.copy()
|
||||
finalurl = url
|
||||
|
||||
if finalver == globs.version:
|
||||
logfile.printdbg('The software is already the newer version')
|
||||
return True
|
||||
|
||||
logfile.printdbg('Preparing download for the new version')
|
||||
|
||||
getTheUpdate = downloader.newdownload(credentials, finalurl, globs.CNIRFolder + '\\..\\CNIPackage.zip').download()
|
||||
|
||||
return True
|
||||
|
||||
## Main Function
|
||||
def umain():
|
||||
try:
|
||||
# Global Handlers
|
||||
logfile = logger.logCur
|
||||
launcherWindow = ihm.launcherWindowCur
|
||||
|
||||
try:
|
||||
# EXECUTING THE UPDATE BATCH
|
||||
success = batch()
|
||||
except Exception as e:
|
||||
logfile.printerr("An error occured on the thread : " + str(traceback.format_exc()))
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text=('ERROR : ' + str(e)))
|
||||
time.sleep(3)
|
||||
launcherWindow.destroy()
|
||||
return 1
|
||||
|
||||
if success:
|
||||
logfile.printdbg("Software is up-to-date !")
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text='Software is up-to-date !')
|
||||
else:
|
||||
logfile.printerr("An error occured. No effective update !")
|
||||
launcherWindow.mainCanvas.itemconfigure(launcherWindow.msg, text='An error occured. No effective update !')
|
||||
time.sleep(2)
|
||||
launcherWindow.destroy()
|
||||
return 0
|
||||
|
||||
except:
|
||||
logfile.printerr("A FATAL ERROR OCCURED : " + str(traceback.format_exc()))
|
||||
launcherWindow.destroy()
|
||||
sys.exit(2)
|
||||
return 2
|
||||
|
||||
return
|
||||
|
2
versions.lst
Normal file
2
versions.lst
Normal file
@ -0,0 +1,2 @@
|
||||
# ver|url|checksum, and | as separator, one version per ||
|
||||
0.0.0|https://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/CNIRevelator_2.2.5|1234||3.0.1|https://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/CNIRevelator_2.2.5|1234||0.0.1|https://www.os-k.eu|1234||3.0.2|https://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/CNIRevelator_2.2.5|1234
|
Loading…
Reference in New Issue
Block a user