From cac10bacc6a038fc8259d310d24098fb25319fa2 Mon Sep 17 00:00:00 2001 From: neox95 Date: Mon, 1 Jul 2019 23:33:05 +0200 Subject: [PATCH] Begin reorganization --- COMPILER.bat => make.bat | 7 +- .../analyzer/CNI_GLOBALVAR.py | 104 +- .../analyzer/CNI_Revelator.py | 225 ++-- CNI_Update.py => src/analyzer/CNI_Update.py | 997 +++++++++--------- CNI_classes.py => src/analyzer/CNI_classes.py | 31 +- .../analyzer/CNI_pytesseract.py | 635 +++++------ src/launcher/CNIRLauncher.py | 34 + src/launcher/downloader.py | 24 + src/launcher/globs.py | 29 + src/launcher/logger.py | 59 ++ 10 files changed, 1178 insertions(+), 967 deletions(-) rename COMPILER.bat => make.bat (56%) rename CNI_GLOBALVAR.py => src/analyzer/CNI_GLOBALVAR.py (51%) rename CNI_Revelator.py => src/analyzer/CNI_Revelator.py (66%) rename CNI_Update.py => src/analyzer/CNI_Update.py (94%) rename CNI_classes.py => src/analyzer/CNI_classes.py (97%) rename CNI_pytesseract.py => src/analyzer/CNI_pytesseract.py (83%) create mode 100644 src/launcher/CNIRLauncher.py create mode 100644 src/launcher/downloader.py create mode 100644 src/launcher/globs.py create mode 100644 src/launcher/logger.py diff --git a/COMPILER.bat b/make.bat similarity index 56% rename from COMPILER.bat rename to make.bat index b69c8cf..5b0e6e6 100644 --- a/COMPILER.bat +++ b/make.bat @@ -1,10 +1,9 @@ @echo off -set /p version=Numero de version: +title Compilation de CNIRevelator +call pyinstaller -w -D --exclude-module PyQt5 --bootloader-ignore-signals --add-data "C:\users\adrie\Anaconda3\Lib\site-packages\tld\res\effective_tld_names.dat.txt";"tld\res" --add-data "id-card.ico";"id-card.ico" -i "id-card.ico" -n CNIRevelator src\analyzer\CNI_Revelator.py -title Compilation du programme final - -call pyinstaller -w -D --exclude-module PyQt5 --bootloader-ignore-signals --add-data "C:\users\adrie\Anaconda3\Lib\site-packages\tld\res\effective_tld_names.dat.txt";"tld\res" --add-data "id-card.ico";"id-card.ico" -i "id-card.ico" -n CNIRevelator_%version%.exe CNI_Revelator.py +copy LICENSE dist\CNIRevelator\ pause diff --git a/CNI_GLOBALVAR.py b/src/analyzer/CNI_GLOBALVAR.py similarity index 51% rename from CNI_GLOBALVAR.py rename to src/analyzer/CNI_GLOBALVAR.py index db74c3e..ba94884 100644 --- a/CNI_GLOBALVAR.py +++ b/src/analyzer/CNI_GLOBALVAR.py @@ -1,47 +1,59 @@ -""" -******************************************************************************** - *** Projet CNI_Revelator *** - - GNU GPL * 07/2018 - - Adrien Bourmault - - VARIABLES - -******************************************************************************** -""" - -class changelog: - - def __init__(self): - self.isOn = False - self.text = "Mise-à-jour de sécurité avec corrections suivantes :\n- ajout de la signature numérique de l'exécutable\n- ajout d'une clé de cryptage plus performante pour le stockage des identifiants\n- passage à la méthode de cryptage AES256\n\nAjout/correction des fonctionnalités suivantes :\n- somme de contrôle des téléchargements pour une meilleure fiabilité\n- amélioration de présentation du log en cas d'erreur\n- correction d'un bug affectant l'analyse des MRZ après la correction manuelle\n\nEt un petit bonjour à tout le monde! ;)" - - -CST_REV = '0' -CST_VERTITLE = '2.2' -CST_TAB_VER = ['2', '2', '5'] -CST_VER = '{0}.{1}.{2}'.format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2]) -CST_TYPE = 'Final Release' -CST_NAME = 'CNIRevelator' -CST_TITLE = CST_NAME + ' ' + CST_VER + ' - GNU/GPL Licensing 2018' -CST_MARK = CST_NAME + ' ' + CST_TYPE + ' ' + CST_VER + ' - by NeoX, GNU/GPL Licensing 2018' -CST_SUM_VER = int(CST_TAB_VER[0]) * 100 + int(CST_TAB_VER[1]) * 10 + int(CST_TAB_VER[2]) -CST_LINK = 'http://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/' -CST_COLOR = '#003380' -CST_TesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8' - - -import base64, hashlib -from Crypto import Random -from Crypto.Cipher import AES -from tkinter import * -from tkinter.messagebox import * -from tkinter import filedialog -from tkinter import ttk -import os, time, threading, sys, urllib.request as urllib2, urllib.error as URLExcept, random -from datetime import datetime - -CST_FOLDER = os.getenv('APPDATA') + '/CNIRevelator/' -CST_CRYPTOKEY = '82Xh!efX3#@P~2eG' +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" + +class changelog: + + def __init__(self): + self.isOn = False + self.text = "Mise-à-jour de sécurité avec corrections suivantes :\n- ajout de la signature numérique de l'exécutable\n- ajout d'une clé de cryptage plus performante pour le stockage des identifiants\n- passage à la méthode de cryptage AES256\n\nAjout/correction des fonctionnalités suivantes :\n- somme de contrôle des téléchargements pour une meilleure fiabilité\n- amélioration de présentation du log en cas d'erreur\n- correction d'un bug affectant l'analyse des MRZ après la correction manuelle\n\nEt un petit bonjour à tout le monde! ;)" + + +CST_REV = '0' +CST_VERTITLE = '2.2' +CST_TAB_VER = ['2', '2', '5'] +CST_VER = '{0}.{1}.{2}'.format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2]) +CST_TYPE = 'Final Release' +CST_NAME = 'CNIRevelator' +CST_TITLE = CST_NAME + ' ' + CST_VER + ' - GNU/GPL Licensing 2018' +CST_MARK = CST_NAME + ' ' + CST_TYPE + ' ' + CST_VER + ' - by NeoX, GNU/GPL Licensing 2018' +CST_SUM_VER = int(CST_TAB_VER[0]) * 100 + int(CST_TAB_VER[1]) * 10 + int(CST_TAB_VER[2]) +CST_LINK = 'http://neoxgroup.eu/ftpaccess/Applicatifs/CNIRevelator/' +CST_COLOR = '#003380' +CST_TesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8' + + +import base64, hashlib +from Crypto import Random +from Crypto.Cipher import AES +from tkinter import * +from tkinter.messagebox import * +from tkinter import filedialog +from tkinter import ttk +import os, time, threading, sys, urllib.request as urllib2, urllib.error as URLExcept, random +from datetime import datetime + +CST_FOLDER = os.getenv('APPDATA') + '/CNIRevelator/' +CST_CRYPTOKEY = '82Xh!efX3#@P~2eG' CST_CHANGELOG = changelog() \ No newline at end of file diff --git a/CNI_Revelator.py b/src/analyzer/CNI_Revelator.py similarity index 66% rename from CNI_Revelator.py rename to src/analyzer/CNI_Revelator.py index dd890b1..947d93f 100644 --- a/CNI_Revelator.py +++ b/src/analyzer/CNI_Revelator.py @@ -1,107 +1,120 @@ -""" -******************************************************************************** - *** Projet CNI_Revelator *** - - GNU GPL * 07/2018 - - Adrien Bourmault - - main - -******************************************************************************** -""" -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()*****') -try: - Answer = SoftUpdate(logger) -except Exception as e: - logger.info('launcher : *****FATAL ERROR*****' + str(e)) - os.abort() - -logger.info('launcher : *****Ending SoftUpdate()*****') -try: - if Answer == True: - logger.info('launcher : *****Launching main()*****') - State = main(logger) -except Exception as e: - logger.info('launcher : *****FATAL ERROR*****' + str(e)) - os.abort() - -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") +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" + +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()*****') +try: + Answer = SoftUpdate(logger) +except Exception as e: + logger.info('launcher : *****FATAL ERROR*****' + str(e)) + os.abort() + +logger.info('launcher : *****Ending SoftUpdate()*****') +try: + if Answer == True: + logger.info('launcher : *****Launching main()*****') + State = main(logger) +except Exception as e: + logger.info('launcher : *****FATAL ERROR*****' + str(e)) + os.abort() + +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) \ No newline at end of file diff --git a/CNI_Update.py b/src/analyzer/CNI_Update.py similarity index 94% rename from CNI_Update.py rename to src/analyzer/CNI_Update.py index 9710c8e..834dd39 100644 --- a/CNI_Update.py +++ b/src/analyzer/CNI_Update.py @@ -1,493 +1,506 @@ -""" -******************************************************************************** - *** Projet CNI_Revelator *** - - GNU GPL * 07/2018 - - Adrien Bourmault - - UPDATE - -******************************************************************************** -""" -from CNI_GLOBALVAR import * -from CNI_classes import * -import hashlib -from pypac import PACSession -from requests.auth import HTTPProxyAuth -import subprocess - -def SoftUpdate(logger): - global ret - global upwin - import zipfile - for f in os.listdir(CST_FOLDER): - if f[-4:] == '.tif': - try: - os.remove(CST_FOLDER + '\\' + f) - except PermissionError as e: - logger.info('SoftUpdate() : Failing to purge : ' + str(e)) - - logger.info('SoftUpdate() : Looking for older version in dir...') - list = os.listdir('.') - for file in list: - if file.startswith('CNIRevelator_'): - temp = ['0', '0', '0'] - ver = file[13:].split('.') - for i in range(len(ver)): - if ver[i] != 'exe': - try: - temp[i] = ver[i] - except: - pass - - ver = temp.copy() - try: - sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2]) - if sum_ver < CST_SUM_VER: - if file[-3:] == 'exe': - os.remove(file) - logger.info('SoftUpdate() : Removed old version : ' + str(file)) - CST_CHANGELOG.isOn = True - except Exception as e: - logger.error('SoftUpdate() : Failing to remove old version ' + str(file) + ' : ' + str(e)) - - def updating(): - - def updator(): - global ret - logger.info('[updator() thread] : Welcome !') - ret = 11 - canvas.itemconfigure(message, text='Recherche de mises-à-jour...') - p.configure(mode='indeterminate', value=0, maximum=20) - p.start() - upwin.update() - logger.info('[updator() thread] : Looking for updates...') - try: - - def download(url, filename): - global key - global login - try: - logger.info('[download() thread] : Trying getting credentials in the config file') - with open(CST_FOLDER + 'conf.ig', 'rb') as (config): - AESObj = AESCipher(CST_CRYPTOKEY) - try: - tempone = AESObj.decrypt(config.read()) - if tempone != '||': - if tempone.find('||') != -1: - IPN, IPN_PASS = tempone.split('||')[0:2] - else: - raise ValueError('Cryptokey is bad !') - else: - IPN = '' - IPN_PASS = '' - except Exception as e: - raise IOError(str(e)) - else: - logger.info('[download() thread] : Got credentials !') - session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) - logger.info('[download() thread] : Authenticated to proxy successfully') - except IOError as e: - logger.error('[download() thread] : False or absent credentials in the config file : ' + str(e)) - NoConnect = True - while NoConnect: - - class LoginDialog(Toplevel): - - def __init__(self, parent): - 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) - w = 150 - h = 110 - self.update() - ws = self.winfo_screenwidth() - hs = self.winfo_screenheight() - if getattr(sys, 'frozen', False): - self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico') - else: - self.iconbitmap('id-card.ico') - upwin.update() - x = ws / 2 - w / 2 - y = hs / 2 - h / 2 - self.geometry('%dx%d+%d+%d' % (w, h, x, y)) - - def connecti(self): - global key - global login - login = self.entry_login.get().strip() - key = self.entry_pass.get().strip() - self.destroy() - - session = PACSession() - if session.get_pac() == None: - IPN = '' - IPN_PASS = '' - NoConnect = False - break - canvas.itemconfigure(message, text='En attente de connexion au serveur proxy...') - login = '' - key = '' - result = LoginDialog(upwin) - result.transient(upwin) - result.grab_set() - upwin.wait_window(result) - IPN = login - IPN_PASS = key - session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) - Ans = session.get('http://www.google.com') - if str(Ans) == '': - canvas.itemconfigure(message, text='Identifiants erronés, accès refusé') - logger.info('[download() thread] : 407 Error') - time.sleep(1) - else: - if str(Ans) == '': - logger.info('[download() thread] : Connection ok !') - NoConnect = False - else: - raise IOError() - - AESObj = AESCipher(CST_CRYPTOKEY) - with open(CST_FOLDER + 'conf.ig', 'wb+') as (f): - logger.info('[download() thread] : Saving credentials in encrypted config file') - f.write(AESObj.encrypt(IPN + '||' + IPN_PASS)) - - if IPN == 'i005316': - canvas.itemconfigure(message, text='Bienvenue Thierry !') - else: - if IPN == 'i020251': - canvas.itemconfigure(message, text='Bienvenue Samia !') - else: - if IPN == 'i018410': - canvas.itemconfigure(message, text='Bienvenue Adrien !') - else: - if IPN == 'i003067': - canvas.itemconfigure(message, text='Bienvenue Remy !') - else: - if IPN == 'i018422': - canvas.itemconfigure(message, text='Bienvenue Eloise !') - time.sleep(1) - try: - Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') - PROXY_USABLE = Prox_us[6:-1].split(';')[0] - proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE - ph = urllib2.ProxyHandler({'http': proxy_server_url}) - auth = urllib2.ProxyBasicAuthHandler() - server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) - urllib2.install_opener(server) - logger.info('[download() thread] : Proxy connection initiated successfully') - except: - logger.info('[download() thread] : Proxy connection not initiated') - - try: - urllib2.urlretrieve(url, filename) - return True - except Exception as e: - logger.error('[download() thread] : HTTP ERROR ') - return e - - logger.info('[updator() thread] : Prepare downloading the version recap file...') - tempfile = CST_FOLDER + 'temp' + str(random.randint(11111, 99999)) + '.cniu' - isOk = download(CST_LINK + 'cnir.ver', tempfile) - if not isOk: - raise isOk - urllib2.urlcleanup() - logger.info('[updator() thread] : Opening version recap file...') - file_ver = open(tempfile, 'r') - logger.info('[updator() thread] : Reading version recap file...') - version = file_ver.read() - logger.info('[updator() thread] : Closing version recap file...') - repert = version.split('|') - file_ver.close() - logger.info('[updator() thread] : Deleting version recap file...') - os.remove(tempfile) - logger.info('[updator() thread] : Parsing informations about version...') - final_f = 'CNI_file' - final_ver = ['0', '0', '0'] - final_hash = '' - for sentence in repert: - try: - file, hashref = sentence.split(':') - except ValueError: - pass - else: - if str.startswith(file, CST_NAME): - ver = file.replace(CST_NAME + '_', '').split('.') - temp = [ - '0', '0', '0'] - for i in range(len(ver)): - temp[i] = ver[i] - - ver = temp.copy() - sum_fver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2]) - sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2]) - if sum_ver > sum_fver: - final_ver = ver.copy() - final_f = file - final_hash = hashref - - sum_ver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2]) - if final_f != 'CNI_file': - if sum_ver > CST_SUM_VER: - logger.info('[updator() thread] : New version of CNIRevelator found !') - canvas.itemconfigure(message, text='Mise à jour disponible ! Préparation du téléchargement...') - logger.info('[updator() thread] : Preparing download') - with open(CST_FOLDER + 'conf.ig', 'rb') as (config): - logger.info('[updator() thread] : Reading credentials for proxy in config file...') - AESObj = AESCipher(CST_CRYPTOKEY) - IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2] - session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) - try: - Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') - PROXY_USABLE = Prox_us[6:-1].split(';')[0] - proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE - ph = urllib2.ProxyHandler({'http': proxy_server_url}) - auth = urllib2.ProxyBasicAuthHandler() - server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) - logger.info('[updator() thread] : Connection to the proxy initiated successfully !') - except: - canvas.itemconfigure(message, text='Téléchargement en connexion directe...') - server = urllib2.build_opener() - logger.info('[updator() thread] : Direct connection initiated successfully') - - logger.info('[updator() thread] : Launching download of ' + final_f) - Statut = Download(CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger) - BLOCKSIZE = 65536 - hasher = hashlib.sha1() - try: - with open(final_f, 'rb') as (afile): - buf = afile.read(BLOCKSIZE) - while len(buf) > 0: - hasher.update(buf) - buf = afile.read(BLOCKSIZE) - - hashcod = hasher.hexdigest() - if hashcod != final_hash: - Statut.success = False - logger.error('[updator() thread] : Hashcode Error :' + final_f) - try: - os.remove(final_f) - except IOError: - pass - - else: - logger.info('[updator() thread] : Hashcode pass :' + final_f) - except FileNotFoundError as e: - logger.error('[updator() thread] : File not found ' + final_f) - - if Statut.success: - try: - os.rename(final_f, final_f + '.exe') - except IOError: - logger.error('[updator() thread] : Unable to rename the file ! Wait 3 sec and retry') - time.sleep(3) - try: - os.rename(final_f, final_f + '.exe') - except IOError: - logger.critical('[updator() thread] : Unable to rename the file !') - - else: - canvas.itemconfigure(message, text='Téléchargement terminé ! Préparation du lancement...') - logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully') - p.configure(mode='indeterminate', value=0, maximum=20) - p.start() - time.sleep(1) - logger.info('[updator() thread] : Launching ' + final_f) - try: - proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True) - except: - logger.error('[updator() thread] : Unable to start the new version ! Wait 3 sec and retry') - time.sleep(3) - try: - proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True) - except Exception as e: - logger.critical('[updator() thread] : Unable to start the new version ! Stopping : ' + str(e)) - showerror("Erreur d'appel de procédure distante", 'Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée', parent=upwin) - - ret = 12 - else: - canvas.itemconfigure(message, text='Echec de la mise à jour : Erreur HTTP. Préparation du lancement...') - logger.error('[updator() thread] : Update has failed with HTTP error') - time.sleep(1) - canvas.itemconfigure(message, text='Logiciel déjà à jour. Préparation du lancement...') - logger.info('[updator() thread] : CNIRevelator is up to date !') - time.sleep(1) - ret = 11 - if os.path.exists(CST_FOLDER + 'Tesseract-OCR4\\tesseract.exe'): - os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' - os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' - else: - final_f = 'tesseract_4' - logger.info('[updator() thread] : Downloading tesseract 4 !') - canvas.itemconfigure(message, text='Mise à jour du module OCR ! Préparation du téléchargement...') - logger.info('[updator() thread] : Preparing download') - with open(CST_FOLDER + 'conf.ig', 'rb') as (config): - logger.info('[updator() thread] : Reading credentials for proxy in config file...') - AESObj = AESCipher(CST_CRYPTOKEY) - IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2] - session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) - try: - Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') - PROXY_USABLE = Prox_us[6:-1].split(';')[0] - proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE - ph = urllib2.ProxyHandler({'http': proxy_server_url}) - auth = urllib2.ProxyBasicAuthHandler() - server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) - logger.info('[updator() thread] : Connection to the proxy initiated successfully !') - except: - canvas.itemconfigure(message, text='Téléchargement en connexion directe...') - server = urllib2.build_opener() - logger.info('[updator() thread] : Direct connection initiated successfully') - - logger.info('[updator() thread] : Launching download of ' + final_f) - Statut = Download(CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger) - hashcod = '' - logger.info('[updator() thread] : Verifying hashcode of ' + final_f) - BLOCKSIZE = 65536 - hasher = hashlib.sha1() - try: - with open(CST_FOLDER + final_f, 'rb') as (afile): - buf = afile.read(BLOCKSIZE) - while len(buf) > 0: - hasher.update(buf) - buf = afile.read(BLOCKSIZE) - - hashcod = hasher.hexdigest() - if hashcod != CST_TesserHash: - Statut.success = False - logger.error('[updator() thread] : Hashcode Error : ' + final_f) - try: - os.remove(CST_FOLDER + final_f) - except IOError: - pass - - else: - logger.info('[updator() thread] : Hashcode pass : ' + final_f) - except FileNotFoundError as e: - logger.error('[updator() thread] : File not found ' + final_f) - - if Statut.success: - canvas.itemconfigure(message, text='Téléchargement terminé ! Installation...') - logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully') - p.configure(mode='indeterminate', value=0, maximum=20) - p.start() - try: - zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') - zip_ref.extractall(CST_FOLDER) - zip_ref.close() - os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' - os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' - canvas.itemconfigure(message, text='Installation terminée !') - except: - logger.error('[updator() thread] : Unable to install the module. Wait and retry') - time.sleep(3) - try: - zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') - zip_ref.extractall(CST_FOLDER) - zip_ref.close() - os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' - os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' - canvas.itemconfigure(message, text='Installation terminée !') - except Exception as e: - logger.critical('[updator() thread] : Unable to install the module ! Stopping : ' + str(e)) - showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.") - - ret = 11 - else: - logger.critical('[updator() thread] : Unable to download the module ! ') - showerror('Erreur de téléchargement', "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante") - path = filedialog.askopenfilename(title="Indiquez le chemin d'accès à tesseract_4...", filetypes=(('Tesseract_4', '*.cni4'), - ('Tesseract_4', '*.cni4'))) - if path != '': - try: - canvas.itemconfigure(message, text='Installation...') - zip_ref = zipfile.ZipFile(path, 'r') - zip_ref.extractall(CST_FOLDER) - zip_ref.close() - logger.error('[updator() thread] : Manual installation successed') - canvas.itemconfigure(message, text='Installation terminée !') - os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' - os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' - except Exception as e: - logger.error('[updator() thread] : Manual installation has failed' + str(e)) - showerror('Erreur de lecture', "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner") - - else: - showerror('Opération annulée', "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner") - except URLExcept.HTTPError as e: - canvas.itemconfigure(message, text=('Echec de la mise à jour : Erreur HTTP ' + str(e.code) + ' . Préparation du lancement...')) - logger.error('[updator() thread] : Update has failed with HTTP error' + str(e.code)) - if int(e.code) == 407: - showerror('Erreur 407', 'Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.') - logger.info('[updator() thread] : Credential error. Deleting the config file...') - os.remove(CST_FOLDER + 'conf.ig') - p.configure(mode='indeterminate', value=0, maximum=20) - p.start() - time.sleep(3) - except Exception as e: - canvas.itemconfigure(message, text='Echec de la mise à jour. Préparation du lancement...') - logger.error('[updator() thread] : Error from the updating system : ' + str(e)) - p.configure(mode='indeterminate', value=0, maximum=20) - p.start() - time.sleep(2) - - p.stop() - upwin.destroy() - root.destroy() - return ret - - logger.info('updating() : Launching updator() thread...') - threading.Thread(target=updator, daemon=True).start() - logger.info('updating() [Thread] : Ending updator() thread') - - ret = 11 - root = Tk() - root.attributes('-alpha', 0.0) - root.iconify() - upwin = Toplevel(root) - upwin.overrideredirect(1) - upwin.configure(bg=CST_COLOR) - upwin.resizable(width=False, height=False) - w = 600 - h = 300 - upwin.update() - canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0) - pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR) - p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate') - upwin.update() - ws = upwin.winfo_screenwidth() - hs = upwin.winfo_screenheight() - canvas.create_text((w / 2), (h / 3), text=(CST_NAME + ' ' + CST_VERTITLE), font='Calibri 30 bold', fill='white') - message = canvas.create_text((w / 2), (h / 1.15), text=' ', font='Calibri 9', fill='white') - upwin.update() - x = ws / 2 - w / 2 - y = hs / 2 - h / 2 - upwin.geometry('%dx%d+%d+%d' % (w, h, x, y)) - canvas.grid() - pbar.grid() - p.grid() - upwin.after(2000, updating) - if getattr(sys, 'frozen', False): - root.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico') - else: - root.iconbitmap('id-card.ico') - logger.info('SoftUpdate() : Entering upwin mainloop()') - upwin.protocol('WM_DELETE_WINDOW', lambda : root.destroy()) - upwin.mainloop() - logger.info('SoftUpdate() : Exiting upwin mainloop()') - if ret == 11: - logger.info('SoftUpdate() : OK to start to main() normally !') - return True - else: - logger.info('SoftUpdate() : Program will stop !') +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" + +from CNI_GLOBALVAR import * +from CNI_classes import * +import hashlib +from pypac import PACSession +from requests.auth import HTTPProxyAuth +import subprocess + +def SoftUpdate(logger): + global ret + global upwin + import zipfile + for f in os.listdir(CST_FOLDER): + if f[-4:] == '.tif': + try: + os.remove(CST_FOLDER + '\\' + f) + except PermissionError as e: + logger.info('SoftUpdate() : Failing to purge : ' + str(e)) + + logger.info('SoftUpdate() : Looking for older version in dir...') + list = os.listdir('.') + for file in list: + if file.startswith('CNIRevelator_'): + temp = ['0', '0', '0'] + ver = file[13:].split('.') + for i in range(len(ver)): + if ver[i] != 'exe': + try: + temp[i] = ver[i] + except: + pass + + ver = temp.copy() + try: + sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2]) + if sum_ver < CST_SUM_VER: + if file[-3:] == 'exe': + os.remove(file) + logger.info('SoftUpdate() : Removed old version : ' + str(file)) + CST_CHANGELOG.isOn = True + except Exception as e: + logger.error('SoftUpdate() : Failing to remove old version ' + str(file) + ' : ' + str(e)) + + def updating(): + + def updator(): + global ret + logger.info('[updator() thread] : Welcome !') + ret = 11 + canvas.itemconfigure(message, text='Recherche de mises-à-jour...') + p.configure(mode='indeterminate', value=0, maximum=20) + p.start() + upwin.update() + logger.info('[updator() thread] : Looking for updates...') + try: + + def download(url, filename): + global key + global login + try: + logger.info('[download() thread] : Trying getting credentials in the config file') + with open(CST_FOLDER + 'conf.ig', 'rb') as (config): + AESObj = AESCipher(CST_CRYPTOKEY) + try: + tempone = AESObj.decrypt(config.read()) + if tempone != '||': + if tempone.find('||') != -1: + IPN, IPN_PASS = tempone.split('||')[0:2] + else: + raise ValueError('Cryptokey is bad !') + else: + IPN = '' + IPN_PASS = '' + except Exception as e: + raise IOError(str(e)) + else: + logger.info('[download() thread] : Got credentials !') + session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) + logger.info('[download() thread] : Authenticated to proxy successfully') + except IOError as e: + logger.error('[download() thread] : False or absent credentials in the config file : ' + str(e)) + NoConnect = True + while NoConnect: + + class LoginDialog(Toplevel): + + def __init__(self, parent): + 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) + w = 150 + h = 110 + self.update() + ws = self.winfo_screenwidth() + hs = self.winfo_screenheight() + if getattr(sys, 'frozen', False): + self.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico') + else: + self.iconbitmap('id-card.ico') + upwin.update() + x = ws / 2 - w / 2 + y = hs / 2 - h / 2 + self.geometry('%dx%d+%d+%d' % (w, h, x, y)) + + def connecti(self): + global key + global login + login = self.entry_login.get().strip() + key = self.entry_pass.get().strip() + self.destroy() + + session = PACSession() + if session.get_pac() == None: + IPN = '' + IPN_PASS = '' + NoConnect = False + break + canvas.itemconfigure(message, text='En attente de connexion au serveur proxy...') + login = '' + key = '' + result = LoginDialog(upwin) + result.transient(upwin) + result.grab_set() + upwin.wait_window(result) + IPN = login + IPN_PASS = key + session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) + Ans = session.get('http://www.google.com') + if str(Ans) == '': + canvas.itemconfigure(message, text='Identifiants erronés, accès refusé') + logger.info('[download() thread] : 407 Error') + time.sleep(1) + else: + if str(Ans) == '': + logger.info('[download() thread] : Connection ok !') + NoConnect = False + else: + raise IOError() + + AESObj = AESCipher(CST_CRYPTOKEY) + with open(CST_FOLDER + 'conf.ig', 'wb+') as (f): + logger.info('[download() thread] : Saving credentials in encrypted config file') + f.write(AESObj.encrypt(IPN + '||' + IPN_PASS)) + + if IPN == 'i005316': + canvas.itemconfigure(message, text='Bienvenue Thierry !') + else: + if IPN == 'i020251': + canvas.itemconfigure(message, text='Bienvenue Samia !') + else: + if IPN == 'i018410': + canvas.itemconfigure(message, text='Bienvenue Adrien !') + else: + if IPN == 'i003067': + canvas.itemconfigure(message, text='Bienvenue Remy !') + else: + if IPN == 'i018422': + canvas.itemconfigure(message, text='Bienvenue Eloise !') + time.sleep(1) + try: + Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') + PROXY_USABLE = Prox_us[6:-1].split(';')[0] + proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE + ph = urllib2.ProxyHandler({'http': proxy_server_url}) + auth = urllib2.ProxyBasicAuthHandler() + server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) + urllib2.install_opener(server) + logger.info('[download() thread] : Proxy connection initiated successfully') + except: + logger.info('[download() thread] : Proxy connection not initiated') + + try: + urllib2.urlretrieve(url, filename) + return True + except Exception as e: + logger.error('[download() thread] : HTTP ERROR ') + return e + + logger.info('[updator() thread] : Prepare downloading the version recap file...') + tempfile = CST_FOLDER + 'temp' + str(random.randint(11111, 99999)) + '.cniu' + isOk = download(CST_LINK + 'cnir.ver', tempfile) + if not isOk: + raise isOk + urllib2.urlcleanup() + logger.info('[updator() thread] : Opening version recap file...') + file_ver = open(tempfile, 'r') + logger.info('[updator() thread] : Reading version recap file...') + version = file_ver.read() + logger.info('[updator() thread] : Closing version recap file...') + repert = version.split('|') + file_ver.close() + logger.info('[updator() thread] : Deleting version recap file...') + os.remove(tempfile) + logger.info('[updator() thread] : Parsing informations about version...') + final_f = 'CNI_file' + final_ver = ['0', '0', '0'] + final_hash = '' + for sentence in repert: + try: + file, hashref = sentence.split(':') + except ValueError: + pass + else: + if str.startswith(file, CST_NAME): + ver = file.replace(CST_NAME + '_', '').split('.') + temp = [ + '0', '0', '0'] + for i in range(len(ver)): + temp[i] = ver[i] + + ver = temp.copy() + sum_fver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2]) + sum_ver = int(ver[0]) * 100 + int(ver[1]) * 10 + int(ver[2]) + if sum_ver > sum_fver: + final_ver = ver.copy() + final_f = file + final_hash = hashref + + sum_ver = int(final_ver[0]) * 100 + int(final_ver[1]) * 10 + int(final_ver[2]) + if final_f != 'CNI_file': + if sum_ver > CST_SUM_VER: + logger.info('[updator() thread] : New version of CNIRevelator found !') + canvas.itemconfigure(message, text='Mise à jour disponible ! Préparation du téléchargement...') + logger.info('[updator() thread] : Preparing download') + with open(CST_FOLDER + 'conf.ig', 'rb') as (config): + logger.info('[updator() thread] : Reading credentials for proxy in config file...') + AESObj = AESCipher(CST_CRYPTOKEY) + IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2] + session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) + try: + Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') + PROXY_USABLE = Prox_us[6:-1].split(';')[0] + proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE + ph = urllib2.ProxyHandler({'http': proxy_server_url}) + auth = urllib2.ProxyBasicAuthHandler() + server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) + logger.info('[updator() thread] : Connection to the proxy initiated successfully !') + except: + canvas.itemconfigure(message, text='Téléchargement en connexion directe...') + server = urllib2.build_opener() + logger.info('[updator() thread] : Direct connection initiated successfully') + + logger.info('[updator() thread] : Launching download of ' + final_f) + Statut = Download(CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger) + BLOCKSIZE = 65536 + hasher = hashlib.sha1() + try: + with open(final_f, 'rb') as (afile): + buf = afile.read(BLOCKSIZE) + while len(buf) > 0: + hasher.update(buf) + buf = afile.read(BLOCKSIZE) + + hashcod = hasher.hexdigest() + if hashcod != final_hash: + Statut.success = False + logger.error('[updator() thread] : Hashcode Error :' + final_f) + try: + os.remove(final_f) + except IOError: + pass + + else: + logger.info('[updator() thread] : Hashcode pass :' + final_f) + except FileNotFoundError as e: + logger.error('[updator() thread] : File not found ' + final_f) + + if Statut.success: + try: + os.rename(final_f, final_f + '.exe') + except IOError: + logger.error('[updator() thread] : Unable to rename the file ! Wait 3 sec and retry') + time.sleep(3) + try: + os.rename(final_f, final_f + '.exe') + except IOError: + logger.critical('[updator() thread] : Unable to rename the file !') + + else: + canvas.itemconfigure(message, text='Téléchargement terminé ! Préparation du lancement...') + logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully') + p.configure(mode='indeterminate', value=0, maximum=20) + p.start() + time.sleep(1) + logger.info('[updator() thread] : Launching ' + final_f) + try: + proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True) + except: + logger.error('[updator() thread] : Unable to start the new version ! Wait 3 sec and retry') + time.sleep(3) + try: + proc = subprocess.Popen((final_f + '.exe'), shell=False, stdin=None, stdout=None, stderr=None, close_fds=True) + except Exception as e: + logger.critical('[updator() thread] : Unable to start the new version ! Stopping : ' + str(e)) + showerror("Erreur d'appel de procédure distante", 'Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée', parent=upwin) + + ret = 12 + else: + canvas.itemconfigure(message, text='Echec de la mise à jour : Erreur HTTP. Préparation du lancement...') + logger.error('[updator() thread] : Update has failed with HTTP error') + time.sleep(1) + canvas.itemconfigure(message, text='Logiciel déjà à jour. Préparation du lancement...') + logger.info('[updator() thread] : CNIRevelator is up to date !') + time.sleep(1) + ret = 11 + if os.path.exists(CST_FOLDER + 'Tesseract-OCR4\\tesseract.exe'): + os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' + os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' + else: + final_f = 'tesseract_4' + logger.info('[updator() thread] : Downloading tesseract 4 !') + canvas.itemconfigure(message, text='Mise à jour du module OCR ! Préparation du téléchargement...') + logger.info('[updator() thread] : Preparing download') + with open(CST_FOLDER + 'conf.ig', 'rb') as (config): + logger.info('[updator() thread] : Reading credentials for proxy in config file...') + AESObj = AESCipher(CST_CRYPTOKEY) + IPN, IPN_PASS = AESObj.decrypt(config.read()).split('||')[0:2] + session = PACSession(proxy_auth=(HTTPProxyAuth(IPN, IPN_PASS))) + try: + Prox_us = session.get_pac().find_proxy_for_url(CST_LINK, 'neoxgroup.eu') + PROXY_USABLE = Prox_us[6:-1].split(';')[0] + proxy_server_url = IPN + ':' + IPN_PASS + '@' + PROXY_USABLE + ph = urllib2.ProxyHandler({'http': proxy_server_url}) + auth = urllib2.ProxyBasicAuthHandler() + server = urllib2.build_opener(ph, auth, urllib2.HTTPHandler) + logger.info('[updator() thread] : Connection to the proxy initiated successfully !') + except: + canvas.itemconfigure(message, text='Téléchargement en connexion directe...') + server = urllib2.build_opener() + logger.info('[updator() thread] : Direct connection initiated successfully') + + logger.info('[updator() thread] : Launching download of ' + final_f) + Statut = Download(CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger) + hashcod = '' + logger.info('[updator() thread] : Verifying hashcode of ' + final_f) + BLOCKSIZE = 65536 + hasher = hashlib.sha1() + try: + with open(CST_FOLDER + final_f, 'rb') as (afile): + buf = afile.read(BLOCKSIZE) + while len(buf) > 0: + hasher.update(buf) + buf = afile.read(BLOCKSIZE) + + hashcod = hasher.hexdigest() + if hashcod != CST_TesserHash: + Statut.success = False + logger.error('[updator() thread] : Hashcode Error : ' + final_f) + try: + os.remove(CST_FOLDER + final_f) + except IOError: + pass + + else: + logger.info('[updator() thread] : Hashcode pass : ' + final_f) + except FileNotFoundError as e: + logger.error('[updator() thread] : File not found ' + final_f) + + if Statut.success: + canvas.itemconfigure(message, text='Téléchargement terminé ! Installation...') + logger.info('[updator() thread] : Download of ' + final_f + 'finished successfully') + p.configure(mode='indeterminate', value=0, maximum=20) + p.start() + try: + zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') + zip_ref.extractall(CST_FOLDER) + zip_ref.close() + os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' + os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' + canvas.itemconfigure(message, text='Installation terminée !') + except: + logger.error('[updator() thread] : Unable to install the module. Wait and retry') + time.sleep(3) + try: + zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r') + zip_ref.extractall(CST_FOLDER) + zip_ref.close() + os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' + os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' + canvas.itemconfigure(message, text='Installation terminée !') + except Exception as e: + logger.critical('[updator() thread] : Unable to install the module ! Stopping : ' + str(e)) + showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.") + + ret = 11 + else: + logger.critical('[updator() thread] : Unable to download the module ! ') + showerror('Erreur de téléchargement', "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante") + path = filedialog.askopenfilename(title="Indiquez le chemin d'accès à tesseract_4...", filetypes=(('Tesseract_4', '*.cni4'), + ('Tesseract_4', '*.cni4'))) + if path != '': + try: + canvas.itemconfigure(message, text='Installation...') + zip_ref = zipfile.ZipFile(path, 'r') + zip_ref.extractall(CST_FOLDER) + zip_ref.close() + logger.error('[updator() thread] : Manual installation successed') + canvas.itemconfigure(message, text='Installation terminée !') + os.environ['PATH'] = CST_FOLDER + 'Tesseract-OCR4\\' + os.environ['TESSDATA_PREFIX'] = CST_FOLDER + 'Tesseract-OCR4\\tessdata' + except Exception as e: + logger.error('[updator() thread] : Manual installation has failed' + str(e)) + showerror('Erreur de lecture', "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner") + + else: + showerror('Opération annulée', "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner") + except URLExcept.HTTPError as e: + canvas.itemconfigure(message, text=('Echec de la mise à jour : Erreur HTTP ' + str(e.code) + ' . Préparation du lancement...')) + logger.error('[updator() thread] : Update has failed with HTTP error' + str(e.code)) + if int(e.code) == 407: + showerror('Erreur 407', 'Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.') + logger.info('[updator() thread] : Credential error. Deleting the config file...') + os.remove(CST_FOLDER + 'conf.ig') + p.configure(mode='indeterminate', value=0, maximum=20) + p.start() + time.sleep(3) + except Exception as e: + canvas.itemconfigure(message, text='Echec de la mise à jour. Préparation du lancement...') + logger.error('[updator() thread] : Error from the updating system : ' + str(e)) + p.configure(mode='indeterminate', value=0, maximum=20) + p.start() + time.sleep(2) + + p.stop() + upwin.destroy() + root.destroy() + return ret + + logger.info('updating() : Launching updator() thread...') + threading.Thread(target=updator, daemon=True).start() + logger.info('updating() [Thread] : Ending updator() thread') + + ret = 11 + root = Tk() + root.attributes('-alpha', 0.0) + root.iconify() + upwin = Toplevel(root) + upwin.overrideredirect(1) + upwin.configure(bg=CST_COLOR) + upwin.resizable(width=False, height=False) + w = 600 + h = 300 + upwin.update() + canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0) + pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR) + p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate') + upwin.update() + ws = upwin.winfo_screenwidth() + hs = upwin.winfo_screenheight() + canvas.create_text((w / 2), (h / 3), text=(CST_NAME + ' ' + CST_VERTITLE), font='Calibri 30 bold', fill='white') + message = canvas.create_text((w / 2), (h / 1.15), text=' ', font='Calibri 9', fill='white') + upwin.update() + x = ws / 2 - w / 2 + y = hs / 2 - h / 2 + upwin.geometry('%dx%d+%d+%d' % (w, h, x, y)) + canvas.grid() + pbar.grid() + p.grid() + upwin.after(2000, updating) + if getattr(sys, 'frozen', False): + root.iconbitmap(sys._MEIPASS + '\\id-card.ico\\id-card.ico') + else: + root.iconbitmap('id-card.ico') + logger.info('SoftUpdate() : Entering upwin mainloop()') + upwin.protocol('WM_DELETE_WINDOW', lambda : root.destroy()) + upwin.mainloop() + logger.info('SoftUpdate() : Exiting upwin mainloop()') + if ret == 11: + logger.info('SoftUpdate() : OK to start to main() normally !') + return True + else: + logger.info('SoftUpdate() : Program will stop !') return False \ No newline at end of file diff --git a/CNI_classes.py b/src/analyzer/CNI_classes.py similarity index 97% rename from CNI_classes.py rename to src/analyzer/CNI_classes.py index abf27a3..18695a3 100644 --- a/CNI_classes.py +++ b/src/analyzer/CNI_classes.py @@ -1,15 +1,28 @@ """ -******************************************************************************** - *** Projet CNI_Revelator *** - - GNU GPL * 07/2018 - - Adrien Bourmault - - CLASSES - +******************************************************************************** +* 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 . * ******************************************************************************** """ + from CNI_GLOBALVAR import * from PIL import Image, ImageFont, ImageDraw, ImageTk, ImageEnhance, ImageFilter import math, warnings, string diff --git a/CNI_pytesseract.py b/src/analyzer/CNI_pytesseract.py similarity index 83% rename from CNI_pytesseract.py rename to src/analyzer/CNI_pytesseract.py index 27f5319..ba023a5 100644 --- a/CNI_pytesseract.py +++ b/src/analyzer/CNI_pytesseract.py @@ -1,311 +1,326 @@ -""" -******************************************************************************** - *** Projet CNI_Revelator *** - - GNU GPL * 07/2018 - - Adrien Bourmault - - Pytesseract modification to comply with Pyinstaller - -***** -""" -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__': +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" + + +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() \ No newline at end of file diff --git a/src/launcher/CNIRLauncher.py b/src/launcher/CNIRLauncher.py new file mode 100644 index 0000000..68765fd --- /dev/null +++ b/src/launcher/CNIRLauncher.py @@ -0,0 +1,34 @@ +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" + +import logger + +## Launching the launcher + +# Creating a log file +logfile = logger.NewLoggingSystem() + +# Hello world +logfile.printdbg('*** CNIRLauncher LOGFILE. Hello World ! ***') diff --git a/src/launcher/downloader.py b/src/launcher/downloader.py new file mode 100644 index 0000000..f6032d4 --- /dev/null +++ b/src/launcher/downloader.py @@ -0,0 +1,24 @@ +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" \ No newline at end of file diff --git a/src/launcher/globs.py b/src/launcher/globs.py new file mode 100644 index 0000000..3375a25 --- /dev/null +++ b/src/launcher/globs.py @@ -0,0 +1,29 @@ +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" +import os + +CNIRTesserHash = '5b58db27f7bc08c58a2cb33d01533b034b067cf8' +#CNIRFolder = os.getenv('APPDATA') + '/CNIRevelator/' +CNIRFolder = '.' \ No newline at end of file diff --git a/src/launcher/logger.py b/src/launcher/logger.py new file mode 100644 index 0000000..d2e1af4 --- /dev/null +++ b/src/launcher/logger.py @@ -0,0 +1,59 @@ +""" +******************************************************************************** +* 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 . * +******************************************************************************** +""" +## The logging class + +import logging +import globs + +class NewLoggingSystem: + + def __init__(self): + + # Deleting the error log + try: + os.remove(globs.CNIRFolder + '\\error.log') # The deletion does not working + except: + 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('[ %(module)s/%(funcName)s ] %(asctime)s :: %(levelname)s :: %(message)s') + error_handler = logging.FileHandler((globs.CNIRFolder + '\\error.log'), mode='w', encoding='utf-8', delay=True) + info_handler = logging.FileHandler((globs.CNIRFolder + '\\launcher.log'), 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 \ No newline at end of file