From acd09ff55170d566179b62d67a138fe35d6aad26 Mon Sep 17 00:00:00 2001 From: a-Sansara Date: Fri, 18 Jul 2014 01:48:07 +0200 Subject: [PATCH] version 1.01 - compat kirmah 2.18 --- CHANGELOG | 12 + README | 24 + cmd.bat | 43 +- impra-cli.py | 45 + impra.py | 45 + impra/__init__.py | 1 + impra/app.py | 264 +++ impra/cli.py | 1852 +++++++------------ impra/cliapp.py | 573 ++++++ impra/conf.py | 88 + impra/core.py | 1514 +++++----------- impra/crypt.py | 48 +- impra/gui.py | 858 +++++++++ impra/imap.py | 453 ----- impra/index.py | 730 ++++++++ impra/ini.py | 101 ++ impra/mail.py | 95 + impra/ui.py | 415 +++++ impra/util.py | 515 ------ impra/w32color.py | 71 - imprastorage.py | 47 +- kirmah-cli.py | 45 + kirmah/__init__.py | 1 + kirmah/app.py | 217 +++ kirmah/cli.py | 461 +++++ kirmah/cliapp.py | 334 ++++ kirmah/conf.py | 125 ++ kirmah/crypt.py | 1218 +++++++++++++ kirmah/gui.py | 418 +++++ kirmah/ui.py | 372 ++++ launcher.bat | 43 +- psr/__init__.py | 3 + psr/cli.py | 224 +++ psr/const.py | 105 ++ psr/imap.py | 553 ++++++ psr/ini.py | 209 +++ psr/io.py | 260 +++ psr/log.py | 127 ++ psr/mproc.py | 155 ++ psr/sys.py | 610 +++++++ psr/w32color.py | 71 + resources/impra/LICENSE | 674 +++++++ resources/impra/glade/impra.glade | 2398 +++++++++++++++++++++++++ resources/imprastorage.desktop | 12 + resources/pixmaps/impra/impra.png | Bin 0 -> 17155 bytes resources/pixmaps/impra/impra_ico.png | Bin 0 -> 14656 bytes scripts/imprastorage | 45 + scripts/imprastorage-cli | 45 + scripts/kirmah-cli | 45 + setup.py | 53 + setup_build.py | 51 +- 51 files changed, 13324 insertions(+), 3344 deletions(-) create mode 100755 CHANGELOG create mode 100755 README create mode 100755 impra-cli.py create mode 100755 impra.py create mode 100755 impra/app.py mode change 100644 => 100755 impra/cli.py create mode 100755 impra/cliapp.py create mode 100755 impra/conf.py mode change 100644 => 100755 impra/core.py create mode 100755 impra/gui.py delete mode 100644 impra/imap.py create mode 100755 impra/index.py create mode 100755 impra/ini.py create mode 100755 impra/mail.py create mode 100755 impra/ui.py delete mode 100644 impra/util.py delete mode 100644 impra/w32color.py create mode 100755 kirmah-cli.py create mode 100755 kirmah/__init__.py create mode 100755 kirmah/app.py create mode 100755 kirmah/cli.py create mode 100755 kirmah/cliapp.py create mode 100755 kirmah/conf.py create mode 100755 kirmah/crypt.py create mode 100755 kirmah/gui.py create mode 100755 kirmah/ui.py create mode 100755 psr/__init__.py create mode 100755 psr/cli.py create mode 100755 psr/const.py create mode 100755 psr/imap.py create mode 100755 psr/ini.py create mode 100755 psr/io.py create mode 100755 psr/log.py create mode 100755 psr/mproc.py create mode 100755 psr/sys.py create mode 100755 psr/w32color.py create mode 100755 resources/impra/LICENSE create mode 100755 resources/impra/glade/impra.glade create mode 100644 resources/imprastorage.desktop create mode 100755 resources/pixmaps/impra/impra.png create mode 100755 resources/pixmaps/impra/impra_ico.png create mode 100755 scripts/imprastorage create mode 100755 scripts/imprastorage-cli create mode 100755 scripts/kirmah-cli create mode 100644 setup.py diff --git a/CHANGELOG b/CHANGELOG new file mode 100755 index 0000000..73cf1b4 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,12 @@ +ImpraStorage 1.01 (2014-07-18) +========================= + +New General Features +------------------------- + + * + +Bugfixes +------------------------- + + * [#] -- diff --git a/README b/README new file mode 100755 index 0000000..96a1fd8 --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +ImpraStorage 1.01 (2014-07-18) +========================= + +Install on Archlinux +------------------------- +sudo pacman -U http://sourceforge.net/projects/imprastorage/files/imprastorage-1.01/packages/archlinux/kirmah-1.01-1-any.pkg.tar.xz/download + +(re)build instruction +------------------------- +mkdir /tmp/impra; cd /tmp/impra; +wget http://sourceforge.net/projects/imprastorage/files/imprastorage-1.01//packages/archlinux/PKGBUILD/download PKGBUILD +wget http://sourceforge.net/projects/imprastorage/files/imprastorage-1.01//packages/archlinux/imprastorage.install/download imprastorage.install +makepkg -s; + + +Install on other distrib +------------------------- +install pkg depends (look at archlinux PKGBUILD file wich provide usefull informations) + +simply untar archive, then launch : +$ python3 ./imprastorage/impra.py +$ python3 ./imprastorage/impra-cli.py + +you can also use python disutils and setup.py diff --git a/cmd.bat b/cmd.bat index 0c3c8a0..8010fab 100644 --- a/cmd.bat +++ b/cmd.bat @@ -1,28 +1,29 @@ -:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -:: # -:: software : ImpraStorage # -:: version : 0.8 # -:: date : 2012 # -:: licence : GPLv3.0 # -:: author : a-Sansara # -:: copyright : pluie.org # -:: # -:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # :: -:: This file is part of ImpraStorage. +:: software : ImpraStorage +:: version : 1.01 +:: date : 2014 +:: licence : GPLv3.0 +:: author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +:: copyright : pluie.org :: -:: ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # :: -:: ImpraStorage 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. +:: This file is part of ImpraStorage. +:: +:: ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +:: +:: ImpraStorage 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 ImpraStorage. If not, see . :: -:: You should have received a copy of the GNU General Public License -:: along with ImpraStorage. If not, see . @echo off TITLE ImpraStorage (GNU GPLv3) set cmd='' diff --git a/impra-cli.py b/impra-cli.py new file mode 100755 index 0000000..3149b4f --- /dev/null +++ b/impra-cli.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra-cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +from psr.sys import Sys, Const +from impra.cli import Cli + +def main(): + try: + c = 0 + Cli('.'+Sys.sep) + except Exception as e : + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/impra.py b/impra.py new file mode 100755 index 0000000..3d20cfc --- /dev/null +++ b/impra.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +from psr.sys import Sys, Const +from impra.gui import AppGui + +def main(): + try: + c = 0 + AppGui() + except Exception as e: + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/impra/__init__.py b/impra/__init__.py index e69de29..8b13789 100755 --- a/impra/__init__.py +++ b/impra/__init__.py @@ -0,0 +1 @@ + diff --git a/impra/app.py b/impra/app.py new file mode 100755 index 0000000..3fd84e4 --- /dev/null +++ b/impra/app.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- +# impra/app.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module app ~~ + +from psr.sys import Sys, Io, Const, init +from psr.log import Log +from psr.mproc import Manager +from kirmah.ui import IdleObject +from impra import conf +from impra.core import ImpraStorage, ImpraConf +from psr.imap import BadHostException, BadLoginException +from gi.repository.GObject import threads_init, GObject, idle_add, SIGNAL_RUN_LAST, TYPE_NONE, TYPE_STRING, TYPE_INT, TYPE_FLOAT, TYPE_BOOLEAN +from gi.repository.Gtk import STOCK_MEDIA_PAUSE, STOCK_NEW, STOCK_SAVE, STOCK_REFRESH, STOCK_REMOVE, STOCK_PROPERTIES, STOCK_EDIT, STOCK_DIALOG_INFO, STOCK_CLOSE +from threading import Thread, current_thread, enumerate as thread_enum + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class CliThread ~~ + +class ImpraThread(Thread, IdleObject): + """""" + __gsignals__ = { # signal type signal return signal args + 'completed' : ( SIGNAL_RUN_LAST , TYPE_NONE , ()), + 'interrupted' : ( SIGNAL_RUN_LAST , TYPE_NONE , ()), + 'progress' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_FLOAT,)), + 'fileget' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN, TYPE_STRING )), + 'fileremoved' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN, TYPE_STRING )), + 'fileadded' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN, TYPE_STRING)), + 'fileinfos' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN,)), + 'fileedited' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN,)), + 'indexrefreshed' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN,)), + 'needconfig' : ( SIGNAL_RUN_LAST , TYPE_NONE , ()), + 'filesearch' : ( SIGNAL_RUN_LAST , TYPE_NONE , ()), + 'fileaddretry' : ( SIGNAL_RUN_LAST , TYPE_NONE , (TYPE_BOOLEAN, TYPE_STRING)), + 'hasaddretry' : ( SIGNAL_RUN_LAST , TYPE_NONE , ()) + } + + TASK_WAIT = 0 + TASK_ADD = 1 + TASK_GET = 2 + TASK_REFRESH = 3 + TASK_REMOVE = 4 + TASK_INFOS = 5 + TASK_EDIT = 6 + TASK_ADD_RETRY = 7 + TASK_EXIT = 8 + + K_SIGNAL_NAME = 0 + K_SIGNAL_BIND = 1 + K_SIGNAL_DATA = 2 + + TASK_LABEL = ['wait','add','get','refresh','remove','infos','edit','add-retry','exit'] + TASK_STOCK = [STOCK_MEDIA_PAUSE, STOCK_NEW, STOCK_SAVE, STOCK_REFRESH, STOCK_REMOVE, STOCK_PROPERTIES, STOCK_EDIT, STOCK_DIALOG_INFO, STOCK_CLOSE] + + @Log(Const.LOG_DEBUG) + def __init__(self, evtStop, taskQueue, condition, conf, name='ImpraThread', instce=None): + Thread.__init__(self) + IdleObject.__init__(self) + self.setName(name) + self.cancelled = False + self.evtStop = evtStop + self.taskQueue = taskQueue + self.condition = condition + self.conf = conf + self.impst = instce + print(' INIT THREAD '+name) + print(self.impst) + + @Log(Const.LOG_DEBUG) + def connect_signals(self, signals, usrData=None): + """""" + for signal in signals: + self.connect(signal[self.K_SIGNAL_NAME], signal[self.K_SIGNAL_BIND], signal[self.K_SIGNAL_DATA] if len(signal)>self.K_SIGNAL_DATA else usrData) + + + @Log(Const.LOG_DEBUG) + def run(self): + """""" + self.cancelled = False + self.evtStop.clear() + Sys.g.THREAD_CLI = self + Sys.g.GUI = True + Sys.g.GUI_PRINT_STDOUT = True + done = False + self.can_retry = True + init(conf.PRG_NAME, Sys.g.DEBUG, loglvl=Const.LOG_APP) + try : + if self.impst is None : + + label = ' [[ INIT IMPRASTORAGE ]] ' + label = ' '+'~'*((Const.LINE_SEP_LEN-len(label))//2-1)+label+'~'*((Const.LINE_SEP_LEN-len(label))//2-1)+' ' + Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0 , True), + (label.ljust(Const.LINE_SEP_LEN, ' ') , Const.CLZ_INIT , True), + (Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0 , True)]) + + self.impst = ImpraStorage(self.conf) + + done = True + except Exception as e : + self.emit('needconfig') + raise e + self.emit('indexrefreshed', done) + if done : + while not self.evtStop.is_set() or not self.cancelled: + with self.condition : + self.condition.wait_for(lambda : not self.taskQueue.empty(), 2) + + if self.can_retry and self.impst.hasBackupAddMap(): + self.emit('hasaddretry') + self.can_retry = False + + if not self.taskQueue.empty(): + task, params, idtask = self.taskQueue.get_nowait() + label = ' [[ TASK '+str(idtask)+' : '+self.TASK_LABEL[task].upper()+' ]] ' + label = ' '+'>'*((Const.LINE_SEP_LEN-len(label))//2-1)+label+'<'*((Const.LINE_SEP_LEN-len(label))//2-1)+' ' + Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0 , True), + (label.ljust(Const.LINE_SEP_LEN, ' ') , Const.CLZ_ACTION, True), + (Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0 , True)]) + + try: + if task is self.TASK_WAIT : + print('wait') + Sys.sleep(params) + elif task is self.TASK_GET: + #~ mg = Manager(self.taskGet, 1, None, Sys.g.MPEVENT, uid=params) + #~ mg.run() + #~ self.emit('fileget', True, '') + #~ Thread(target=self.taskGet, name='impra-1', kwargs={'uid':params}).start() + print(params) + self.taskGet(params) + elif task is self.TASK_ADD: + self.taskAdd(params) + elif task is self.TASK_ADD_RETRY: + self.taskAddRetry(params) + elif task is self.TASK_REFRESH: + self.taskRefresh(params) + elif task is self.TASK_INFOS: + self.taskInfos(params) + elif task is self.TASK_REMOVE: + self.taskRemove(params) + elif task is self.TASK_EDIT: + self.taskEdit(params) + self.taskQueue.task_done() + + except self.impst.SocketError() as e : + Sys.pwarn((('ImpraThread.run : ',(str(e),Sys.CLZ_WARN_PARAM), ' !'),)) + self.impst.reconnect() + + except Exception as e: + print(type(e)) + Sys.pwarn((('ImpraThread.run : ',(str(e),Sys.CLZ_WARN_PARAM), ' !'),)) + else : + """""" + Sys.sleep(0.5) + self.emit('completed') + + + def taskGet(self, uid): + """""" + done, p = self.impst.getFile(uid) + self.emit('fileget', done, p) + + + def taskAdd(self, params): + """""" + fromPath, label, catg = params + done, p = self.impst.addFile(fromPath, label, catg) + self.emit('fileadded', done, p) + self.emit('indexrefreshed', done) + + + def taskAddRetry(self, params=None): + done, p = self.impst.sendFile(self.impst.getBackupAddMap(), True) + self.emit('fileadded', done, p) + self.emit('indexrefreshed', done) + self.can_retry = True + + + def taskRefresh(self, noparam=None): + """""" + self.emit('progress',10) + self.impst.idxu.get(True) + self.emit('indexrefreshed', True) + + + def taskInfos(self, uid): + """""" + self.impst.getInfo(uid) + self.emit('fileinfos', True) + + + def taskRemove(self, uid): + """""" + done, p = self.impst.removeFile(uid) + self.emit('fileremoved', done, p) + self.emit('indexrefreshed', done) + + + def taskEdit(self, params): + """""" + key, label, catg = params + done = self.impst.editFile(key, label, catg) + self.emit('fileedited', done) + self.emit('indexrefreshed', done) + + + @Log(Const.LOG_NEVER) + def progress(self, value): + """""" + #~ print('progress '+str(value)) + self.emit('progress', value) + + + @Log(Const.LOG_NEVER) + def cancel(self): + """ + Threads in python are not cancellable, so we implement our own + cancellation logic + """ + self.cancelled = True + self.evtStop.set() + + + @Log(Const.LOG_NEVER) + def stop(self): + """""" + if self.isAlive(): + self.cancel() + if current_thread().getName()==self.getName(): + try: + self.emit('interrupted') + Sys.thread_exit() + except RuntimeError as e : + print(str(self.getName()) + ' COULD NOT BE TERMINATED') + raise e + diff --git a/impra/cli.py b/impra/cli.py old mode 100644 new mode 100755 index 6c4115a..e8e8b89 --- a/impra/cli.py +++ b/impra/cli.py @@ -1,1221 +1,765 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# This file is part of ImpraStorage. +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org # -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# ImpraStorage 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. +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . # -# You should have received a copy of the GNU General Public License -# along with ImpraStorage. If not, see . # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ package cli ~~ - -from optparse import OptionParser, OptionGroup -import sys, os, platform -import impra.crypt as crypt -import impra.util as util -import impra.core as core -from impra.util import Clz, mprint -from time import strftime +# ~~ module cli ~~ -LINE_SEP_LEN = 120 -LINE_SEP_CHAR = '―' -if not Clz.isUnix : LINE_SEP_CHAR = '-' -APP_TITLE = 'ImpraStorage' -APP_VERSION = '0.8' -APP_AUTHOR = 'a-Sansara' -APP_COPY = 'pluie.org' -APP_LICENSE = 'GNU GPLv3' -APP_DESC = """ - ImpraStorage provided a private imap access to store large files. Each file stored on the server is split - in severals random parts. Each part also contains random noise data (lenght depends on a crypt key) to - ensure privacy and exclude easy merge without the corresponding key. - - An index of files stored is encrypt (with the symmetric-key algorithm Kirmah) and regularly updated. Once - decrypt, it permit to perform search on the server and download each part. - - Transfert process is transparent. Just vizualize locally the index of stored files and simply select files - to download or upload. ImpraStorage automatically launch the parts to download, then merge parts in the - appropriate way to rebuild the original file. Inversely, a file to upload is split (in several parts with - addition of noise data), and ImpraStorage randomly upload each parts then update the index. - -""" - -def printLineSep(sep,lenSep): - """""" - Clz.print(sep*lenSep, Clz.fgN0) -def printHeaderTitle(title): - """""" - Clz.print(' == '+title+' == ', Clz.BG4+Clz.fgB7, False, True) - -def printHeaderPart(label,value): - """""" - Clz.print(' [' , Clz.fgB0, False) - Clz.print(label, Clz.fgB3, False) - Clz.print(':' , Clz.fgB0, False) - Clz.print(value, Clz.fgB4, False) - Clz.print('] ' , Clz.fgB0, False) - - - -class _OptionParser(OptionParser): - """A simplified OptionParser""" - - def format_description(self, formatter): - return self.description - - def format_epilog(self, formatter): - return self.epilog +from optparse import OptionGroup +from psr.sys import Sys, Io, Const, init +from psr.log import Log +from psr.cli import AbstractCli +import impra.conf as conf +from impra.cliapp import CliApp # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~ class Cli ~~ -class Cli: - - def __init__(self,path): +class Cli(AbstractCli): - self.ini = util.IniFile(path+'impra.ini') - parser = _OptionParser() - parser.print_help = self.print_help - parser.print_usage = self.print_usage - self.wkpath = path+core.sep+'wk'+core.sep - gpData = OptionGroup(parser, '') - gpConf = OptionGroup(parser, '') + def __init__(self, path, remote=False, rwargs=None, thread=None, loglvl=Const.LOG_ALL): + """""" + AbstractCli.__init__(self, conf, self) - # metavar=' ', nargs=2 - parser.add_option('-q', '--quiet' , help='don\'t print status messages to stdout' , action='store_true', default=False) - parser.add_option('-d', '--debug' , help='set debug mode' , action='store_true' , default=False) - parser.add_option('--no-color' , help='disable color mode' , action='store_true' , default=False) + Cli.HOME = conf.DEFVAL_USER_PATH + Cli.DIRKEY = Cli.HOME+'.'+conf.PRG_NAME.lower()+Sys.sep + if not Sys.isUnix() : + Cli.CHQ = '"' + Cli.HOME = 'C:'+Sys.sep+conf.PRG_NAME.lower()+Sys.sep + Cli.DIRKEY = self.HOME+'keys'+Sys.sep + Sys.mkdir_p(Cli.DIRKEY) + + gpData = OptionGroup(self.parser, '') + gpData = OptionGroup(self.parser, '') + gpConf = OptionGroup(self.parser, '') - gpData.add_option('-c', '--category' , help='set specified CATEGORY (crit. for opt. -l,-a or -s)' , action='store', metavar='CATG ') - gpData.add_option('-u', '--user' , help='set specified USER (crit. for opt. -l,-a or -s)' , action='store', metavar='OWNER ') - gpData.add_option('-l', '--label' , help='set specified LABEL (edit mode only)' , action='store', metavar='LABEL ') - gpData.add_option('-o', '--order' , help='set colon ORDER (crit. for opt. -l and -s)' , action='store', metavar='ORDER ' , default='ID') - gpData.add_option('-O', '--order-inv' , help='set colon ORDER_INVERSE (crit. for opt. -l and -s)' , action='store', metavar='ORDER_INVERSE ') - #gpData.add_option('-o', '--output-dir' , help='set specified OUTPUT DIR (for opt. -l,-a,-d or -g)' , action='store', metavar='DIR ') - parser.add_option_group(gpData) + gpData.add_option('-c', '--category' , action='store', metavar='CATG ') + gpData.add_option('-u', '--user' , action='store', metavar='OWNER ') + gpData.add_option('-l', '--label' , action='store', metavar='LABEL ') + gpData.add_option('-o', '--order' , action='store', metavar='ORDER ' , default='ID') + gpData.add_option('-O', '--order-inv' , action='store', metavar='ORDER_INVERSE ') + gpData.add_option('-a', '--account' , action='store', metavar='ACCOUNT ') + self.parser.add_option_group(gpData) - gpConf.add_option('-V', '--view' , help='view configuration' , action='store' ) - gpConf.add_option('-L', '--load' , help='load configuration' , action='store' ) - gpConf.add_option('-S', '--save' , help='save configuration' , action='store' ) - gpConf.add_option('-C', '--check' , help='check configuration' , action='store' ) - gpConf.add_option('-H', '--set-host' , help='set imap host server' , action='store', metavar='HOST ') - gpConf.add_option('-U', '--set-user' , help='set imap user login' , action='store', metavar='USER ') - gpConf.add_option('-X', '--set-pass' , help='set imap user password' , action='store', metavar='PASS ') - gpConf.add_option('-P', '--set-port' , help='set imap port' , action='store', metavar='PORT ') - gpConf.add_option('-N', '--set-name' , help='set user name' , action='store', metavar='NAME ') - gpConf.add_option('-M', '--set-multi' , help='set multi account' , action='store', metavar='PROFILE ') - gpConf.add_option('-R', '--remove-multi' , help='remove multi account' , action='store', metavar='PROFILE ') - gpConf.add_option('-B', '--set-boxname' , help='set boxName on imap server (default:[%default])' , action='store', metavar='BOXNAME ') - gpConf.add_option('-K', '--gen-key' , help='generate new key' , action='store_true', default=False) + gpConf.add_option('-V', '--view' , action='store' ) + gpConf.add_option('-L', '--load' , action='store' ) + gpConf.add_option('-S', '--save' , action='store' ) + gpConf.add_option('-C', '--check' , action='store' ) + gpConf.add_option('-H', '--set-host' , action='store', metavar='HOST ') + gpConf.add_option('-U', '--set-user' , action='store', metavar='USER ') + gpConf.add_option('-X', '--set-pass' , action='store', metavar='PASS ') + gpConf.add_option('-P', '--set-port' , action='store', metavar='PORT ') + gpConf.add_option('-N', '--set-name' , action='store', metavar='NAME ') + gpConf.add_option('-M', '--set-multi' , action='store', metavar='PROFILE ') + gpConf.add_option('-R', '--remove-multi' , action='store', metavar='PROFILE ') + gpConf.add_option('-B', '--set-boxname' , action='store', metavar='BOXNAME ') + gpConf.add_option('-K', '--gen-key' , action='store_true', default=False) + self.parser.add_option_group(gpConf) - parser.add_option_group(gpConf) + # rewrite argv sended by remote + if rwargs is not None : + import sys + sys.argv = rwargs + + (o, a) = self.parser.parse_args() + + Sys.g.QUIET = o.quiet + Sys.g.THREAD_CLI = thread + Sys.g.GUI = thread is not None + + init(conf.PRG_NAME, o.debug, remote, not o.no_color, loglvl) + Const.LINE_SEP_LEN = 120 - (o, a) = parser.parse_args() - - if o.no_color : - util.Clz.active = False - - if o.quiet : - util.DEBUG.active = False - else: - util.DEBUG.active = True - if o.debug : - util.DEBUG.level = util.DEBUG.ALL - else : - util.DEBUG.level = util.DEBUG.INFO - - mprint() if not a: - try : - if not o.help : - self.parserError(' no commando specified') + if not o.help or not o.version: + self.parser.error_cmd(('no command specified',), True) else : - if util.DEBUG.active : core.clear() - parser.print_help() + Sys.clear() + Cli.print_help() except : - self.parserError(' no commando specified') + if not o.version : + self.parser.error_cmd(('no command specified',), True) + else : + Cli.print_header() else: + if a[0] == 'help': + Sys.clear() + Cli.print_help() - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # ~~ conf CMD ~~ - if a[0] == 'conf' : - if util.DEBUG.active : core.clear() - if o.load is not None or o.view is not None or o.save is not None : + elif a[0] in ['add','conf','import','info', 'edit','export','get','list','remove','search'] : + + app = CliApp(self.HOME, path, self.parser, Cli, a, o) + + if a[0]=='add': + app.onCommandAdd() + elif a[0]=='conf': + app.onCommandConf() + elif a[0]=='info': + app.onCommandInfo() + elif a[0]=='import': + app.onCommandImport() + elif a[0]=='edit': + app.onCommandEdit() + elif a[0]=='export': + app.onCommandExport() + elif a[0]=='get': + app.onCommandGet() + elif a[0]=='list': + app.onCommandList() + elif a[0]=='remove': + app.onCommandRemove() + elif a[0]=='search': + app.onCommandSearch() - if o.view is not None : - o.active_profile = o.view - if o.load is not None : - o.active_profile = o.load - if o.save is not None : - o.active_profile = o.save - - if o.active_profile==None: - if self.ini.has('profile') : o.active_profile = self.ini.get('profile') - else : o.active_profile = 'default' - - if o.load : - self.print_header() - self.load_profile(o) - - elif o.view : - self.print_header() - if o.view == 'all' : - sections = self.ini.getSections() - if len(sections) > 0: - ap = self.ini.get('profile') - sep = '' - for p in sections: - if p == ap : - colr = Clz.fgB1 - p = '*'+p - else : colr = Clz.fgB3 - Clz.print(sep+p, colr, False) - if sep=='':sep=',' - mprint() - else : Clz.print(' no profiles', Clz.fgB1) - else: self.ini.print(o.view) - - elif o.save : - self.print_header() - if not o.set_host and not o.set_user and not o.set_pass and not o.set_port and not o.set_boxname and not o.set_name and not o.gen_key and not o.set_multi and not o.remove_multi: - parser.error(' no options specified') - else : - if o.set_port and not util.represents_int(o.set_port): - parser.error(' port must be a number') - self.exit(1) - else : - if o.set_boxname: self.ini.set('box' , o.set_boxname,o.active_profile+'.imap') - if o.set_host : self.ini.set('host' , o.set_host,o.active_profile+'.imap') - if o.set_user : self.ini.set('user' , o.set_user,o.active_profile+'.imap') - if o.set_pass : self.ini.set('pass' , o.set_pass,o.active_profile+'.imap') - if o.set_port : self.ini.set('port' , o.set_port,o.active_profile+'.imap') - if o.set_name : self.ini.set('name' , o.set_name,o.active_profile+'.infos') - - m = self.ini.get('multi',o.active_profile+'.imap') - if m is None : m = [] - else : m = m.split(',') - m = [x for x in m if x] - if o.set_multi : - if self.check_imap(o.set_multi): - if not o.set_multi in m :m.append(o.set_multi) - else: - mprint() - Clz.print('imap profile '+o.set_multi+' not found', Clz.fgB1) - mprint() - elif o.remove_multi and o.remove_multi in m : m.remove(o.remove_multi) - self.ini.set('multi', ','.join(m), o.active_profile+'.imap') - - if o.gen_key: - kg = crypt.KeyGen(256) - self.ini.set('key' ,kg.key,o.active_profile+'.keys') - self.ini.set('mark',kg.mark,o.active_profile+'.keys') - self.ini.set('salt','-¤-ImpraStorage-¤-',o.active_profile+'.keys') - if self.check_profile(o.active_profile): - self.ini.set('profile', o.active_profile) - self.ini.write() - self.ini.print(o.active_profile) - elif o.check : - self.print_header() - self.check_profile(o.check, True) - - else : - self.print_usage('') - - elif a[0] == 'help': - if util.DEBUG.active : core.clear() - parser.print_help() - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # ~~ data CMD ~~ - elif not self.ini.isEmpty() and (a[0] == 'list' or a[0] == 'add' or a[0] == 'get' or a[0] == 'remove' or a[0] == 'search' or a[0] == 'edit' or a[0] == 'export' or a[0] == 'import'): - - o.active_profile = self.ini.get('profile') - - - if self.check_profile(o.active_profile): - if util.DEBUG.active : core.clear() - if util.DEBUG.active: self.print_header() - conf = core.ImpraConf(self.ini,o.active_profile) - impst = None - try: - impst = core.ImpraStorage(conf, False, self.wkpath) - except crypt.BadKeyException as e : - mprint() - Clz.print(' it seems that your current profile `' , Clz.fgB1, False) - Clz.print(o.active_profile , Clz.fgB3, False) - Clz.print('` (account:`' , Clz.fgB1, False) - Clz.print(conf.get('user','imap') , Clz.fgB3, False) - Clz.print('`) has a wrong key to decrypt index on server.' , Clz.fgB1) - Clz.print(' you can remove index but all presents files`' , Clz.fgB1, False) - Clz.print('` will be unrecoverable\n' , Clz.fgB1, True, False) - - resp = input(' backup index ? (yes/no) ') - if resp.lower()=='yes': - encData = util.get_file_content(core.dirname(conf.ini.path)+core.sep+'.index') - ipath = core.dirname(conf.ini.path)+core.sep+strftime('%Y%m%d%H%M%S')+'-'+o.active_profile+'.index' - with open(ipath, mode='w', encoding='utf-8') as o: - o.write(encData) - Clz.print('\nindex backup in `',Clz.fgn7, False) - Clz.print(ipath,Clz.fgB3, False) - Clz.print('`',Clz.fgn7) - Clz.print('\n',Clz.fgB1, True, False) - resp = input(' remove index ? (yes/no) ') - if resp.lower()=='yes': - impst = core.ImpraStorage(conf, True, self.wkpath) - mprint() - mprint(' bye') - Clz.print(' ',Clz.OFF) - mprint() - self.exit(1) - - else : - mprint() - mprint(' bye') - Clz.print(' ',Clz.OFF) - mprint() - self.exit(1) - - - if a[0]=='export': - if not impst.irefresh: impst.getIndex(True) - encData = impst.index.encrypt() - ipath = core.dirname(conf.ini.path)+core.sep+strftime('%Y%m%d%H%M%S')+'_'+o.active_profile+'.index' - with open(ipath, mode='w', encoding='utf-8') as o: - o.write(encData) - mprint() - Clz.print(' index saved as ', Clz.fgn7) - Clz.print(' '+ipath, Clz.fgB2) - mprint() - - if a[0]=='import': - if not len(a)>1 : self.error_cmd('`'+a[0]+' need one argument',parser) - else : - vfile = a[1] - if util.file_exists(vfile) : - encData = util.get_file_content(vfile) - try : - impst.importIndex(encData) - impst.saveIndex() - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - except crypt.BadKeyException as e: - mprint() - Clz.print(' Error : BadKeyException', Clz.fgB1) - Clz.print(' Your profile key don\'t match the index', Clz.fgB1) - mprint() - else : - mprint() - Clz.print(' file not found', Clz.fgB1) - mprint() - - elif a[0]=='list': - - uid = conf.get('uid' ,'index') - date = conf.get('date','index') - account = conf.get('user','imap') - if impst.index != None: - noData = impst.index.isEmpty() - if uid == None or noData : uid = 'EMPTY' - if date == None or noData : date = '' - if util.DEBUG.active : core.clear() - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - printHeaderTitle(APP_TITLE) - printHeaderPart('account',account) - printHeaderPart('index',uid) - printHeaderPart('box',impst.rootBox) - Clz.print(date, Clz.fgB7, True, True) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - - matchIdsCatg = None - matchIdsUser = None - matchIds = None - if o.category is not None : - mprint(o.category) - matchIdsCatg = impst.index.getByCategory(o.category) - if o.user is not None : - matchIdsUser = impst.index.getByUser(o.user) - - if o.category is not None : - if o.user is not None : - matchIds = impst.index.getIntersection(matchIdsCatg,matchIdsUser) - else : matchIds = matchIdsCatg - - elif o.user is not None : - matchIds = matchIdsUser - - order = o.order - if o.order_inv is not None: - order = '-'+o.order_inv - impst.index.print(order,matchIds) - - - elif a[0] == 'add': - if not len(a)>1 : self.error_cmd('`'+a[0]+' need at least one argument',parser) - else: - vfile = a[1] - if not util.file_exists(vfile) : - if os.path.isdir(vfile): - for f in os.listdir(vfile): - if not os.path.isdir(f): - label, ext = core.splitext(core.basename(f)) - if o.category is None : o.category = '' - done = impst.addFile(vfile+core.sep+f,label,o.category) - if done : - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - else : self.error_cmd('`'+a[1]+' is not a file or dir',parser) - else: - if not len(a)>2 : - label, ext = core.splitext(core.basename(vfile)) - else: label = a[2] - if o.category is None : o.category = '' - done = impst.addFile(vfile,label,o.category) - if done : - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - - - elif a[0] == 'edit': - if not len(a)>1 : self.error_cmd('`'+a[0]+' need an id',parser) - else: - if not util.represents_int(a[1]): - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(a[1] , Clz.bg1+Clz.fgB3, False) - Clz.print('` is not a valid id == ', Clz.bg1+Clz.fgB7) - mprint() - self.exit(1) - else : - vid = a[1] - key = impst.index.getById(vid) - if key !=None : - done = impst.editFile(key,o.label,o.category) - if done : - impst.saveIndex() - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - else : - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(a[1] , Clz.bg1+Clz.fgB3, False) - Clz.print('` has not been modified == ', Clz.bg1+Clz.fgB7) - mprint() - else: - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(a[1] , Clz.bg1+Clz.fgB3, False) - Clz.print('` is not a valid id == ', Clz.bg1+Clz.fgB7) - mprint() - - elif a[0] == 'get': - - if not len(a)>1 : self.error_cmd('`'+a[0]+' need an id',parser) - else: - vid = a[1] - ids = [] - for sid in vid.split(',') : - seq = sid.split('-') - if len(seq)==2 : ids.extend(range(int(seq[0]),int(seq[1])+1)) - else: ids.append(sid) - for sid in ids : - key = impst.index.getById(str(sid)) - if key !=None : - done = impst.getFile(key) - if done : - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - else: - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(str(sid) , Clz.bg1+Clz.fgB3, False) - Clz.print('` is not a valid id == ', Clz.bg1+Clz.fgB7) - mprint() - - - elif a[0] == 'search': - - uid = conf.get('uid' ,'index') - date = conf.get('date','index') - account = conf.get('user','imap') - - if not len(a)>1 : self.error_cmd('`'+a[0]+' need one argument',parser) - else : - - vsearch = a[1] - - matchIds = impst.index.getByPattern(vsearch) - if util.DEBUG.active : core.clear() - if matchIds is not None: - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - printHeaderTitle(APP_TITLE) - printHeaderPart('account',account) - printHeaderPart('index',uid) - printHeaderPart('box',impst.rootBox) - Clz.print(date, Clz.fgB7, True, True) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print(' searching --' , Clz.fgB3, False) - Clz.print(' `'+vsearch+'`' , Clz.fgB7, False) - Clz.print(' -- found ' , Clz.fgB3, False) - Clz.print(str(len(matchIds)), Clz.fgB1, False) - Clz.print(' results --' , Clz.fgB3) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - - matchIdsCatg = None - matchIdsUser = None - matchIdsCrit = None - if o.category is not None : - mprint(o.category) - matchIdsCatg = impst.index.getByCategory(o.category) - if o.user is not None : - matchIdsUser = impst.index.getByUser(o.user) - - if o.category is not None : - if o.user is not None : - matchIdsCrit = impst.index.getIntersection(matchIdsCatg,matchIdsUser) - else : matchIdsCrit = matchIdsCatg - - elif o.user is not None : - matchIdsCrit = matchIdsUser - - if matchIdsCrit is not None : - matchIds = impst.index.getIntersection(matchIds,matchIdsCrit) - - order = o.order - if o.order_inv is not None: - order = '-'+o.order_inv - impst.index.print(o.order,matchIds) - else: - mprint('\n ',end='') - Clz.print(' == no match found for pattern `', Clz.bg3+Clz.fgB4, False) - Clz.print(vsearch , Clz.bg3+Clz.fgB1, False) - Clz.print('` == ' , Clz.bg3+Clz.fgB4) - mprint() - - elif a[0] == 'remove': - - if not len(a)>1 : self.error_cmd('`'+a[0]+' need an id',parser) - else : - - if not util.represents_int(a[1]): - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(a[1] , Clz.bg1+Clz.fgB3, False) - Clz.print('` is not a valid id == ', Clz.bg1+Clz.fgB7) - mprint() - self.exit(1) - else : - vid = a[1] - key = impst.index.getById(vid) - if key !=None : - done = impst.removeFile(key) - if done : - mprint('\n ',end='') - Clz.print(' == OK == ', Clz.bg2+Clz.fgb7) - mprint() - else: - mprint('\n ',end='') - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(a[1] , Clz.bg1+Clz.fgB3, False) - Clz.print('` is not a valid id == ', Clz.bg1+Clz.fgB7) - mprint() - else : - self.check_profile(o.active_profile, True) + Sys.dprint('PUT END SIGNAL') + if Sys.g.LOG_QUEUE is not None : + Sys.g.LOG_QUEUE.put(Sys.g.SIGNAL_STOP) else : - if self.ini.isEmpty() : - Clz.print(' '*4+'ImpraStorage has no configuration file !!', Clz.fgB1) - mprint() - Clz.print(' '*8+'# to create the config file you must use this command with appropriate values :',Clz.fgn7) - Clz.print(' '*8+'# type command help for details',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('profileName ', Clz.fgB1, False) - Clz.print('-N ', Clz.fgB3, False) - Clz.print('yourName ', Clz.fgB1, False) - Clz.print('-K -H ', Clz.fgB3, False) - Clz.print('your.host.net ', Clz.fgB1, False) - Clz.print('-P ', Clz.fgB3, False) - Clz.print('993 ', Clz.fgB1, False) - Clz.print('-U ', Clz.fgB3, False) - Clz.print('accountName ', Clz.fgB1, False) - Clz.print('-X ', Clz.fgB3, False) - Clz.print('accountPassword ', Clz.fgB1) - else : - self.error_cmd('unknow command `'+a[0]+'`',parser) - mprint() - - - def error_cmd(self,msg, parser): - if util.DEBUG.active : core.clear() - self.print_usage('') - Clz.print('error : '+msg,Clz.fgB7) - self.exit(1) + self.parser.error_cmd((('unknow command ',(a[0],Sys.Clz.fgb3)),), True) - def parserError(self, msg): - if util.DEBUG.active : core.clear() - self.print_usage('') - Clz.print('error : '+msg,Clz.fgB7) - self.exit(1) + if not o.quiet : Sys.dprint() - def exit(self, code): - if Clz.isUnix : sys.exit(code) - def check_imap(self,profile): - return self.ini.has('host',profile+'.imap') and self.ini.has('user',profile+'.imap') and self.ini.has('pass',profile+'.imap') and self.ini.has('port',profile+'.imap') - - def check_profile(self,profile, activeCheck=False): + @staticmethod + def print_usage(data, withoutHeader=False): """""" - c = self.ini.hasSection(profile+'.keys') and self.check_imap(profile) and self.ini.has('name',profile+'.infos') - if activeCheck : - if c : - Clz.print(' '+profile+' is ok', Clz.fgB2) - Clz.print(' testing...', Clz.fgB3) - conf = core.ImpraConf(self.ini,profile) - impst = None - try: - impst = core.ImpraStorage(conf, False, self.wkpath) - Clz.print(' done...', Clz.fgB2) - except crypt.BadKeyException as e : - pass - else : - Clz.print(' profile `' , Clz.fgB1, False) - Clz.print(profile , Clz.fgB3, False) - Clz.print('` is incomplete\n need :', Clz.fgB1) - if not self.ini.hasSection(profile+'.keys'): - Clz.print(' '*4+'key'.ljust(18,' ')+' (conf -S "'+profile+'" -K)', Clz.fgB3) - if not self.ini.has('host',profile+'.imap'): - Clz.print(' '*4+'imap host'.ljust(18,' ')+' (conf -S "'+profile+'" -H hostName)', Clz.fgB3) - if not self.ini.has('user',profile+'.imap'): - Clz.print(' '*4+'imap user'.ljust(18,' ')+' (conf -S "'+profile+'" -U accountName)', Clz.fgB3) - if not self.ini.has('pass',profile+'.imap'): - Clz.print(' '*4+'imap password'.ljust(18,' ')+' (conf -S "'+profile+'" -X password)', Clz.fgB3) - if not self.ini.has('port',profile+'.imap'): - Clz.print(' '*4+'imap port'.ljust(18,' ')+' (conf -S "'+profile+'" -P port)', Clz.fgB3) - if not self.ini.has('name',profile+'.infos'): - if c : - Clz.print(' think to add your userName :',Clz.bgB3) - Clz.print(' '*4+'userName'.ljust(18,' ')+' (conf -S "'+profile+'" -N yourName)', Clz.fgB3) - return c + if not withoutHeader : Cli.print_header() - def print_header(self): - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - printHeaderTitle(APP_TITLE) - printHeaderPart('version',APP_VERSION) - printHeaderPart('author',APP_AUTHOR) - printHeaderPart('license',APP_LICENSE) - printHeaderPart('copyright','2012 '+APP_COPY) - Clz.print(' ', Clz.OFF) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - mprint() + Sys.echo(' USAGE :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('help ' , Sys.CLZ_HELP_CMD) - def print_version(self, data): - self.print_header() + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('add'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('filePath' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' [' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('name' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('category' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) - def print_usage(self, data, withoutHeader=False): - if not withoutHeader : self.print_header() - - Clz.print(' USAGE :\n', Clz.fgB3) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('help ', Clz.fgB3) - - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('add '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('filePath', Clz.fgB1, False) - Clz.print('} ', Clz.fgB1, False) - Clz.print('[', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('name', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('category', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(']', Clz.fgB3) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('edit'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('id' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' [ -l ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('label' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('category' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('edit '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('id', Clz.fgB1, False) - Clz.print('} ', Clz.fgB1, False) - Clz.print('[', Clz.fgB3, False) - Clz.print(' -l ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('label', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('category', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(']', Clz.fgB3) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('get'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('id|ids' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('get '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('id|ids', Clz.fgB1, False) - Clz.print('}', Clz.fgB1) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('list'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('[ -c ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('category' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('user' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('|' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('-O ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('colon' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -a ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('account' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('list '.ljust(8,' '), Clz.fgB3, False) - Clz.print('[', Clz.fgB3, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('category', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -u ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('user', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -o', Clz.fgB3, False) - Clz.print('|', Clz.fgB1, False) - Clz.print('-O ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('colon', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(']', Clz.fgB3) - - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('remove '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('id', Clz.fgB1, False) - Clz.print('}', Clz.fgB1) - - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('search '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('pattern', Clz.fgB1, False) - Clz.print('} ', Clz.fgB1, False) - Clz.print('[', Clz.fgB3, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('category', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -u ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('user', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -o', Clz.fgB3, False) - Clz.print('|', Clz.fgB1, False) - Clz.print('-O ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('colon', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(']', Clz.fgB3) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('remove'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('id' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('export '.ljust(8,' '), Clz.fgB3) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('info'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('id' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('import '.ljust(8,' '), Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('filePath', Clz.fgB1, False) - Clz.print('} ', Clz.fgB1) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('search'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('pattern' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' [ -c ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('category' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('user' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('|' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('-O ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('colon' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('conf '.ljust(8,' '), Clz.fgB3, False) - Clz.print('-L', Clz.fgB3, False) - Clz.print('|', Clz.fgB1, False) - Clz.print('-V', Clz.fgB3, False) - Clz.print('|', Clz.fgB1, False) - Clz.print('-C ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('profile', Clz.fgB1, False) - Clz.print('}', Clz.fgB1) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('export'.ljust(10,' ') , Sys.CLZ_HELP_CMD) - Clz.print(' imprastorage ', Clz.fgb7, False) - Clz.print('conf '.ljust(8,' '), Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('profile', Clz.fgB1, False) - Clz.print('} ', Clz.fgB1, False) - Clz.print('[', Clz.fgB3, False) - Clz.print(' -K', Clz.fgB3, False) - Clz.print(' -H ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('host', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -U ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('user', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -X ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('password', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -P ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('port', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -B ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('box', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -N ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('name', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' \\', Clz.fgB3) - - Clz.print(' '*40, Clz.fgb7, False) - Clz.print('-M ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('profile', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' -R ', Clz.fgB3, False) - Clz.print('{', Clz.fgB1, False) - Clz.print('profile', Clz.fgB1, False) - Clz.print('}', Clz.fgB1, False) - Clz.print(' ]', Clz.fgB3) - - def print_options(self): - mprint('\n') - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print(' MAIN OPTIONS :\n' , Clz.fgB3) - Clz.print(' '*4+'-h, --help' , Clz.fgB3) - Clz.print(' '*50+'display help message' , Clz.fgB7) - Clz.print(' '*4+'-q, --quiet' , Clz.fgB3) - Clz.print(' '*50+'don\'t print status messages to stdout' , Clz.fgB7) - Clz.print(' '*4+'-d, --debug' , Clz.fgB3) - Clz.print(' '*50+'set debug mode' , Clz.fgB7) - Clz.print(' '*4+' --no-color' , Clz.fgB3) - Clz.print(' '*50+'disable color mode' , Clz.fgB7) - mprint('\n') - - Clz.print(' COMMANDS OPTIONS :\n' , Clz.fgB3) - Clz.print(' '*4+'-c ' , Clz.fgB3, False) - Clz.print('CATEGORY'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --category '.ljust(18,' ') , Clz.fgB3, False) - Clz.print('CATEGORY'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set a category' , Clz.fgB7) - - Clz.print(' '*4+'-u ' , Clz.fgB3, False) - Clz.print('USER'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --user'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('USER'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set a user' , Clz.fgB7) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('import'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('filePath' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*4+'-l ' , Clz.fgB3, False) - Clz.print('LABEL'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --label'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('LABEL'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set a label (edit mode only)' , Clz.fgB7) - - Clz.print(' '*4+'-o ' , Clz.fgB3, False) - Clz.print('COLON'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --order'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('COLON'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'order by specified colon' , Clz.fgB7) - - Clz.print(' '*4+'-O ' , Clz.fgB3, False) - Clz.print('COLON'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --order-rev'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('COLON'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'reverse order by specified colon' , Clz.fgB7) - - mprint('\n') - Clz.print(' CONF OPTIONS :\n', Clz.fgB3) - Clz.print(' '*4+'-L ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --load'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'load the specified profile' , Clz.fgB7) - - Clz.print(' '*4+'-V ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --view'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'view the specified profile (or \'all\' for view availables)' , Clz.fgB7) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('conf'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('-L' , Sys.CLZ_HELP_ARG , False) + Sys.echo('|' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('-V' , Sys.CLZ_HELP_ARG, False) + Sys.echo('|' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('-C ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('profile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*4+'-C ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --check'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'check the specified profile' , Clz.fgB7) - - Clz.print(' '*4+'-S ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --save'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'save the specified profile' , Clz.fgB7) - - mprint('\n') - Clz.print(' CONF -S OPTIONS :\n', Clz.fgB3) - Clz.print(' '*4+'-N ' , Clz.fgB3, False) - Clz.print('NAME'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-name'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('NAME'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imprastorage username' , Clz.fgB7) - - Clz.print(' '*4+'-K ' , Clz.fgB3, False) - Clz.print(''.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --gen-key'.ljust(18,' ') , Clz.fgB3, False) - Clz.print(''.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'generate a new key' , Clz.fgB7) - - Clz.print(' '*4+'-H ' , Clz.fgB3, False) - Clz.print('HOST'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-host'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('HOST'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imap host' , Clz.fgB7) - - Clz.print(' '*4+'-U ' , Clz.fgB3, False) - Clz.print('USER'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-user'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('USER'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imap user' , Clz.fgB7) - - Clz.print(' '*4+'-X ' , Clz.fgB3, False) - Clz.print('PASSWORD'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-password'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('USER'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imap password' , Clz.fgB7) - - Clz.print(' '*4+'-P ' , Clz.fgB3, False) - Clz.print('PORT'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-port'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PORT'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imap port' , Clz.fgB7) - - Clz.print(' '*4+'-B ' , Clz.fgB3, False) - Clz.print('BOXNAME'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-box'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('BOXNAME'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'set imap boxname (default:__impra__)' , Clz.fgB7) - - Clz.print(' '*4+'-M ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --set-multi'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'add imap multi account' , Clz.fgB7) - - Clz.print(' '*4+'-R ' , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1, False) - Clz.print(', --remove-multi'.ljust(18,' ') , Clz.fgB3, False) - Clz.print('PROFILE'.ljust(10,' ') , Clz.fgB1) - Clz.print(' '*50+'remove imap multi account' , Clz.fgB7) - - mprint('\n') + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG , False) + Sys.echo('conf'.ljust(10,' ') , Sys.CLZ_HELP_CMD , False) + Sys.echo('-S ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('profile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' [ -K -H ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('host' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -U ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('user' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -X ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('password' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -P ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('port' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -B ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('box' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -N ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('name' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' \\' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*45 , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -M ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('profile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -R ' , Sys.CLZ_HELP_ARG , False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('profile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' ]' , Sys.CLZ_HELP_ARG) - def print_help(self): + @staticmethod + def print_options(): """""" - self.print_header() - Clz.print(APP_DESC, Clz.fgN1) - self.print_usage('',True) - self.print_options() - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - mprint() - Clz.print(' EXEMPLES :\n', Clz.fgB3) + Sys.dprint('\n') + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + Sys.echo(' MAIN OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-h'.ljust(13,' ')+', --help' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display help' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-q'.ljust(13,' ')+', --quiet' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'don\'t print status messages to stdout' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-v'.ljust(13,' ')+', --version' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display programm version' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-d'.ljust(13,' ')+', --debug' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable debug mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-f'.ljust(13,' ')+', --force' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'force rewriting existing files without alert' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+' '.ljust(13,' ')+', --no-color' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'disable color mode' , Sys.CLZ_HELP_ARG_INFO) + + + Sys.dprint('\n') + Sys.echo(' COMMANDS OPTIONS:\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('CATEGORY'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --category'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('LENGTH'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set a category' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('USER'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --user'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('USER'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set a user' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-l ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('LABEL'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --label'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('LABEL'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set a label (edit mode only)' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('COLON'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --order'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('COLON'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'order by specified colon' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-O ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('COLON'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --order-rev'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('COLON'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'reverse order by specified colon' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-a ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('ACCOUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --account'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('ACCOUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set an profile account' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' CONF OPTIONS:\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-L ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --load'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'load the specified profile' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-V ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --view'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'view the specified profile (or \'all\' for view availables)', Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-C ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --check'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'check the specified profile' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-S ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --save'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'save the specified profile' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' CONF -S OPTIONS:\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-N ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('NAME'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-name'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('NAME'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imprastorage username' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-K ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(''.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --gen-key'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo(''.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'generate a new key' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-H ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('HOST'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-host'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('HOST'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imap host' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-U ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('USER'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-user'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('USER'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imap user' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-X ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PASSWORD'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-password'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PASSWORD'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imap password' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-P ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PORT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-port'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PORT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imap port' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-B ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('BOXNAME'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-box'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('BOXNAME'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'set imap boxname (default:__impra2__)' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-P ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --set-multi'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'add imap multi account' , Sys.CLZ_HELP_ARG_INFO) + + Sys.echo(' '*4+'-R ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --remove-multi'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('PROFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'remove imap multi account' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + + + @staticmethod + def print_help(): + """""" + Cli.print_header() + Sys.echo(Cli.conf.PRG_DESC, Sys.CLZ_HELP_DESC) + Cli.print_usage('',True) + Cli.print_options() + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint() + Sys.echo(' EXEMPLES :\n', Sys.CLZ_HELP_CMD) CHQ = "'" - sep = core.sep - HOME = sep+'home'+sep - if not Clz.isUnix : - CHQ = '"' - HOME = 'C:'+sep - - Clz.print(' '*4+'command add :', Clz.fgB3) - - Clz.print(' '*8+'# add (upload) a file',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('add ', Clz.fgB3, False) - Clz.print(HOME+'Share'+sep+'2009-mountains.avi', Clz.fgB1) - - Clz.print(' '*8+'# add a file with a label (label will be the filename on downloading)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('add ', Clz.fgB3, False) - Clz.print(HOME+'Share'+sep+'2009-mountains.avi '+CHQ+'summer 2009 - in mountains'+CHQ, Clz.fgB1) - - Clz.print(' '*8+'# add a file on a category (category will be the dir structure on downloading)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('add ', Clz.fgB3, False) - Clz.print(HOME+'Share'+sep+'2009-mountains.avi', Clz.fgB1, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('videos/perso/2009', Clz.fgB1) - - Clz.print(' '*8+'# add a file with a label on a category',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('add ', Clz.fgB3, False) - Clz.print(HOME+'Share'+sep+'2009-mountains.avi '+CHQ+'summer 2009 - in mountains'+CHQ, Clz.fgB1, False) - Clz.print(' -c ', Clz.fgB3, False) - Clz.print('videos/perso/2009', Clz.fgB1) + + Sys.echo(' '*4+'command add :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# add (upload) a file', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('add ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'Share'+Sys.sep+'2009-mountains.avi', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# add a file with a label (label will be the filename on downloading)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('add ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'Share'+Sys.sep+'2009-mountains.avi', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ', Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso/2009', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# add a file with a label on a category', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('add ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'Share'+Sys.sep+'2009-mountains.avi '+CHQ+'summer 2009 - in mountains'+CHQ, Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ', Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso/2009', Sys.CLZ_HELP_PARAM) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command edit :', Clz.fgB3) - - Clz.print(' '*8+'# edit label on file with id 15',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('edit ', Clz.fgB3, False) - Clz.print('15', Clz.fgB1, False) - Clz.print(' -l ' , Clz.fgB3, False) - Clz.print('newName', Clz.fgB1) - Clz.print(' '*8+'# edit category on file with id 15',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('edit ', Clz.fgB3, False) - Clz.print('15', Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('new/category', Clz.fgB1) + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command edit :', Sys.CLZ_HELP_CMD) - Clz.print(' '*8+'# edit label and category on file with id 15',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('edit ', Clz.fgB3, False) - Clz.print('15', Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('new/category', Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print(CHQ+'my newName'+CHQ, Clz.fgB1) - - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command get :', Clz.fgB3) - - Clz.print(' '*8+'# get file with id 15',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('get ', Clz.fgB3, False) - Clz.print('15', Clz.fgB1) - - Clz.print(' '*8+'# get files with id 15,16,17,18,19',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('get ', Clz.fgB3, False) - Clz.print('15-19', Clz.fgB1) - - Clz.print(' '*8+'# get files with id 22,29,30',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('get ', Clz.fgB3, False) - Clz.print('22,29,30', Clz.fgB1) - - Clz.print(' '*8+'# get files with id 22,29,30,31,32,33,34,35,48',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('get ', Clz.fgB3, False) - Clz.print('22,29-35,48', Clz.fgB1) + Sys.echo(' '*8+'# edit label on file with id 15', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('edit ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -l ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('newname' , Sys.CLZ_HELP_PARAM) - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command list :', Clz.fgB3) + Sys.echo(' '*8+'# edit category on file with id 15', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('edit ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('new/category' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# list all files',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('list', Clz.fgB3) - - Clz.print(' '*8+'# list all files (sorted by LABEL)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('list', Clz.fgB3, False) - Clz.print(' -o ' , Clz.fgB3, False) - Clz.print('LABEL', Clz.fgB1) - - Clz.print(' '*8+'# list all files on category `videos/perso` ',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('list', Clz.fgB3, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('videos/perso', Clz.fgB1) - - Clz.print(' '*8+'# list all files sent by `bob`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('list', Clz.fgB3, False) - Clz.print(' -u ' , Clz.fgB3, False) - Clz.print('bob', Clz.fgB1) - - Clz.print(' '*8+'# list all files sent by `bob` on category `videos/perso` (reverse sorted by SIZE)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('list', Clz.fgB3, False) - Clz.print(' -O ' , Clz.fgB3, False) - Clz.print('SIZE', Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('videos/perso', Clz.fgB1, False) - Clz.print(' -u ' , Clz.fgB3, False) - Clz.print('bob', Clz.fgB1) - - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command remove :', Clz.fgB3) - - Clz.print(' '*8+'# remove file with id 15 (removing command only take a single id)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('remove ', Clz.fgB3, False) - Clz.print('15', Clz.fgB1) - - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command search :', Clz.fgB3) - - Clz.print(' '*8+'# search all files wich contains `mountains`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print('mountains', Clz.fgB1) - - Clz.print(' '*8+'# search all files wich contains `old mountain` on category `videos/perso`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'old mountain'+CHQ, Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('videos/perso', Clz.fgB1) - - Clz.print(' '*8+'# search all files wich contains `old mountain` sent by user `bob`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'old mountain'+CHQ, Clz.fgB1, False) - Clz.print(' -u ' , Clz.fgB3, False) - Clz.print('bob', Clz.fgB1) - - Clz.print(' '*8+'# search all files wich contains `old mountain` (reverse sorted by SIZE)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'old mountain'+CHQ, Clz.fgB1, False) - Clz.print(' -O ' , Clz.fgB3, False) - Clz.print('SIZE', Clz.fgB1) - - Clz.print(' '*8+'# search all files wich contains `old mountain` sent by user `bob` and on category `videos/perso` (reverse',Clz.fgn7) - Clz.print(' '*8+'# sorted by LABEL)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'old mountain'+CHQ, Clz.fgB1, False) - Clz.print(' -c ' , Clz.fgB3, False) - Clz.print('videos/perso', Clz.fgB1, False) - Clz.print(' -u ' , Clz.fgB3, False) - Clz.print('bob', Clz.fgB1, False) - Clz.print(' -O ' , Clz.fgB3, False) - Clz.print('LABEL' , Clz.fgB1) - - Clz.print(' '*8+'# search all files starting by `old mountain`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'^old mountain'+CHQ, Clz.fgB1) - - Clz.print(' '*8+'# search all files ending by `old mountain`',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('search ', Clz.fgB3, False) - Clz.print(CHQ+'old mountain$'+CHQ, Clz.fgB1) + Sys.echo(' '*8+'# edit label and category on file with id 15', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('edit ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('new/category' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -l ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(CHQ+'my newName'+CHQ , Sys.CLZ_HELP_PARAM) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command export :', Clz.fgB3) - - Clz.print(' '*8+'# export the current index (as an encrypt file)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('export ', Clz.fgB3) - - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command import :', Clz.fgB3) - - Clz.print(' '*8+'# import an index (build by export command)',Clz.fgn7) - Clz.print(' '*8+'# carreful with this command, current index will be unrecoverable',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('import ', Clz.fgB3, False) - Clz.print(' 20121010222244_gmail.index', Clz.fgB1) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - Clz.print('\n'+' '*4+'command conf :', Clz.fgB3) + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command get :', Sys.CLZ_HELP_CMD) - Clz.print(' '*8+'# this command is tipycally a profile creation (or rewrite if profile exists)',Clz.fgn7) - Clz.print(' '*8+'# set a userName, generate a new Key and set imap account with host,port,user,password for profile bobgmail',Clz.fgn7) - Clz.print(' '*8+'# then set it as current profile',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('bobgmail ', Clz.fgB1, False) - Clz.print('-N ', Clz.fgB3, False) - Clz.print('bob ', Clz.fgB1, False) - Clz.print('-K -H ', Clz.fgB3, False) - Clz.print('imap.gmail.com ', Clz.fgB1, False) - Clz.print('-P ', Clz.fgB3, False) - Clz.print('993 ', Clz.fgB1, False) - Clz.print('-U ', Clz.fgB3, False) - Clz.print('bob22 ', Clz.fgB1, False) - Clz.print('-X ', Clz.fgB3, False) - Clz.print('mypassword ', Clz.fgB1) + Sys.echo(' '*8+'# get file with id 15', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('get ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# check config profile bobimap (current profile doesn\'t change)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-C ', Clz.fgB3, False) - Clz.print('bobimap ', Clz.fgB1) + Sys.echo(' '*8+'# get files with id 15,16,17,18,19', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('get ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15-19' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# get files with id 22,29,30', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('get ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('22,29,30' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# load config profile bobimap and set it as current profile',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-L ', Clz.fgB3, False) - Clz.print('bobimap ', Clz.fgB1) + Sys.echo(' '*8+'# get files with id 22,29,30,31,32,33,34,35,48', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('get ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('22,29-35,48' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# list all config profile',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-V ', Clz.fgB3, False) - Clz.print('all ', Clz.fgB1) - Clz.print(' '*8+'# view config profile bobgmail (current profile doesn\'t change)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-V ', Clz.fgB3, False) - Clz.print('bobgmail ', Clz.fgB1) + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command list :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# list all files' , Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list ' , Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# list all files (sorted by LABEL)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('LABEL' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# list all files on category `videos/perso` ', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# generate a new Key for profile bobgmail and set it as current profile (carreful with this command ',Clz.fgn7) - Clz.print(' '*8+'# if your account has no empty index - all files will be unrecoverable without the appropriate key)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('bobgmail ', Clz.fgB1, False) - Clz.print('-K ', Clz.fgB3) + Sys.echo(' '*8+'# list all files sent by `Imran`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# add multi account to profile bobgmail (accounts must be on same host)',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('bobgmail ', Clz.fgB1, False) - Clz.print('-M ', Clz.fgB3, False) - Clz.print('bobimap', Clz.fgB1) + Sys.echo(' '*8+'# list all files sent by `Imran` on category `videos/perso` (reverse sorted by SIZE)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -O ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('SIZE' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# list all files sent by `Imran` on category `videos/perso` (reverse sorted by SIZE) and account imran22', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('list' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -O ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('SIZE' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -a ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imran22' , Sys.CLZ_HELP_PARAM) + + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command remove :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# remove file with id 15 (removing command only take a single id)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('remove ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM) + + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command info :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# get info about file with id 15', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('info ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('15' , Sys.CLZ_HELP_PARAM) + + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command search :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# search all files wich contains `mountains`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('mountains' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files wich contains `old mountain` on category `videos/perso`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'old mountain'+CHQ , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files wich contains `old mountain` sent by user `Imran`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'old mountain'+CHQ , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files wich contains `old mountain` (reverse sorted by SIZE)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'old mountain'+CHQ , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -O ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('SIZE' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files wich contains `old mountain` sent by user `Imran` and on category `videos/perso` (reverse', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# sorted by LABEL)' , Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'old mountain'+CHQ , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -c ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('videos/perso' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -u ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -O ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('LABEL' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files starting by `old mountain`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'^old mountain'+CHQ , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files starting by `old mountain`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'^old mountain'+CHQ , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# search all files ending by `old mountain`', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('search ' , Sys.CLZ_HELP_CMD, False) + Sys.echo(CHQ+'old mountain$'+CHQ , Sys.CLZ_HELP_PARAM) - Clz.print(' '*8+'# remove multi account to profile bobgmail',Clz.fgn7) - Clz.print(' '*8+'imprastorage ', Clz.fgB7, False) - Clz.print('conf ', Clz.fgB3, False) - Clz.print('-S ', Clz.fgB3, False) - Clz.print('bobgmail ', Clz.fgB1, False) - Clz.print('-R ', Clz.fgB3, False) - Clz.print('bobimap', Clz.fgB1) - - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - mprint() - def load_profile(self,o): - """""" - if self.check_profile(o.active_profile): - mprint('',end=' ') - Clz.print(' == profile `', Clz.bg2+Clz.fgb7, False) - Clz.print(o.active_profile, Clz.bg2+Clz.fgB3, False) - Clz.print('` loaded == ', Clz.bg2+Clz.fgb7) - mprint() - self.ini.set('profile', o.active_profile) - self.ini.write() - else : - self.check_profile(o.active_profile, True) + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command export :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# export the current index (as an encrypt file)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('export ' , Sys.CLZ_HELP_CMD) + + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command import :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# import an index (build by export command)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# carreful with this command, current index will be unrecoverable', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('import ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('20121010222244_gmail.index', Sys.CLZ_HELP_PARAM) + + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command conf :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# this command is tipycally a profile creation (or rewrite if profile exists)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# set a userName, generate a new Key and set imap account with host,port,user,password for profile imrangmail', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# then set it as current profile', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -S ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imrangmail' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -N ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('Imran' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -K -H ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imap.gmail.com' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -P ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('993' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -U ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imran22' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -X ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imranpassword' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# check config profile imranimap (current profile doesn\'t change)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -C ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imranimap' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# load config profile imranimap and set it as current profile', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -L ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imranimap' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# list all config profile', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -V ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('all' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# view config profile imrangmail (current profile doesn\'t change)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('-V ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imrangmail' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# generate a new Key for profile imrangmail and set it as current profile (carreful with this command ', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# if your account has no empty index - all files will be unrecoverable without the appropriate key)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -S ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imrangmail' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -K ' , Sys.CLZ_HELP_ARG) + + Sys.echo(' '*8+'# add multi account to profile imrangmail (accounts must be on same host)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -S ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imrangmail' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -M ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imranimap' , Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# remove multi account to profile imrangmail', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('conf' , Sys.CLZ_HELP_CMD, False) + Sys.echo(' -S ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imrangmail' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -R ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('imranimap' , Sys.CLZ_HELP_PARAM) + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint() diff --git a/impra/cliapp.py b/impra/cliapp.py new file mode 100755 index 0000000..a8ba68f --- /dev/null +++ b/impra/cliapp.py @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/cliapp.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module cliapp ~~ + +import impra.conf as conf +from impra.core import ImpraStorage, ImpraConf +from impra.ini import KiniFile +from kirmah.crypt import Kirmah, BadKeyException, KeyGen, represents_int, KeyGen, represents_int +from psr.sys import Sys, Const, Io +from psr.log import Log +from psr.imap import BadHostException, BadLoginException + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class CliApp ~~ + +class CliApp: + + @Log(Const.LOG_BUILD) + def __init__(self, home, path, parser, Cli, a, o): + """""" + self.parser = parser + self.Cli = Cli + self.a = a + self.o = o + self.home = home + self.stime = Sys.datetime.now() + self.account = 'default' + self.rootBox = '__impra__2' + self.uid = '0' + self.date = '' + if not Io.file_exists('impra2.ini'+Kirmah.EXT): + print('ini file not exist') + if len(self.a)>0 and self.a[0]=='conf' and self.o.save : + kg = KeyGen() + Io.set_data('impra2.ini.key', kg.key) + self.ini = KiniFile('impra2.ini') + self.ini.set('key' , kg.key , self.account+'.keys') + self.ini.set('mark', kg.mark , self.account+'.keys') + self.ini.set('salt', '-¤-ImpraStorage-¤-' , self.account+'.keys') + else : + self.needConfig() + + else : + if not (len(self.a)>0 and self.a[0]=='conf') : + self.ini = KiniFile('impra2.ini') + self.impst = ImpraStorage(ImpraConf(self.ini)) + self.uid = self.impst.idxu.conf.get('uid' ,'index') + self.date = self.impst.idxu.conf.get('date','index') + self.account = self.impst.idxu.conf.get('user','imap') + self.rootBox = self.impst.rootBox + if self.impst.idxu.index != None: + noData = self.impst.idxu.index.isEmpty() + if self.uid == None or noData : self.uid = 'EMPTY' + if self.date == None or noData : self.date = '' + else : + self.ini = KiniFile('impra2.ini') + + + @Log(Const.LOG_ALL) + def pheader(self): + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + self.Cli.printHeaderTitle(self.Cli.conf.PRG_CLI_NAME) + self.Cli.printHeaderPart('account', self.account) + self.Cli.printHeaderPart('index' , self.uid) + self.Cli.printHeaderPart('box' , self.rootBox) + Sys.echo(self.date, Sys.Clz.fgB7, True, True) + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + + @Log() + def getMatchKey(self): + key = None + if not len(self.a)>1 : + self.parser.error_cmd((a[0]+' command need an id',), True) + else: + if not represents_int(self.a[1]): + self.parser.error_cmd((('not a valid id : ',(self.a[1], Sys.CLZ_ERROR_PARAM)),), False) + else : + vid = self.a[1] + key = self.impst.idxu.index.getById(vid) + if key is None : + self.parser.error_cmd(((' not a valid id : ',(self.a[1], Sys.CLZ_ERROR_PARAM)),), False) + return key + + + @Log(Const.LOG_DEBUG) + def onCommandAdd(self): + """""" + if not len(self.a)>1 : + self.parser.error_cmd((self.a[0]+' command need one argument',), True) + else: + vfile = self.a[1] + if not Io.file_exists(vfile) : + if Sys.isdir(vfile): + for f in Sys.listdir(vfile): + if not Sys.isdir(f): + label, ext = Sys.getFileExt(Sys.basename(f)) + if self.o.category is None : self.o.category = '' + done = self.impst.addFile(vfile+Sys.sep+f, label , self.o.category) + if done : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + else : + self.parser.error_cmd((self.a[0]+' is not a file or a directory',), True) + else: + if not len(self.a)>2 : + label = Sys.basename(vfile) + else: label = self.a[2] + if self.o.category is None : self.o.category = '' + Sys.clear() + self.pheader() + done = self.impst.addFile(vfile,label,self.o.category) + if done : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + else : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == KO == ', Sys.Clz.bg1+Sys.Clz.fgb7) + Sys.dprint() + + + @Log(Const.LOG_DEBUG) + def needConfig(self): + Sys.clear() + self.pheader() + Sys.echo(' '*4+'ImpraStorage has no configuration file !!', Sys.Clz.fgB1) + Sys.dprint() + Sys.echo(' '*8+'# to create the config file you must use this command with appropriate values :',Sys.Clz.fgn7) + Sys.echo(' '*8+'# type command help for details',Sys.Clz.fgn7) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.Clz.fgB7, False) + Sys.echo('conf ', Sys.Clz.fgB3, False) + Sys.echo('-S ', Sys.Clz.fgB3, False) + Sys.echo('profileName ', Sys.Clz.fgB1, False) + Sys.echo('-N ', Sys.Clz.fgB3, False) + Sys.echo('yourName ', Sys.Clz.fgB1, False) + Sys.echo('-K -H ', Sys.Clz.fgB3, False) + Sys.echo('accountHost ', Sys.Clz.fgB1, False) + Sys.echo('-P ', Sys.Clz.fgB3, False) + Sys.echo('993 ', Sys.Clz.fgB1, False) + Sys.echo('-U ', Sys.Clz.fgB3, False) + Sys.echo('accountName ', Sys.Clz.fgB1, False) + Sys.echo('-X ', Sys.Clz.fgB3, False) + Sys.echo('accountPassword ', Sys.Clz.fgB1) + Sys.dprint() + Sys.exit(1) + + + @Log(Const.LOG_DEBUG) + def onCommandConf(self): + """""" + if self.o.load is not None or self.o.view is not None or self.o.save is not None : + + if self.o.view is not None : + self.o.active_profile = self.o.view + if self.o.load is not None : + self.o.active_profile = self.o.load + if self.o.save is not None : + self.o.active_profile = self.o.save + + if self.o.active_profile==None: + if self.ini.has('profile') : self.o.active_profile = self.ini.get('profile') + else : self.o.active_profile = 'default' + + if self.o.load : + self.pheader() + self.load_profile() + + elif self.o.view : + self.pheader() + if self.o.view == 'all' : + sections = self.ini.getSections() + if len(sections) > 0: + ap = self.ini.get('profile') + sep = '' + for p in sections: + if p == ap : + colr = Sys.Clz.fgB1 + p = '*'+p + else : colr = Sys.Clz.fgB3 + Sys.echo(sep+p, colr, False) + if sep=='':sep=',' + Sys.dprint() + else : Sys.echo(' no profiles', Sys.Clz.fgB1) + else: + print(self.ini.get('profile')) + self.ini.print(self.o.view) + + elif self.o.save : + self.pheader() + if not self.o.set_host and not self.o.set_user and not self.o.set_pass and not self.o.set_port and not self.o.set_boxname and not self.o.set_name and not self.o.gen_key and not self.o.set_multi and not self.o.remove_multi: + self.parser.error(' no options specified') + else : + if self.o.set_port and not represents_int(self.o.set_port): + self.parser.error(' port must be a number') + self.exit(1) + else : + if self.o.set_boxname: self.ini.set('box' , self.o.set_boxname, self.o.active_profile+'.imap') + if self.o.set_host : self.ini.set('host' , self.o.set_host , self.o.active_profile+'.imap') + if self.o.set_user : self.ini.set('user' , self.o.set_user , self.o.active_profile+'.imap') + if self.o.set_pass : self.ini.set('pass' , self.o.set_pass , self.o.active_profile+'.imap') + if self.o.set_port : self.ini.set('port' , self.o.set_port , self.o.active_profile+'.imap') + if self.o.set_name : self.ini.set('name' , self.o.set_name , self.o.active_profile+'.infos') + + if self.ini.has('multi',self.o.active_profile+'.imap'): + m = self.ini.get('multi',self.o.active_profile+'.imap') + else : m = None + if m is None : m = [] + else : m = m.split(',') + m = [x for x in m if x] + if self.o.set_multi : + if self.check_imap(self.o.set_multi): + if not self.o.set_multi in m :m.append(self.o.set_multi) + else: + Sys.dprint() + Sys.echo('imap profile '+self.o.set_multi+' not found', Sys.Clz.fgB1) + Sys.dprint() + elif self.o.remove_multi and self.o.remove_multi in m : m.remove(self.o.remove_multi) + self.ini.set('multi', ','.join(m), self.o.active_profile+'.imap') + + if self.o.gen_key: + kg = KeyGen() + self.ini.set('key' , kg.key , self.o.active_profile+'.keys') + self.ini.set('mark', kg.mark , self.o.active_profile+'.keys') + self.ini.set('salt', '-¤-ImpraStorage-¤-' , self.o.active_profile+'.keys') + if self.check_profile(self.o.active_profile): + self.ini.set('profile', self.o.active_profile) + if not self.o.set_boxname and not self.ini.has('box', self.o.active_profile+'.imap') : + self.ini.set('box' , self.rootBox, self.o.active_profile+'.imap') + self.ini.save() + self.ini.print(self.o.active_profile) + + elif self.o.check : + self.pheader() + self.check_profile(self.o.check, True) + + else : + self.parser.print_usage('') + + + @Log(Const.LOG_DEBUG) + def check_imap(self, profile): + """""" + return self.ini.has('host',profile+'.imap') and self.ini.has('user',profile+'.imap') and self.ini.has('pass',profile+'.imap') and self.ini.has('port',profile+'.imap') + + + @Log(Const.LOG_DEBUG) + def check_profile(self, profile, activeCheck=False): + """""" + c = self.ini.hasSection(profile+'.keys') and self.check_imap(profile) and self.ini.has('name',profile+'.infos') + if activeCheck : + if c : + Sys.echo(' '+profile+' is ok', Sys.Clz.fgB2) + Sys.echo(' testing...' , Sys.Clz.fgB3) + kini = self.ini + conf = ImpraConf(kini, profile) + impst = None + try: + impst = ImpraStorage(self.ini.path, False) + Sys.echo(' done', Sys.Clz.fgB2) + except BadHostException as e : + Sys.echo(' fail ! bad host or port !', Sys.Clz.fgB1) + pass + + except BadLoginException as e: + Sys.echo(str(e) , Sys.Clz.fgN1) + Sys.echo(' fail ! bad login or password !' , Sys.Clz.fgB1) + pass + except Exception as e: + Sys.echo(' fail ! check your configuration !' , Sys.Clz.fgB1) + pass + + else : + Sys.echo(' profile `' , Sys.Clz.fgB1, False) + Sys.echo(profile , Sys.Clz.fgB3, False) + Sys.echo('` is incomplete\n need :', Sys.Clz.fgB1) + if not self.ini.hasSection(profile+'.keys'): + Sys.echo(' '*4+'key'.ljust(18,' ')+' (conf -S "'+profile+'" -K)', Sys.Clz.fgB3) + if not self.ini.has('host',profile+'.imap'): + Sys.echo(' '*4+'imap host'.ljust(18,' ')+' (conf -S "'+profile+'" -H hostName)', Sys.Clz.fgB3) + if not self.ini.has('user',profile+'.imap'): + Sys.echo(' '*4+'imap user'.ljust(18,' ')+' (conf -S "'+profile+'" -U accountName)', Sys.Clz.fgB3) + if not self.ini.has('pass',profile+'.imap'): + Sys.echo(' '*4+'imap password'.ljust(18,' ')+' (conf -S "'+profile+'" -X password)', Sys.Clz.fgB3) + if not self.ini.has('port',profile+'.imap'): + Sys.echo(' '*4+'imap port'.ljust(18,' ')+' (conf -S "'+profile+'" -P port)', Sys.Clz.fgB3) + if not self.ini.has('name',profile+'.infos'): + if c : + Sys.echo(' think to add your userName :',Sys.Clz.bgB3) + Sys.echo(' '*4+'userName'.ljust(18,' ')+' (conf -S "'+profile+'" -N yourName)', Sys.Clz.fgB3) + return c + + + @Log(Const.LOG_DEBUG) + def load_profile(self): + """""" + if self.check_profile(self.o.active_profile): + Sys.dprint(' ',end=' ') + Sys.echo(' == profile `' , Sys.Clz.bg2+Sys.Clz.fgb7, False) + Sys.echo(self.o.active_profile, Sys.Clz.bg2+Sys.Clz.fgB3, False) + Sys.echo('` loaded == ' , Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + self.ini.set('profile', self.o.active_profile) + self.ini.save() + else : + self.check_profile(self.o.active_profile, True) + + + @Log(Const.LOG_DEBUG) + def onCommandImport(self): + """""" + print('cmd import') + self.impst.sendFile(self.impst.getBackupAddMap(), True) + + + @Log(Const.LOG_DEBUG) + def onCommandEdit(self): + """""" + key = self.getMatchKey() + if key is not None : + if self.o.label is not None or self.o.category is not None : + if self.impst.editFile(key, self.o.label, self.o.category) : + Sys.clear() + self.pheader() + self.impst.idxu.index.print('ID', [int(self.a[1])]) + Sys.dprint('\n ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + else : + self.parser.error_cmd((('id ', (self.a[1], Sys.CLZ_ERROR_PARAM), ' has not been modified '),), False) + else : + self.parser.error_cmd(((' command edit need a label or a category '),), True) + + + @Log(Const.LOG_DEBUG) + def onCommandExport(self): + """""" + Sys.clear() + self.pheader() + from time import strftime + name = strftime('%Y%m%d%H%M%S')+'_'+self.impst.idxu.conf.profile + Sys.echo(' saving ', Sys.Clz.fgn7, False) + Sys.echo(name+'.index'+Kirmah.EXT, Sys.Clz.fgB2) + Io.copy(self.impst.idxu.pathIdx, name+'.index'+Kirmah.EXT) + Sys.echo(' saving ', Sys.Clz.fgn7, False) + Sys.echo(name+'.ini'+Kirmah.EXT, Sys.Clz.fgB2) + self.impst.idxu.conf.ini.save(name+'.ini', True) + Sys.echo(' saving ', Sys.Clz.fgn7, False) + Sys.echo(name+'.key', Sys.Clz.fgB2) + Io.set_data(name+'.key', self.impst.idxu.conf.get('key','keys')) + Sys.dprint('\n ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + + + @Log(Const.LOG_DEBUG) + def onCommandGet(self): + """""" + if not len(self.a)>1 : + self.parser.error_cmd((self.a[0]+' command need an id',), True) + else: + vid = self.a[1] + ids = [] + for sid in vid.split(',') : + seq = sid.split('-') + if len(seq)==2 : ids.extend(range(int(seq[0]),int(seq[1])+1)) + else: ids.append(sid) + for sid in ids : + Sys.clear() + self.pheader() + if self.impst.getFile(sid) : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + Sys.dprint() + else: + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == `' , Sys.Clz.bg1+Sys.Clz.fgB7, False) + Sys.echo(str(sid) , Sys.Clz.bg1+Sys.Clz.fgB3, False) + Sys.echo('` KO == ', Sys.Clz.bg1+Sys.Clz.fgB7) + Sys.dprint() + + + @Log(Const.LOG_DEBUG) + def onCommandList(self): + """""" + matchIdsCatg = None + matchIdsUser = None + matchIdsAcc = None + matchIds = None + + if self.o.account is not None : + matchIdsAcc = [] + # print(self.impst.idxu.index.acclist) + # print(self.impst.idxu.index.acclist.keys()) + if self.impst.idxu.conf.ini.has('user', self.o.account+'.imap') : + usr = self.impst.idxu.conf.ini.get('user', self.o.account+'.imap') + if usr in self.impst.idxu.index.acclist.keys() : + print(usr) + for k in self.impst.idxu.index.acclist.keys(): + if self.impst.idxu.index.acclist[k] == usr : + print('matched') + matchIdsAcc = self.impst.idxu.index.getByAccount(k) + print(matchIdsAcc) + break + else : + matchIdsAcc = [] + + if self.o.category is not None : + matchIdsCatg = self.impst.idxu.index.getByCategory(self.o.category) + if self.o.user is not None : + matchIdsUser = self.impst.idxu.index.getByUser(self.o.user) + + if self.o.category is not None : + if self.o.user is not None : + matchIds = self.impst.idxu.index.getIntersection(matchIdsCatg,matchIdsUser) + else : matchIds = matchIdsCatg + + elif self.o.user is not None : + matchIds = matchIdsUser + + if matchIdsAcc is not None: + matchIds = matchIdsAcc if matchIds is None else self.impst.idxu.index.getIntersection(matchIdsAcc,matchIds) + + order = self.o.order + if self.o.order_inv is not None: + order = '-'+self.o.order_inv + Sys.clear() + self.pheader() + self.impst.idxu.index.print(order,matchIds) + + + @Log(Const.LOG_DEBUG) + def onCommandRemove(self): + """""" + key = self.getMatchKey() + if key is not None : + Sys.clear() + self.pheader() + if self.impst.removeFile(self.a[1]) : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == OK == ', Sys.Clz.bg2+Sys.Clz.fgb7) + else : + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == can\'t remove file == ', Sys.Clz.bg3+Sys.Clz.fgB4) + Sys.dprint() + + + @Log(Const.LOG_DEBUG) + def onCommandInfo(self): + """""" + key = self.getMatchKey() + if key is not None : + Sys.clear() + self.pheader() + self.impst.getInfo(int(self.a[1])) + #~ self.cleanAccount('gmail6') + + + def cleanAccount(self, account): + """""" + ids = self.impst.idxu.index.getByAccount(account) + self.impst.idxu.switchFileAccount(account) + self.pheader() + print('cleaning account :'+account) + self.impst.idxu.index.print('ID',ids) + + status, resp = self.impst.idxu.ih.cnx.uid('search', None, '(ALL)') + allids = resp[0].split(b' ') + + goodids = [] + for uid in ids : + key = self.impst.idxu.index.getById(uid) + row = self.impst.idxu.index.get(key) + if row is not None : + km = Kirmah(row[self.impst.idxu.index.KEY]) + hlst = km.ck.getHashList(key, row[self.impst.idxu.index.PARTS], True) + goodids += self.impst.idxu.ih.searchBySubject(hlst['head'][2], True) + + badids = [ i for i in set(allids).difference(set(goodids))] + if len(badids)>0: + self.impst.idxu.ih.delete(badids, True, True) + self.impst.idxu.ih.clearTrash() + + + @Log(Const.LOG_DEBUG) + def onCommandSearch(self): + """""" + if not len(self.a)>1 : + self.parser.error_cmd((' search command need one argument',), True) + else: + vsearch = self.a[1] + + matchIds = self.impst.idxu.index.getByPattern(vsearch) + Sys.clear() + self.pheader() + if matchIds is not None: + Sys.echo(' searching --' , Sys.Clz.fgB3, False) + Sys.echo(' `'+vsearch+'`' , Sys.Clz.fgB7, False) + Sys.echo(' -- found ' , Sys.Clz.fgB3, False) + Sys.echo(str(len(matchIds)), Sys.Clz.fgB1, False) + Sys.echo(' results --' , Sys.Clz.fgB3) + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + matchIdsCatg = None + matchIdsUser = None + matchIdsCrit = None + if self.o.category is not None : + Sys.dprint(self.o.category) + matchIdsCatg = self.impst.idxu.index.getByCategory(self.o.category) + if self.o.user is not None : + matchIdsUser = impst.idxu.index.getByUser(o.user) + + if self.o.category is not None : + if self.o.user is not None : + matchIdsCrit = self.impst.idxu.index.getIntersection(matchIdsCatg,matchIdsUser) + else : matchIdsCrit = matchIdsCatg + + elif self.o.user is not None : + matchIdsCrit = matchIdsUser + + if matchIdsCrit is not None : + matchIds = self.impst.idxu.index.getIntersection(matchIds,matchIdsCrit) + + order = self.o.order + if self.o.order_inv is not None: + order = '-'+self.o.order_inv + + self.impst.idxu.index.print(self.o.order,matchIds) + else: + self.Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint(' ', end='') + Sys.echo(' == no match found for pattern `', Sys.Clz.bg3+Sys.Clz.fgB4, False) + Sys.echo(vsearch , Sys.Clz.bg3+Sys.Clz.fgB1, False) + Sys.echo('` == ' , Sys.Clz.bg3+Sys.Clz.fgB4) + Sys.dprint() diff --git a/impra/conf.py b/impra/conf.py new file mode 100755 index 0000000..7818877 --- /dev/null +++ b/impra/conf.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/conf.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module conf ~~ + +from getpass import getuser as getUserLogin +from os import sep +from os.path import dirname, realpath, isdir, join + +PRG_NAME = 'Imprastorage' +PRG_PACKAGE = 'impra' +PRG_SCRIPT = PRG_NAME.lower() +PRG_CLI_NAME = PRG_SCRIPT+'-cli' +PRG_VERS = '1.12' +PRG_AUTHOR = 'a-Sansara' +PRG_COPY = 'pluie.org' +PRG_YEAR = '2013' +PRG_WEBSITE = 'http://imprastorage.sourceforge.net' +PRG_LICENSE = 'GNU GPL v3' +PRG_RESOURCES_PATH = '/usr/share/'+PRG_PACKAGE+sep +if not isdir(PRG_RESOURCES_PATH): + PRG_RESOURCES_PATH = dirname(dirname(realpath(__file__)))+sep+'resources'+sep+PRG_PACKAGE+sep +#~ print(PRG_RESOURCES_PATH) +PRG_GLADE_PATH = PRG_RESOURCES_PATH+'glade'+sep+PRG_PACKAGE+'.glade' +PRG_LICENSE_PATH = PRG_RESOURCES_PATH+'/LICENSE' +PRG_LOGO_PATH = join(PRG_RESOURCES_PATH,'..'+sep,'pixmaps'+sep,PRG_PACKAGE+sep,PRG_PACKAGE+'.png') +PRG_LOGO_ICON_PATH = join(PRG_RESOURCES_PATH,'..'+sep,'pixmaps'+sep,PRG_PACKAGE+sep,PRG_PACKAGE+'_ico.png') +PRG_ABOUT_LOGO_SIZE = 160 +PRG_ABOUT_COPYRIGHT = '(c) '+PRG_AUTHOR+' - '+PRG_COPY+' '+PRG_YEAR +PRG_ABOUT_COMMENTS = ''.join(['ImpraStorage provided a private imap access to store large files','\n', 'license ',PRG_LICENSE]) +PRG_DESC = """ + ImpraStorage provided a private imap access to store large files. Each file stored on the server is split + in severals random parts. Each part also contains random noise data (lenght depends on a crypt key) to + ensure privacy and exclude easy merge without the corresponding key. + + An index of files stored is encrypt (with the symmetric-key algorithm Kirmah) and regularly updated. Once + decrypt, it permit to perform search on the server and download each part. + + Transfert process is transparent. Just vizualize locally the index of stored files and simply select files + to download or upload. ImpraStorage automatically launch the parts to download, then merge parts in the + appropriate way to rebuild the original file. Inversely, a file to upload is split (in several parts with + addition of noise data), and ImpraStorage randomly upload each parts then update the index. + +""" + +DEFVAL_USER_PATH = ''.join([sep,'home',sep,getUserLogin(),sep]) +DEFVAL_UKEY_PATH = ''.join([DEFVAL_USER_PATH, '.', PRG_PACKAGE,sep]) +DEFVAL_UKEY_NAME = '.default.key' +DEFVAL_UKEY_LENGHT = 1024 + +DEBUG = True +UI_TRACE = True +PCOLOR = True + +def redefinePaths(path): + + PRG_GLADE_PATH = path+PRG_PACKAGE+sep+'glade'+sep+PRG_PACKAGE+'.glade' + PRG_LICENSE_PATH = path+PRG_PACKAGE+sep+'LICENSE' + PRG_LOGO_PATH = path+'pixmaps'+sep+PRG_PACKAGE+sep+PRG_PACKAGE+'.png' + PRG_LOGO_ICON_PATH = path+'pixmaps'+sep+PRG_PACKAGE+sep+PRG_PACKAGE+'_ico.png' diff --git a/impra/core.py b/impra/core.py old mode 100644 new mode 100755 index cfd2970..1a9cb6d --- a/impra/core.py +++ b/impra/core.py @@ -1,165 +1,43 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/core.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# This file is part of ImpraStorage. +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org # -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# ImpraStorage 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. +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . # -# You should have received a copy of the GNU General Public License -# along with ImpraStorage. If not, see . # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ package core ~~ +# ~~ module core ~~ -from base64 import urlsafe_b64encode, b64decode -from binascii import b2a_base64, a2b_base64 -from datetime import datetime, timedelta -from email.encoders import encode_base64 -from email.header import Header -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.utils import formatdate -from json import dump as jdump, load as jload, dumps as jdumps, loads as jloads -from math import ceil, floor -from mmap import mmap -from os import remove, urandom, sep -from os.path import abspath, dirname, join, realpath, basename, getsize, splitext -from re import split as regsplit, match as regmatch, compile as regcompile, search as regsearch -from time import time, sleep -from impra.imap import ImapHelper, ImapConfig, BadLoginException -from impra.util import __CALLER__, RuTime, formatBytes, randomFrom, bstr, quote_escape, stack, run, file_exists, get_file_content, DEBUG, mkdir_p, is_binary, clear, Clz, mprint -from impra.crypt import Kirmah, ConfigKey, Noiser, Randomiz, hash_sha256, hash_md5_file, BadKeyException, hash_sha256_file -from sys import exit as sysexit - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class FSplitter ~~ - -class FSplitter : - """""" - - def __init__(self, ck, wkdir='./'): - """""" - rt = RuTime(eval(__CALLER__())) - self.ck = ck - self.wkdir = wkdir - self.DIR_CACHE = join(self.wkdir,'.cache')+sep - self.DIR_INBOX = join(self.wkdir,'inbox')+sep - self.DIR_OUTBOX = join(self.wkdir,'outbox')+sep - self.DIR_DEPLOY = join(self.wkdir,'deploy')+sep - rt.stop() - - def addFile(self, fromPath, label, fixCount = False): - """""" - rt = RuTime(eval(__CALLER__())) - fsize = getsize(fromPath) - count = ceil(fsize/self.ck.psize) - minp, maxp = 52, 62 - if fsize < 4800000 : minp, maxp = 8, 16 - elif fsize < 22200000 : minp, maxp = 16, 22 - elif fsize < 48000000 : minp, maxp = 22, 32 - elif fsize < 222000000 : minp, maxp = 32, 42 - if not fixCount : - if count < minp : count = randomFrom(maxp,minp) - else: count = fixCount - if not count > 62 : - hlst = self._split(fromPath, self.ck.getHashList(label,count, True)) - else : - raise Exception(fromPath+' size exceeds limits (max : '+formatBytes(self.ck.psize*62)+' ['+str(self.ck.psize*64)+' bytes])') - rt.stop() - return hlst - - def _split(self, fromPath, hlst): - """""" - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - f = open(fromPath, 'rb+') - m = mmap(f.fileno(), 0) - p = 0 - psize = ceil(getsize(fromPath)/hlst['head'][1]) - Clz.print(' '+formatBytes(getsize(fromPath)), Clz.fgB2, False) - Clz.print(' on ' , Clz.fgn7, False) - Clz.print(str(len(hlst['data'])) , Clz.fgB1, False) - Clz.print(' parts (~' , Clz.fgn7, False) - Clz.print(formatBytes(psize) , Clz.fgB2, False) - Clz.print(')' , Clz.fgn7) - while m.tell() < m.size(): - self._splitPart(m,p,psize,hlst['data'][p]) - p += 1 - m.close() - hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[4]) - hlst['head'].append(psize) - rt.stop() - return hlst - - def _splitPart(self,mmap,part,size,phlst): - """""" - rt = RuTime(eval(__CALLER__('mmap,%s,%s,phlist' % (part,size)))) - with open(self.DIR_OUTBOX+phlst[1]+'.ipr', mode='wb') as o: - #~ mprint(self.DIR_OUTBOX+phlst[1]+'.ipr') - #~ mprint(str(phlst[2])+' - '+str(size)+' - '+str(phlst[3])+' = '+str(phlst[2]+size+phlst[3])) - o.write(self.ck.noiser.getNoise(phlst[2])+mmap.read(size)+self.ck.noiser.getNoise(phlst[3])) - - rt.stop() - - def deployFile(self, hlst, fileName, ext='', uid='', dirs=None, fake=False): - """""" - rt = RuTime(eval(__CALLER__())) - p = 0 - hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[0]) - - if dirs is not None and dirs!='none' : - dirPath = join(self.DIR_DEPLOY,dirs)+sep - mkdir_p(dirPath) - else: dirPath = self.DIR_DEPLOY - - filePath = dirPath+fileName - if file_exists(filePath+ext): - Clz.print('\n name already exist, deploying file as :' , Clz.fgB1) - Clz.print(' '+basename(filePath) , Clz.fgB2, False) - Clz.print('-'+str(uid) , Clz.fgB1, False) - Clz.print(ext , Clz.fgB2) - filePath += '-'+str(uid) - else : - Clz.print('\n deploying file as :' , Clz.fgn7) - Clz.print(' '+basename(filePath)+ext , Clz.fgB2, False) - filePath += ext - filePath = abspath(filePath) - fp = open(filePath, 'wb+') - depDir = self.DIR_INBOX - if fake : depDir = self.DIR_OUTBOX - while p < hlst['head'][1] : - self._mergePart(fp,p,hlst['data'][p],depDir) - p += 1 - fp.close() - rt.stop() - return filePath - - def _mergePart(self,fp,part,phlst,depDir): - """""" - rt = RuTime(eval(__CALLER__('fp,%s,phlist,depDir' % part))) - with open(depDir+phlst[1]+'.ipr', mode='rb') as o: - fp.write(o.read()[phlst[2]:-phlst[3]]) - o.close() - remove(depDir+phlst[1]+'.ipr') - rt.stop() +from random import choice +from psr.sys import Sys, Const, Io +from psr.log import Log +from psr.imap import ImapConfig, ImapHelper +from impra.index import ImpraIndex, IndexUpdater, jdumps, jloads +from impra.ini import KiniFile +from kirmah.crypt import KeyGen, Kirmah, hash_sha256_file # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -167,25 +45,25 @@ class FSplitter : class ImpraConf: """""" - + SEP_SECTION = '.' """""" - - def __init__(self, iniFile, profile='default'): + + def __init__(self, iniFile, defaultKeyLenght = 1024): """""" - self.profile = profile - self.ini = iniFile + self.ini = iniFile + self.profile = self.ini.get('profile') save = False if self.ini.isEmpty(): save = True - kg = crypt.KeyGen(256) + kg = KeyGen(defaultKeyLenght) self.set('host' ,'host','imap') self.set('port' ,'993','imap') self.set('user' ,'login','imap') self.set('pass' ,'password','imap') - self.set('box' ,'__IMPRA','imap') - self.set('key' ,kg.key,'keys') - self.set('mark' ,kg.mark,'keys') + self.set('box' ,'__impra__','imap') + self.set('key' , kg.key,'keys') + self.set('mark' , kg.mark,'keys') self.set('salt' ,'-¤-ImpraStorage-¤-','keys') if not self.ini.hasSection(self.profile+self.SEP_SECTION+'catg'): save = True @@ -193,924 +71,484 @@ class ImpraConf: self.set('users', self.get('name','infos'),'catg') except Exception : pass self.set('types', 'music,films,doc,images,archives,games','catg') - if save : - self.ini.write() - - def get(self, key, section='main', profile=None): + + if save : + self.ini.save() + + + def get(self, key, section='main', profile=None, defaultValue=None): """""" if profile == None : profile = self.profile - v = None + v = defaultValue if self.ini.has(key,profile+self.SEP_SECTION+section): v = self.ini.get(key, profile+self.SEP_SECTION+section) return v - + + def set(self, key, value, section='main', profile=None): """""" if profile == None : profile = self.profile v = self.ini.set(key, value, profile+self.SEP_SECTION+section) - self.ini.write() + self.ini.save() return v + + def sets(self, data, section='main', profile=None, save=True): + """""" + if profile == None : profile = self.profile + v = [] + for vals in data: + v.append(self.ini.set(vals[0], vals[1], profile+self.SEP_SECTION+(section if len(vals)==2 else vals[2]))) + if save : self.ini.save() + return v + + + def renameProfile(self, profile, toName, defaultProfile=False): + """""" + o, dic = [], self.ini.getSection(profile) + for s in dic : + o = [] + for key in dic[s] : + o.append([key, dic[s][key], s]) + self.sets(o, profile=toName, save=not defaultProfile) + if defaultProfile : + self.ini.set('profile', toName) + self.ini.save() + + def rem(self, key, section='main', profile=None): """""" if profile == None : profile = self.profile v = self.ini.rem(key, profile+self.SEP_SECTION+section) - self.ini.write() + self.ini.save() return v + - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class ImpraIndex ~~ - -class ImpraIndex: - """A representation of the index stored on the server""" - - SEP_ITEM = '―' - """Separator used for entry""" - - SEP_TOKEN = '#' - """Separator used for token""" - - SEP_CATEGORY = '¤' - """Separator used for category section""" - - QUOTE_REPL = '§' - """Char replacement of simple quote String""" - - SEP_KEY_INTERN = '@' - """Separator used for internal key such categories""" - - HASH = 0 - """""" - LABEL = 1 - """""" - PARTS = 2 - """""" - EXT = 3 - """""" - USER = 4 - """""" - CATG = 5 - """""" - UID = 6 - """""" - BFLAG = 7 - """""" - SIZE = 8 - """""" - ACCOUNT = 9 - """""" - FILE_BINARY = 'b' - """""" - FILE_CRYPT = 'c' - """""" - COLS = ('HASH','LABEL','PART','TYPE','USER','CATEGORY','ID','BLFAG','SIZE') - """""" - - def __init__(self, key, mark, encdata='', dicCategory={}): - """Initialize the index with rsa and encoded data - - :Parameters: - `key` : str - appropriate key to decrypt/encrypt data - `mark` : str - appropriate mark to check correct key - `encdata` : str - initial content of the index encrypted with Kirmah Algorythm - and representing a dic index as json string - """ - rt = RuTime(eval(__CALLER__())) - self.km = Kirmah(key, mark) - self.dic = {} - if encdata =='' : - self.dic = {} - self.id = 1 - else : - self.dic = self.decrypt(encdata) - l = [self.dic[k][self.UID] for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] - if len(l) > 0 : - self.id = max(l)+1 - else: self.id = 1 - for k in dicCategory : - if k == "users" : - for k1 in dicCategory[k]: - if self.SEP_KEY_INTERN+k in self.dic: - if k1 not in self.dic[self.SEP_KEY_INTERN+k]: - self.dic[self.SEP_KEY_INTERN+k][k1] = dicCategory[k][k1] - else : - if not self.SEP_KEY_INTERN+k in self.dic: - self.dic[self.SEP_KEY_INTERN+k] = dicCategory[k] - rt.stop() - - def add(self,key, label, count, ext='', usr='', cat='', md5='', bFlag='b', size='', account=''): - """Add an entry to the index - """ - rt = RuTime(eval(__CALLER__())) - if self.get(md5) == None : - self.dic[md5] = (key,label,count,ext,usr,cat,self.id,bFlag,size,account) - self.id +=1 - else : - mprint(label+' already exist') - rt.stop() - - def addUser(self, nameFrom, hashName): + def remProfile(self, profile): """""" - if not self.hasUser(hashName): - self.dic[self.SEP_KEY_INTERN+'users'][hashName] = nameFrom - - def hasUser(self, hashName): - """""" - if not self.SEP_KEY_INTERN+'users' in self.dic: - self.dic[self.SEP_KEY_INTERN+'users'] = {} - return hashName in self.dic[self.SEP_KEY_INTERN+'users'] - - def getUser(self, hashName): - """""" - usrName = 'Anonymous' - if self.hasUser(hashName): - usrName = self.dic[self.SEP_KEY_INTERN+'users'][hashName] - return usrName - - def rem(self,label): - """Remove the selected label from the index""" - self.dic.pop(label, None) - - def getAutoCatg(self,ext): - """""" - catg = 'none' - if regsearch('\.(jpg|jpeg|gif|png)',ext): - catg = 'images' - elif regsearch('\.(txt|doc|odt|csv|pdf)',ext): - catg = 'doc' - elif regsearch('\.(mp4|avi|mpg|mpeg|flv|ogv)',ext): - catg = 'films' - elif regsearch('\.(mp3|ogg|flac)',ext): - catg = 'music' - elif regsearch('\.(zip|7z|tar|gz|rar|bz|xz|jar)',ext): - catg = 'archives' - return catg - - def isEmpty(self): - """""" - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] - return len(r) == 0 - - def getLabel(self, key): - """Get label corresponding to key in the index - :Returns: `str`|None label - """ - value = '' - row = self.get(key) - if row is not None : - value = row[self.LABEL] - - def get(self, key): - """Get the corresponding key in the index - :Returns: `tuple` row - """ - row = None - if key in self.dic : row = self.dic.get(key) - return row - - def edit(self, key, label=None, category=None): - """Get the corresponding key in the index - :Returns: `tuple` row - """ - rt = RuTime(eval(__CALLER__())) - done = False - row = self.dic[key] - r = list(row) - if label != None : - r[self.LABEL] = label - if category != None : - r[self.CATG] = category - self.dic[key] = tuple(r) - done = row != self.dic[key] - rt.stop() - return done - - def getById(self,sid): - """Get the corresponding id in the index - :Returns: `str`|None key - """ - rt = RuTime(eval(__CALLER__(sid))) - l = None - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and self.dic[k][self.UID] == int(sid)] - if len(r)==1: l = r[0] - rt.stop() - return l - - def fixAccount(self,account): - """""" - rt = RuTime(eval(__CALLER__('%s' % account))) - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] - for k in r: - t = list(self.dic[k]) - if len(t)-1 < self.ACCOUNT: - t.append(account) - else: - t[self.ACCOUNT] = account - self.dic[k] = tuple(t) - rt.stop() - - def getLightestAccount(self,l): - """""" - rt = RuTime(eval(__CALLER__('%s' % str(l)))) - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] - t = {} - for k in r: - if not self.dic[k][self.ACCOUNT] in t: t[self.dic[k][self.ACCOUNT]] = self.dic[k][self.SIZE] - else: t[self.dic[k][self.ACCOUNT]] += int(self.dic[k][self.SIZE]) - profile = None - r = [] - for a in l: - if not a in t : - profile = a - break - else : r.append((t[a],a)) - if profile is None : - d = sorted(r, reverse=False, key=lambda lst:lst[0]) - profile = d[0][1] - rt.stop() - return profile - - def fixDuplicateIds(self): - """Get corresponding keys of duplicate ids in the index - :Returns: `str`|None key - """ - rt = RuTime(eval(__CALLER__())) - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] - l = [(k,self.dic[k][self.UID]) for k in r] - l2 = [k[1] for k in l] - if len(l2)> 0 : - mxid = max(l2) - import collections - l3 = [x for x, y in collections.Counter(l2).items() if y > 1] - d = [k[0] for k in l if any( k[1] == v for v in l3)] - for k in d: - mxid += 1 - #mprint(self.dic[k]) - t = list(self.dic[k]) - t[self.UID] = mxid - #mprint(t) - self.dic[k] = tuple(t) - self.id = mxid+1 - else: - self.id = 1 - d = () - rt.stop() - return len(d)>0 - - def getByLabel(self,label): - """Get the corresponding label in the index - :Returns: `str`|None key - """ - rt = RuTime(eval(__CALLER__(sid))) - l = None - r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and self.dic[k][self.LABEL] == int(label)] - if len(r)==1: l = r[0] - rt.stop() - return l - - def getByPattern(self,pattern): - """Get ids corresponding to label matching the pattern in the index - :Returns: `[uid]`|None matchIds - """ - rt = RuTime(eval(__CALLER__(pattern))) - l = None - r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(pattern,self.dic[k][self.LABEL]) is not None ] - l = [self.dic[k][self.UID] for k in r] - rt.stop() - return l - - def getByCategory(self,category): - """Get ids corresponding to category - :Returns: `[uid]`|None matchIds - """ - rt = RuTime(eval(__CALLER__(category))) - l = None - r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(category,self.dic[k][self.CATG]) is not None ] - l = [self.dic[k][self.UID] for k in r] - rt.stop() - return l - - def getByUser(self,user): - """Get ids corresponding to category - :Returns: `[uid]`|None matchIds - """ - rt = RuTime(eval(__CALLER__(user))) - l = None - r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(user,self.getUser(self.dic[k][self.USER])) is not None ] - l = [self.dic[k][self.UID] for k in r] - rt.stop() - return l - - def getIntersection(self,list1, list2): - """Get ids intercept list1 and list2 - :Returns: `[uid]`|None matchIds - """ - rt = RuTime(eval(__CALLER__())) - l = [ i for i in set(list1).intersection(set(list2))] - rt.stop() - return l - - def encrypt(self): - """""" - #~ mprint('encrypting index :') - jdata = jdumps(self.dic) - #~ mprint(jdata) - return self.km.encrypt(jdata,'.index',22) - - def decrypt(self,data): - """""" - #~ mprint('decrypting index : ') - try : - jdata = self.km.decrypt(data,'.index',22) - data = jloads(jdata) - except ValueError as e: - raise BadKeyException(e) - return data - - def print(self,order='ID', matchIds=None): - """Print index content as formated bloc""" - #clear() - from impra.cli import printLineSep, LINE_SEP_LEN, LINE_SEP_CHAR - inv = order.startswith('-') - if inv : order = order[1:] - orderIndex = self.COLS.index(order) - if orderIndex is None : orderIndex = self.COLS.index('ID') - - d = sorted([(self.dic.get(k),k) for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)], reverse=inv, key=lambda lst:lst[0][orderIndex]) - - sizeid = 1+ceil(len(d)) - if sizeid < 3 : sizeid = 3 - sizeid = 3 - addsize = abs(3 - sizeid); - - Clz.print(' ID'+' '*(1+addsize), Clz.BG4+Clz.fgB7, False, False) - mprint('HASH' +' '*6 , end='') - mprint('LABEL' +' '*(35), end='') - mprint('SIZE' +' '*5 , end='') - mprint('PART' +' '*2 , end='') - mprint('TYPE' +' '*2 , end='') - mprint('USER' +' '*7 , end='') - #mprint('CATEGORY'+' '*(17-addsize)) - mprint('CATEGORY'+' '*(17-addsize), end='') - Clz.print('ACCOUNT'+' '*(2), Clz.BG4+Clz.fgB7) - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - dbg = DEBUG.active - DEBUG.active = True - a = '' - tsize = 0 - psize = 0 - acc = {} - for v,k in d : - if matchIds==None or v[self.UID] in matchIds: - a = '' - Clz.print(str(v[self.UID]).rjust(sizeid,' '), Clz.bg1+Clz.fgB7, False) - Clz.print(' '+str(k)[0:6]+'... ' , Clz.fgN2, False) - if len(v[self.LABEL])>36 : a = '...' - Clz.print(str(v[self.LABEL][:36]+a).ljust(40,' ') , Clz.fgN7, False) - a = '' - Clz.print(formatBytes(int(v[self.SIZE]))[:8].rjust(8,' ')+' '*2 , Clz.fgN5, False) - Clz.print(str(v[self.PARTS]).rjust(2 ,'0') +' '*2 , Clz.fgN1, False) - Clz.print(str(v[self.EXT][:5]).ljust(7,' ') , Clz.fgn3, False) - Clz.print(self.getUser(str(v[self.USER])).ljust(11 ,' ') , Clz.fgn7, False) - #~ Clz.print(str(v[self.CATG]).ljust(30 ,' ') , Clz.fgN3) - if len(v[self.CATG])>22 : a = '...' - Clz.print(str(v[self.CATG]+a).ljust(25 ,' ') , Clz.fgN3, False) - if len(v)-1==self.ACCOUNT: - Clz.print(str(v[self.ACCOUNT]).ljust(14 ,' ') , Clz.fgN3) - if v[self.ACCOUNT] in acc : - acc[v[self.ACCOUNT]] += int(v[self.SIZE]) - else : acc[v[self.ACCOUNT]] = int(v[self.SIZE]) - else: mprint() - - psize += int(v[self.SIZE]) - tsize += int(v[self.SIZE]) - if len(d)==0: - Clz.print(' empty', Clz.fgB1) - DEBUG.active = dbg - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - c = Clz.fgB2 - if psize != tsize : c = Clz.fgB7 - Clz.print(' size : ', Clz.fgB3, False) - Clz.print(formatBytes(int(psize))[:9].rjust(9,' '), c, False) - if psize != tsize : - Clz.print(' / ', Clz.fgB3, False) - Clz.print(formatBytes(int(tsize)), Clz.fgB2, False) - mprint() - printLineSep(LINE_SEP_CHAR,LINE_SEP_LEN) - - #~ Clz.print(' '*4+'[', Clz.fgB7, False) - #~ sep = '' - #~ for k in acc: - #~ if k!= '': - #~ Clz.print(sep+k,Clz.fgB3,False) - #~ Clz.print(':',Clz.fgB7,False) - #~ Clz.print(formatBytes(acc[k]),Clz.fgB2,False) - #~ if sep=='':sep = ',' - #~ Clz.print(']', Clz.fgB7, False) - #~ mprint() + if profile is not None : + print(self.ini.getSections()) + print('-'*10) + s = self.ini.getSection(profile) + d = s.copy() + print(d) + for section in d : + print(' pop '+profile+self.SEP_SECTION+section) + self.ini.dic.pop(profile+self.SEP_SECTION+section) + for key in d[section] : + print(' '+key) + print(' ['+profile+self.SEP_SECTION+section+']') + self.ini.rem(key, profile+self.SEP_SECTION+section) + self.ini.save() + #~ return v # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~ class ImpraStorage ~~ class ImpraStorage: - """""" - - def __init__(self, conf, remIndex=False, wkdir=None): + """""" + + @Log(Const.LOG_BUILD) + def __init__(self, conf, remIndex=False, wkdir='./', emit=None): """""" - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - if wkdir == None : wkdir = abspath(join(dirname( __file__ ), '..', 'wk')) - self.wkdir = wkdir - self.conf = conf - self.pathInd = dirname(self.conf.ini.path)+sep+'.index' - self.rootBox = self.conf.get('box','imap') - self.mb = MailBuilder(self.conf.get('salt','keys')) - self.fsplit = FSplitter(ConfigKey(),self.wkdir) - self.delids = [] - self.ih = None - self._setIndexImap() - if remIndex : self.removeIndex() - self.getIndex() - rt.stop() - - def _setIndexImap(self): - iconf = ImapConfig(self.conf.get('host','imap'), self.conf.get('port','imap'), self.conf.get('user', 'imap'), self.conf.get('pass', 'imap')) - try : - if self.rootBox == None : - self.rootBox = '__impra__' - self.conf.set('box',self.rootBox,'imap') - if self.ih is None or self.ih.conf.user != iconf.user : - try : - self.ih = ImapHelper(iconf,self.rootBox) - except BadLoginException as e1: - Clz.print('Error :', Clz.fgB1, True, False) - mprint(e1) - Clz.print('', Clz.OFF) - sysexit(1) - - except Exception as e: - Clz.print('Error :', Clz.fgB1, True, False) - mprint(e) - Clz.print('check your connection or your imap config', Clz.fgB1) - sysexit(1) - - def _getIdIndex(self): - """""" - mid = None - ids = self.ih.searchBySubject(self.mb.getHashName('index'),True) - if len(ids) > 0 and int(ids[0]) >= 0 : - mid = ids[len(ids)-1] - for i in ids: - if i != mid : self.delids.append(i) - self.idx = mid - return mid - - def _getIdsBySubject(self,subject): - """""" - status, resp = self.ih.srv.search(None, '(SUBJECT "%s")' % subject) - ids = [m for m in resp[0].split()] - return ids - - def _getCryptIndex(self): - """""" - encData = '' - if not self.idx : self._getIdIndex() - if self.idx : - msgIndex = self.ih.email(self.idx, True) - if msgIndex != None : - for part in msgIndex.walk(): - ms = part.get_payload(decode=True) - encData = str(ms,'utf-8') - return encData - - def getIndexDefaultCatg(self): - """""" - usrName = self.conf.get('name','infos') - defUsers = self.conf.get('users','catg').split(',') - dic = {'catg':self.conf.get('types','catg'), 'users':{ ('%s' % self.mb.getHashName('all')) : 'all', ('%s' % self.mb.getHashName(usrName)) : usrName}} - for u in defUsers : - dic['users'][('%s' % self.mb.getHashName(u.strip()))] = u.strip() - return dic - - def getIndex(self, forceRefresh=False): - """""" - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - index = None - encData = '' - uid = self.conf.get('uid' ,'index') - date = self.conf.get('date ','index') - tstamp = self.conf.get('time' ,'index') - refresh = forceRefresh - if not refresh and tstamp is not None and (datetime.now() - datetime.strptime(tstamp[:-7], '%Y-%m-%d %H:%M:%S')) < timedelta(minutes = 1) : - # getFromFile - if uid != None and file_exists(self.pathInd): # int(self.idx) == int(uid) - self.idx = uid - encData = get_file_content(self.pathInd) - Clz.print(' get index from cache', Clz.fgn7) - else: refresh = True - else: refresh = True - self.irefresh = refresh - if refresh : - Clz.print(' refreshing index', Clz.fgn7) - self._getIdIndex() - if self.idx : - encData = self._getCryptIndex() - with open(self.pathInd, mode='w', encoding='utf-8') as o: - o.write(encData) - self.conf.set('time',str(datetime.now()),'index') - self.importIndex(encData) - rt.stop() - - def importIndex(self, encData): - self.index = ImpraIndex(self.conf.get('key','keys'),self.conf.get('mark','keys'), encData, self.getIndexDefaultCatg()) - defUsers = self.conf.get('users','catg') - if not ImpraIndex.SEP_KEY_INTERN+'users' in self.index.dic: - self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'] = {} - for k in self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users']: - if self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'][k] not in [ i.strip() for i in defUsers.split(',')]: - self.conf.set('users',defUsers+', '+self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'][k],'catg') + iconf = ImapConfig(conf.get('host','imap'), conf.get('user', 'imap'), conf.get('pass', 'imap'), conf.get('port','imap')) + self.rootBox = conf.get('box', 'imap') + self.emit = emit + self.wkdir = wkdir + self.idxu = IndexUpdater(ImapHelper(iconf, self.rootBox), conf, self.wkdir, emit=emit) + self.outbox = self.wkdir + 'outbox/' + self.inbox = self.wkdir + 'inbox/' + self.deploy = self.wkdir + 'deploy/' + self.addmapPath = self.outbox+'.addmap' + self.wkdir = wkdir + #~ impconf.renameProfile('default', 'gmail1', True) + #~ impconf.ini.print() - def removeIndex(self): - """""" - rt = RuTime(eval(__CALLER__())) - self._getIdIndex() - if self.idx : - self.ih.delete(self.idx, True) - self.ih.deleteBin() - self.conf.rem('*','index') - self.idx = None - remove(self.pathInd) - rt.stop() - - def saveIndex(self): - """""" - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - try: - if self.idx != None : - self.ih.delete(self.idx, True) - except Exception as e : - mprint('error : ') - mprint(e) - #~ if len(self.delids) > 0 : - #~ for i in self.delids : self.ih.delete(i, True, False) - #~ Clz.print('\n expunge, waiting server...\n', Clz.fgB1) - #~ self.srv.expunge() - #~ sleep(2) - self.index.fixDuplicateIds() - #~ self.index.fixAccount('gmail5') - encData = self.index.encrypt() - msgIndex = self.mb.buildIndex(encData) - if DEBUG.level <= DEBUG.NOTICE : mprint(msgIndex.as_string()) - ids = self.ih.send(msgIndex.as_string(), self.rootBox) - date = self.ih.headerField('date', ids[1], True) - self.conf.set('uid',ids[1],'index') - self.conf.set('date',date,'index') - with open(self.pathInd, mode='w', encoding='utf-8') as o: - o.write(encData) - self.conf.set('time',str(datetime.now()),'index') - self.clean() - rt.stop() - return True - def encryptTextFile(self,path): - """""" - rt = RuTime(eval(__CALLER__())) - cdata = self.index.km.subenc(get_file_content(path)) - with open(self.fsplit.DIR_CACHE+'.~KirmahEnc', mode='w') as o: - o.write(cdata) - rt.stop() - return self.fsplit.DIR_CACHE+'.~KirmahEnc' - - def decryptTextFile(self,path): - """""" - rt = RuTime(eval(__CALLER__())) - data = self.index.km.subdec(get_file_content(path)) - with open(path, mode='w') as o: - o.write(data) - rt.stop() - def checkSendIds(self,sendIds,subject): + def SocketError(self): """""" - lloc = [bytes(str(data[0]),'utf-8') for mid, data in enumerate(sendIds)] - lsrv = self.ih.searchBySubject(subject,True) - return [ int(i) for i in set(lloc).difference(set(lsrv))] + return self.idxu.ih.cnx.abort - def switchFileAccount(self,profile=None): + def reconnect(self): """""" - rt = RuTime(eval(__CALLER__('%s' % str(profile)))) - pl = self.conf.get('multi','imap') - if pl is not None : - pl = pl.split(',') - if len(pl) > 0: - if not self.conf.profile in pl: - pl.append(self.conf.profile) - iconf = self.ih.conf - account = self.conf.get('user','imap',profile) - if True or iconf.user != account : - # reinit - iconf.user = None - try : - if profile is None : profile = self.index.getLightestAccount(pl) - if profile in pl : - iconf.user = self.conf.get('user','imap',profile) - iconf.pwd = self.conf.get('pass','imap',profile) - iconf.host = self.conf.get('host','imap',profile) - iconf.port = self.conf.get('port','imap',profile) - self.ih = ImapHelper(iconf,self.rootBox) - except Exception as e: - mprint('Error : ') - mprint(e) - mprint('check your connection or your imap config for profile '+profile) - rt.stop() - if profile is None: profile = self.conf.profile - return profile - - def addFile(self, path, label, catg=''): + self.idxu.ih.reconnect() + + + def backupAddMap(self, data): """""" - done = False - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - - account = self.switchFileAccount() - - _, ext = splitext(path) - #~ try: - size = getsize(path) - if size > 0 : - md5 = hash_sha256_file(path) - mprint() - Clz.print(' account : ' , Clz.fgn7, False) - Clz.print(self.ih.conf.user , Clz.fgB7) - Clz.print(' file : ' , Clz.fgn7, False) - Clz.print(path , Clz.fgN1) - Clz.print(' hash : ' , Clz.fgn7, False) - Clz.print(md5 , Clz.fgN2) - mprint() - if not self.index.get(md5) : - - if catg=='' : catg = self.index.getAutoCatg(ext) - - bFlag = ImpraIndex.FILE_BINARY - if not is_binary(path): - bFlag = ImpraIndex.FILE_CRYPT - path = self.encryptTextFile(path) - - hlst = self.fsplit.addFile(path,md5) - if DEBUG.active and DEBUG.level <= DEBUG.NOTICE : - mprint(hlst['head']) - for v in hlst['data']: - mprint(v) - - usr = self.conf.get('name','infos') - ownerHash = self.mb.getHashName(usr) - self.index.addUser(usr,ownerHash) - - cancel = False - sendIds = [] - test = True - for row in hlst['data'] : - msg = self.mb.build(usr,'all',hlst['head'][2],self.fsplit.DIR_OUTBOX+row[1]+'.ipr') - mid = None - try : - mid = self.ih.send(msg.as_string(), self.rootBox) - except Exception as e: - Clz.print('Error :', Clz.fgB1, True, False) - mprint(e) - Clz.print('retry', Clz.fgB1) - self.switchFileAccount(account) - mid = self.ih.send(msg.as_string(), self.rootBox) - - if mid is not None : - #sleep(0.5) - # dont remove - status, resp = self.ih.fetch(mid[1],'(UID BODYSTRUCTURE)', True) - if status == self.ih.OK: - sendIds.append((mid[1],row)) - mprint(' ',end='') - Clz.print('part ' , Clz.fgn7, False) - Clz.print(str(row[0]) , Clz.fgB2, False) - Clz.print(' sent as msg ', Clz.fgn7, False) - Clz.print(str(mid[1]) , Clz.fgB1) - else: - mprint('\n-- error occured when sending part : %s\n-- retrying' % row[0]) - - if not cancel : - - diff = self.checkSendIds(sendIds,hlst['head'][2]) - #~ mprint('diff') - #~ for mid in diff : - #~ if mid > 0: - #~ mprint(mid) - #self.ih.delete(str(mid), True, False) - if len(diff) > 0 : - for mid in diff : - status, resp = self.ih.fetch(str(mid),'(UID BODYSTRUCTURE)', True) - if not status == self.ih.OK: - Clz.print(' error when sending, missing parts :', Clz.fgB1) - # bugfix mid would be without +1 - k = [ k for k in sendIds if len(k)>0 and int(k[0]) == int(mid+1)] - if len(k) > 0 : - mprint(k) - row = k[0][1] - msg = self.mb.build(usr,'all',hlst['head'][2],self.fsplit.DIR_OUTBOX+row[1]+'.ipr') - if DEBUG.active : - Clz.print(' resending part ' , Clz.fgn7, False) - Clz.print(str(row[0]) , Clz.fgN2) - mid = self.ih.send(msg.as_string(), self.rootBox) - else : - mprint() - #Clz.print(' index intall files checked\n', Clz.fgB2) - self._setIndexImap() - self.getIndex(True) - self.index.add(hlst['head'][3],label,hlst['head'][1],ext,ownerHash,catg,md5,bFlag,size,account) - done = self.saveIndex() - - # clean - for mid, row in sendIds : - if cancel : self.ih.delete(mid, True) - if file_exists(self.fsplit.DIR_OUTBOX+row[1]+'.ipr') : remove(self.fsplit.DIR_OUTBOX+row[1]+'.ipr') - self.clean() - - else : - mprint(' ',end='') - Clz.print(' == file already exist on server as ' , Clz.fgN7+Clz.bg1, False) - Clz.print(self.index.dic[md5][ImpraIndex.LABEL] , Clz.bg1+Clz.fgB3, False) - Clz.print(' [id:' , Clz.fgN7+Clz.bg1, False) - Clz.print(str(self.index.dic[md5][ImpraIndex.UID]) , Clz.bg1+Clz.fgB3, False) - Clz.print('] == ' , Clz.fgN7+Clz.bg1) - mprint() - else : - mprint(' ',end='') - Clz.print(' == files is empty or don\t exists == ' , Clz.bg1+Clz.fgN7) - mprint() - - #~ except Exception as e : - #~ mprint('Erroreuh') - #~ mprint(e) - self._setIndexImap() - rt.stop() - return done + Io.set_data(self.addmapPath, jdumps(data)) + call = ' '.join([Sys.executable, 'kirmah-cli.py', 'enc', '-qf', self.addmapPath, '-z', '-r', '-m', '-o', self.addmapPath+Kirmah.EXT, '-k', self.idxu.index.keyPath ]) + print(call) + Sys.sysCall(call) + Io.removeFile(self.addmapPath) - def editFile(self,key,label,category): + def getBackupAddMap(self): """""" - rt = RuTime(eval(__CALLER__())) - done = False - row = self.index.get(key) - if row==None : - mprint() - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(str(key) , Clz.bg1+Clz.fgB3, False) - Clz.print('` not on the server == ', Clz.bg1+Clz.fgB7) - mprint() - else : - done = self.index.edit(key,label,category) - rt.stop() - return done + data = None + if Io.file_exists(self.addmapPath+Kirmah.EXT) : + call = ' '.join([Sys.executable, 'kirmah-cli.py', 'dec', '-qf', self.addmapPath+Kirmah.EXT, '-z', '-r', '-m', '-o', self.addmapPath, '-k', self.idxu.index.keyPath ]) + print(call) + Sys.sysCall(call) + data = jloads(Io.get_data(self.addmapPath)) + Io.removeFile(self.addmapPath) + return data - def removeFile(self,key): + + def hasBackupAddMap(self): """""" - done = False - row = self.index.get(key) - if row==None : - mprint() - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(str(key) , Clz.bg1+Clz.fgB3, False) - Clz.print('` not on the server == ', Clz.bg1+Clz.fgB7) - mprint() - else : - rt = RuTime(eval(__CALLER__('"[%i] %s"' % (row[ImpraIndex.UID],row[ImpraIndex.LABEL]))),DEBUG.INFO) - self.switchFileAccount(row[ImpraIndex.ACCOUNT]) - account = self.ih.conf.user - if DEBUG.active : - Clz.print(' account : ' , Clz.fgn7, False) - Clz.print(account , Clz.fgB7) - ck = ConfigKey(row[ImpraIndex.HASH]) - hlst = ck.getHashList(key,row[ImpraIndex.PARTS],True) - Clz.print(' get file list from server', Clz.fgn7) - ids = self.ih.searchBySubject(hlst['head'][2], True) - for mid in ids : - self.ih.delete(mid, True, False) - Clz.print('\n expunge, waiting pls...\n', Clz.fgB1) - self.ih.srv.expunge() - sleep(0.5) - self._setIndexImap() - self.getIndex(True) - self.index.rem(key) - done = self.saveIndex() - rt.stop() - return done - - def getFile(self,key): + return Io.file_exists(self.addmapPath+Kirmah.EXT) + + + @Log(Const.LOG_DEBUG) + def sendFile(self, data, retry=False): """""" - done = False - row = self.index.get(key) - if row==None : - mprint() - Clz.print(' == `' , Clz.bg1+Clz.fgB7, False) - Clz.print(str(key) , Clz.bg1+Clz.fgB3, False) - Clz.print('` not on the server == ', Clz.bg1+Clz.fgB7) - mprint() + done = None + key = None + if data is not None : + key , label, ext, count, catg, hlst, usr, ownerHash, sha256, size = data + self.idxu.index.addUser(usr, ownerHash) + account = self.idxu.switchFileAccount() + sendIds = [] + cancel = False + d = None - else : - rt = RuTime(eval(__CALLER__('"[%i] %s"' % (row[ImpraIndex.UID],row[ImpraIndex.LABEL]))),DEBUG.INFO) - self.switchFileAccount(row[ImpraIndex.ACCOUNT]) - account = self.ih.conf.user - Clz.print(' account : ' , Clz.fgn7, False) - Clz.print(account , Clz.fgB7) - ck = ConfigKey(row[ImpraIndex.HASH]) - hlst = ck.getHashList(key,row[ImpraIndex.PARTS],True) - ids = self.ih.searchBySubject(hlst['head'][2],True) - - if len(ids) >= row[ImpraIndex.PARTS]: - - for mid in ids : - self.ih.downloadAttachment(mid,self.fsplit.DIR_INBOX, True) - if DEBUG.active and DEBUG.level <= DEBUG.NOTICE : - mprint(hlst['head']) - for v in hlst['data']: - mprint(v) - path = self.fsplit.deployFile(hlst, row[ImpraIndex.LABEL], row[ImpraIndex.EXT], row[ImpraIndex.UID], row[ImpraIndex.CATG]) - if row[ImpraIndex.BFLAG] == ImpraIndex.FILE_CRYPT: - self.decryptTextFile(path) - mprint() - Clz.print(' deploying in ', Clz.fgn7) - Clz.print(' '+dirname(path), Clz.fgB2) - mprint() - done = True - + Sys.cli_emit_progress(0) + Sys.sleep(0.2) + if not retry: + Sys.pwlog([(' Sending... ' , Const.CLZ_7), + (' (' , Const.CLZ_0), + (' ~'+Sys.readableBytes(Sys.getsize(self.outbox+hlst['data'][0][1]+Kirmah.EXT)), Const.CLZ_3), + (' per msg ) ' , Const.CLZ_0, True)]) else : - mprint() - Clz.print(' == `' , Clz.BG3+Clz.fgB1, False) - Clz.print(row[ImpraIndex.LABEL] , Clz.BG3+Clz.fgB4, False) - Clz.print('` invalid count parts ' , Clz.BG3+Clz.fgB1, False) - Clz.print(str(len(ids)) , Clz.BG3+Clz.fgB4, False) - Clz.print('/' , Clz.BG3+Clz.fgB1, False) - Clz.print(str(row[ImpraIndex.PARTS]) , Clz.BG3+Clz.fgB4, False) - Clz.print(' == ' , Clz.BG3+Clz.fgB1) - mprint() - self._setIndexImap() - rt.stop() + Sys.pwlog([(' Retry sending last file... ' , Const.CLZ_0), + (label+ext , Const.CLZ_7), + (' ('+catg+')' , Const.CLZ_3 , True)]) + + ignore = False + + for i, row in enumerate(hlst['data']): + """""" + if retry : + if not Io.file_exists(self.outbox+row[1]+Kirmah.EXT): + continue + elif not ignore: + Sys.pwlog([(' Ignoring file 1 to '+str(i), Const.CLZ_1, True)]) + ignore = True + + d = Sys.datetime.now() + msg = self.idxu.mb.build(usr, 'all', hlst['head'][2], self.outbox+row[1]+Kirmah.EXT) + try : + mid = self.idxu.ih.send(msg.as_string(), self.rootBox) + except Exception as e : + Sys.pwarn((('addFile : ',(str(e),Sys.CLZ_WARN_PARAM), ' !'),)) + Sys.echo('waiting 5 s and retry') + Sys.sleep(5) + # force reconnect + self.impst.idxu.switchFileAccount(account, True) + # retry + mid = self.idxu.ih.send(msg.as_string(), self.rootBox) + finally : + if not mid is None : + status, resp = self.idxu.ih.fetch(mid[1],'(UID BODYSTRUCTURE)', True) + if status == self.idxu.ih.OK: + sendIds.append((mid[1],row)) + Sys.pwlog([(' part ' , Const.CLZ_0), + (str(row[0]).rjust(2, '0') , Const.CLZ_2), + (' sent as msg ' , Const.CLZ_0), + (str(mid[1]).rjust(5, '0') , Const.CLZ_1), + (' (' , Const.CLZ_7), + (str(int(row[4])+1).rjust(2, '0') , Const.CLZ_2), + ('/' , Const.CLZ_7), + (str(count) , Const.CLZ_3), + (') in ' , Const.CLZ_7), + (Sys.getDelta(d) , Const.CLZ_4,True)]) + + Sys.cli_emit_progress(int((i+1)*100/len(hlst['data']))) + + Sys.removeFile(self.outbox+row[1]+Kirmah.EXT) + else: + Sys.pwarn((('error occured when sending part ',(row[0], Sys.Clz.fgb3), ' !'),)) + + diff = self.checkSendIds(sendIds,hlst['head'][2]) + if len(sendIds)==count or retry : + self.idxu.get(True) + self.idxu.index.add(key, label, hlst['head'][1], ext, ownerHash, catg, sha256, size, account) + done = self.idxu.update() + Io.removeFile(self.addmapPath+Kirmah.EXT) + + # resending missing parts + else : + Sys.pwarn((('TODO => must resending ',('missing',Sys.CLZ_WARN_PARAM), ' parts'),)) + print(diff) + + # clean + for mid, row in sendIds : + if Io.file_exists(self.outbox+row[1]+Kirmah.EXT) : Sys.removeFile(self.outbox+row[1]+Kirmah.EXT) + if cancel : + delids = [ mid for mid, row in senids] + print(delids) + self.idxu.ih.delete(delids, True) + return done, key + + + @Log(Const.LOG_APP) + def addFile(self, fromPath, label='', catg='', sendAddMap=False): + """""" + #~ Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True)]) + data = self.buildFile(fromPath, label, catg) if not sendAddMap else self.getBackupAddMap() + return self.sendFile(data) + + + def getCountParts(self, fromPath): + """""" + fsize = Sys.getsize(fromPath) + count = Sys.ceil(fsize/19710000) + minp, maxp = 52, 62 + if fsize < 4800000 : minp, maxp = 8, 16 + elif fsize < 22200000 : minp, maxp = 16, 22 + elif fsize < 48000000 : minp, maxp = 22, 32 + elif fsize < 222000000 : minp, maxp = 32, 42 + if count < minp : + count = choice(list(range(minp,maxp))) + if not count > 62 : + return count + else : + raise Exception(fromPath+' size exceeds limits (max : '+formatBytes(self.ck.psize*62)+' ['+str(self.ck.psize*64)+' bytes])') + + + @Log() + def buildFile(self, fromPath, label='', catg='') : + count = self.getCountParts(fromPath) + Sys.pwlog([(' Get Hash... ' , Const.CLZ_7, True)]) + sha256 = hash_sha256_file(fromPath) + Sys.pwlog([(' hash : ' , Const.CLZ_0), + (sha256 , Const.CLZ_2, True), + (' Build File...' , Const.CLZ_0, True)]) + + kg = KeyGen(128) + size = Sys.getsize(fromPath) + row = self.idxu.index.get(sha256) + if row is None : + if label == '': + label, ext = Sys.getFileExt(Sys.basename(fromPath)) + else : + label, ext = Sys.getFileExt(label) + if catg=='' : + catg = self.idxu.index.getAutoCatg(ext) + size = Sys.getsize(fromPath) + + Sys.pwlog([(' Splitting ' , Const.CLZ_1), + (label , Const.CLZ_7), + (ext , Const.CLZ_7), + (' (' , Const.CLZ_0), + (Sys.readableBytes(size) , Const.CLZ_3), + (')' , Const.CLZ_0, True)]) + Sys.cli_emit_progress(0) + Sys.sleep(0.2) + km = Kirmah(kg.key) + km.DIR_OUTBOX = self.outbox + # hlst genetate with sha256 + hlst = km.ck.getHashList(sha256, int(count), True) + usr = self.idxu.conf.get('name','infos') + ownerHash = self.idxu.mb.getHashName(usr) + km.split(fromPath, hlst) + Sys.pwlog([(' done ' , Const.CLZ_2, True)]) + row = [kg.key , label, ext, count, catg, hlst, usr, ownerHash, sha256, size] + self.backupAddMap(row) + + else : + + Sys.pwlog([(' File Already exist ! ' , Const.CLZ_1, True), + (' id : '.rjust(10,' ') , Const.CLZ_0), + (str(row[ImpraIndex.UID]) , Const.CLZ_1, True), + (' label : '.rjust(10,' ') , Const.CLZ_0), + (row[ImpraIndex.LABEL] , Const.CLZ_3, True)]) + + row = None + return row + + + @Log() + def checkSendIds(self, sendIds, subject): + """""" + lloc = [Io.bytes(str(data[0])) for mid, data in enumerate(sendIds)] + lsrv = self.idxu.ih.searchBySubject(subject,True) + return [ int(i) for i in set(lloc).difference(set(lsrv))] + + + @Log(Const.LOG_APP) + def editFile(self, key, label, category): + """""" + #~ Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True)]) + done = False + uid = self.idxu._getId(True) + uidx = self.idxu.conf.get('uid' ,'index') + if int(uid) != int(uidx) : self.idxu.get(True) + row = self.idxu.index.get(key) + if row is not None: + if row[ImpraIndex.LABEL]!=label or row[ImpraIndex.CATG]!=category: + + Sys.pwlog([(' Editing file ' , Const.CLZ_0), + (row[ImpraIndex.LABEL] , Const.CLZ_7), + (' [' , Const.CLZ_0), + (row[ImpraIndex.CATG] , Const.CLZ_4), + ('] to : ' , Const.CLZ_0), + (label if label is not None else row[ImpraIndex.LABEL] , Const.CLZ_3), + (' [' , Const.CLZ_0), + (category if category is not None else row[ImpraIndex.CATG], Const.CLZ_2), + (']' , Const.CLZ_0, True)]) + + done = self.idxu.index.edit(key, label, category) + + Sys.pwlog([(' done' if done else 'ko', Const.CLZ_2 if done else Const.CLZ_1, True)]) + if done : + Sys.pwlog([(' Updating index...', Const.CLZ_0, True)]) + self.idxu.update() return done - def clean(self): + + @Log(Const.LOG_APP) + def getFile(self, uid): """""" - rt = RuTime(eval(__CALLER__())) - self.ih.deleteBin() - if file_exists(self.fsplit.DIR_CACHE+'.~KirmahEnc'):remove(self.fsplit.DIR_CACHE+'.~KirmahEnc') - rt.stop() + #~ Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True)]) + done = False + key = self.idxu.index.getById(uid) + row = self.idxu.index.get(key) + filePath= None + try : + if row is not None : + account = self.idxu.switchFileAccount(row[self.idxu.index.ACCOUNT]) + km = Kirmah(row[self.idxu.index.KEY]) + hlst = km.ck.getHashList(key, row[self.idxu.index.PARTS], True) + ids = self.idxu.ih.searchBySubject(hlst['head'][2], True) + Sys.cli_emit_progress(0) + Sys.sleep(0.2) + Sys.pwlog([(' Downloading : ' , Const.CLZ_7), + (row[self.idxu.index.LABEL]+row[self.idxu.index.EXT] , Const.CLZ_2), + (' (' , Const.CLZ_0), + (Sys.readableBytes(row[self.idxu.index.SIZE]) , Const.CLZ_3), + (')' , Const.CLZ_0), + (' please wait...' , Const.CLZ_7, True)]) + + if len(ids) >= row[self.idxu.index.PARTS]: + self.getFileParts(row, ids) + + Sys.pwlog([(' Merging parts...', Const.CLZ_7, True)]) + Sys.cli_emit_progress(0) + Sys.sleep(0.2) + filePath = km.merge(hlst, self.deploy+row[self.idxu.index.CATG]+Sys.sep+row[self.idxu.index.LABEL], ext=row[self.idxu.index.EXT], uid=row[self.idxu.index.UID], dirs=self.inbox) + + + Sys.pwlog([(' Deployed as ' , Const.CLZ_7), + (filePath , Const.CLZ_2), + (' (' , Const.CLZ_0), + (Sys.readableBytes(Sys.getsize(filePath)) , Const.CLZ_3), + (') ' , Const.CLZ_0, True), + (' Checking integrity...' , Const.CLZ_7, True)]) + Sys.sleep(0.2) + sha256 = hash_sha256_file(filePath) + done = sha256==row[ImpraIndex.HASH] + done = True + + else: + print('incomplete') + + except Exception as e : + print(e) + Sys.pwlog([(' done' if done else 'ko', Const.CLZ_2 if done else Const.CLZ_1, True)]) + return done, filePath -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class MailBuilder ~~ - -class MailBuilder: - """A simple mail builder to create mails for ImpraIndex and parts attchments""" - - DOMAIN_NAME = 'impra.storage' - """Domain name used for from and to mail fields""" - - def __init__(self, salt=''): + def getFileParts(self, row, ids): """""" - self.salt = salt - - def getHashName(self, name): - """Return a simplified hash of specified name - :Returns: `str` - """ - return hash_sha256(self.salt+name)[0:12] + done = False + if len(ids) >= row[self.idxu.index.PARTS]: + for i, uid in enumerate(ids): + d = Sys.datetime.now() + self.idxu.ih.getAttachment(uid, self.inbox, True) + + Sys.pwlog([(' part ' , Const.CLZ_0), + (str(i+1).rjust(2, ' ') , Const.CLZ_2), + (' / ' , Const.CLZ_0), + (str(len(ids)) , Const.CLZ_3), + (' downloaded in ' , Const.CLZ_0), + (Sys.getDelta(d) , Const.CLZ_4, True)]) - def build(self, nameFrom, nameTo, subject, filePath): - """Build mail with attachment part - :Returns: 'email.message.Message' - """ - rt = RuTime(eval(__CALLER__('%s' % basename(filePath)))) - msg = MIMEMultipart() - msg['From'] = self.getHashName(nameFrom)+'@'+self.DOMAIN_NAME - msg['To'] = self.getHashName(nameTo)+'@'+self.DOMAIN_NAME - msg['Date'] = formatdate(localtime=True) - msg['Subject'] = Header(subject,'utf-8') - part = MIMEBase('application', 'octet-stream') - part.set_payload(open(filePath, 'rb').read()) - encode_base64(part) - part.add_header('Content-Disposition','attachment; filename="%s"' % basename(filePath)) - msg.attach(part) - rt.stop() - return msg - - def buildIndex(self, data): - """Build mail for ImpraIndex - :Returns: 'email.message.Message' - """ - rt = RuTime(eval(__CALLER__())) - msg = MIMEMultipart() - msg['From'] = self.getHashName('system')+'@'+self.DOMAIN_NAME - msg['To'] = self.getHashName('all')+'@'+self.DOMAIN_NAME - msg['Date'] = formatdate(localtime=True) - msg['Subject'] = Header(self.getHashName('index'),'utf-8') - msg.attach(MIMEText(data,_charset='utf-8')) - rt.stop() - return msg + Sys.cli_emit_progress(int((i+1)*100/len(ids))) + Sys.sleep(0.5) + + Sys.mkdir_p(self.deploy+row[self.idxu.index.CATG]) + Sys.cli_emit_progress(100) + + + @Log(Const.LOG_APP) + def getInfo(self, uid): + """""" + done = False + key = self.idxu.index.getById(uid) + row = self.idxu.index.get(key) + if row is not None : + account = self.idxu.switchFileAccount(row[self.idxu.index.ACCOUNT]) + km = Kirmah(row[self.idxu.index.KEY]) + hlst = km.ck.getHashList(key, row[self.idxu.index.PARTS], True) + + Sys.pwlog([('id '.rjust(14,' ')+': ' , Const.CLZ_0), + (str(row[ImpraIndex.UID]) , Const.CLZ_1, True), + ('hash '.rjust(14,' ')+': ' , Const.CLZ_0), + (row[ImpraIndex.HASH] , Const.CLZ_2, True), + ('name '.rjust(14,' ')+': ' , Const.CLZ_0), + (row[ImpraIndex.LABEL]+row[ImpraIndex.EXT] , Const.CLZ_7, True), + ('size '.rjust(14,' ')+': ' , Const.CLZ_0), + (Sys.readableBytes(row[ImpraIndex.SIZE]) , Const.CLZ_6, True), + ('category '.rjust(14,' ')+': ' , Const.CLZ_0), + (row[ImpraIndex.CATG] , Const.CLZ_5, True), + ('user '.rjust(14,' ')+': ' , Const.CLZ_0), + (self.idxu.index.getUser(row[ImpraIndex.USER]) , Const.CLZ_3), + (' ('+row[ImpraIndex.USER]+')' , Const.CLZ_5, True), + ('account '.rjust(14,' ')+': ' , Const.CLZ_0), + (self.idxu.conf.get('user' , 'imap', row[ImpraIndex.ACCOUNT]), Const.CLZ_4, True), + (Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True), + ('subject '.rjust(14,' ')+': ' , Const.CLZ_0), + (hlst['head'][2] , Const.CLZ_1, True)]) + + ids = self.idxu.ih.searchBySubject(hlst['head'][2], True) + + for i, uid in enumerate(ids): + if i < len(hlst['data']) : + + Sys.pwlog([('attach file '.rjust(14,' ')+': ' , Const.CLZ_0), + (hlst['data'][i][1]+Kirmah.EXT , Const.CLZ_2), + (' (' , Const.CLZ_0), + (str(int(uid)) , Const.CLZ_1), + (') (' , Const.CLZ_0), + (str(hlst['data'][i][4]) , Const.CLZ_3), + (')' , Const.CLZ_0, True)]) + else: + Sys.pwlog([(' Wrong id (to del)'.ljust(14,' ')+': ' , Const.CLZ_2), + (str(uid) , Const.CLZ_2, True)]) + return done + + + @Log(Const.LOG_APP) + def removeFile(self, uid): + """""" + #~ Sys.pwlog([(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True)]) + done = False + key = self.idxu.index.getById(uid) + row = self.idxu.index.get(key) + if row is not None: + account = self.idxu.switchFileAccount(row[self.idxu.index.ACCOUNT]) + Sys.pwlog([(' Removing... plz wait. ' , Const.CLZ_7)]) + km = Kirmah(row[self.idxu.index.KEY]) + hlst = km.ck.getHashList(key, row[self.idxu.index.PARTS], True) + ids = self.idxu.ih.searchBySubject(hlst['head'][2], True) + self.idxu.ih.delete(ids, True, True) + self.idxu.ih.clearTrash() + self.idxu.switchFileAccount(self.idxu.conf.profile) + self.idxu.get(True) + self.idxu.index.rem(key) + done = self.idxu.update() + return done, key diff --git a/impra/crypt.py b/impra/crypt.py index 20c3be9..c011508 100644 --- a/impra/crypt.py +++ b/impra/crypt.py @@ -1,33 +1,35 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#-*- coding: utf-8 -*- +# impra/cryot.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# This file is part of ImpraStorage. +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org # -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# ImpraStorage 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. +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . # -# You should have received a copy of the GNU General Public License -# along with ImpraStorage. If not, see . # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ package crypt ~~ +# ~~ module crypt ~~ from base64 import urlsafe_b64encode, b64decode from binascii import b2a_base64, a2b_base64 diff --git a/impra/gui.py b/impra/gui.py new file mode 100755 index 0000000..b6533c2 --- /dev/null +++ b/impra/gui.py @@ -0,0 +1,858 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/gui.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module gui ~~ + +from gi.repository import Gtk, GObject, GLib, Gdk, Pango +from kirmah.crypt import KeyGen, Kirmah, KirmahHeader, ConfigKey, BadKeyException, b2a_base64, a2b_base64, hash_sha256_file +from impra.ui import Gui, CliThread +from impra import conf +from psr.sys import Sys, Io, Const, init +from psr.log import Log +from psr.ini import IniFile +from psr.imap import ImapConfig, ImapHelper, BadLoginException, BadHostException +import impra.conf as conf +from impra.core import ImpraStorage, ImpraConf +from impra.index import ImpraIndex +from impra.ini import KiniFile +from threading import Condition, RLock, Event +from queue import Queue +from impra.app import ImpraThread + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class AppGui ~~ + +class AppGui(Gui): + + start = False + poslog = 0 + + TASK_RUN = False + BAD_CONF = False + PENDING_ID = 0 + + @Log(Const.LOG_BUILD) + def __init__(self, wname='window1'): + """""" + super().__init__(wname) + + + @Log(Const.LOG_UI) + def on_start(self): + """""" + Sys.g.GUI_PRINT_STDOUT = False + Sys.g.GUI = True + init(conf.PRG_NAME, False, Sys.getpid(), True, Const.LOG_ALL) + self.conf = ImpraConf(KiniFile('impra2.ini')) + self.populate_profiles() + self.populate_config() + self.taskLabel = ImpraThread.TASK_LABEL + self.taskStock = ImpraThread.TASK_STOCK + self.progressbar = self.get('progressbar1') + self.textview = self.get('textview1') + try : + self.textview.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1.0)) + self.textview.modify_font(Pango.font_description_from_string ('DejaVu Sans Mono Book 11' if Sys.isUnix() else 'Lucida Conosle 11')) + except : + pass + self.textbuffer = self.textview.get_buffer() + self.tags = self.buildTxtTags(self.textbuffer) + self.initWidgetByThread('impra-1', self.textview, self.textbuffer, self.progressbar, self.tags) + self.initWidgetByThread('MainThread', self.textview, self.textbuffer, self.progressbar, self.tags) + self.tree = self.get('treeview1') + self.tree.connect('row-activated', self.on_row_select) + self.tree.get_selection().connect('changed', self.on_tree_selection_changed) + self.launch_thread(self.on_ended) + + self.searchCatg = self.get('comboboxtext1') + self.searchUser = self.get('comboboxtext4') + self.searchAccount = self.get('comboboxtext5') + self.filterIds = None + self.index = None + self.taskList = {} + self.threads_work = [False, False] + + + def on_ended(self, thread=None, ref=None): + """""" + print('ended') + + + def on_delete_event(self, data=None, data2=None): + """""" + print('on_delete_event') + self.thimpra.stop() + if self.thimpra2 is not None : + self.thimpra2.stop() + + + + # ~~ INDEX ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_tree_selection_changed(self, selection): + """""" + model, treeiter = selection.get_selected() + hasSel = treeiter != None + if hasSel : + """""" + self.unselect_actions(hasSel, True) + + + @Log(Const.LOG_UI) + def unselect_actions(self, sensible=False, exceptAdd=False): + """""" + self.get('button5').set_sensitive(sensible) + self.get('button6').set_sensitive(sensible) + if not exceptAdd : self.get('button12').set_sensitive(sensible) + self.get('button7').set_sensitive(sensible) + + + @Log(Const.LOG_UI) + def build_treeview_column(self): + """""" + if self.tree.get_column(0)==None : + self.get('label5').set_label(' '+self.conf.get('user', 'imap',defaultValue='')) + self.get('label7').set_label(' '+self.conf.get('uid', 'index',defaultValue='')) + self.get('label9').set_label(' '+self.conf.get('box', 'imap',defaultValue='')) + self.get('label10').set_label(' '+self.conf.get('date', 'index',defaultValue='')) + #~ self.update_progress(10, 10) + #~ self.progressbar.set_fraction(10/100.0) + css = [('#900000', 'white'), (None, '#82C59A'), (None, '#D4D4D4'), (None, '#D290C5'), (None, '#D87475'), (None, '#C58473'), (None, '#A7A2A2'), (None, '#EAD049'), (None, '#81AFDD')] + for i, s in enumerate(ImpraIndex.COLS[:-1]): + r = Gtk.CellRendererText() + + #~ r.set_property('family', 'mono') + r.set_property('size-set', True) + r.set_property('size-points', 11) + if css[i][0] is not None : + r.set_property('cell-background', css[i][0]) + if css[i][1] is not None : + r.set_property('foreground', css[i][1]) + if i==0: + r.set_property('weight', 800) + if i==1 or i==0 or i==3: + r.set_property('family', 'DejaVu Sans Mono') + #~ r.set_property('size-points', 10) + if i==2: + r.set_property('width', 350) + if i==6: + r.set_property('width', 90) + if i==7: + r.set_property('width', 120) + """""" + cl = Gtk.TreeViewColumn(s, r, text=i) + cl.set_attributes(r, text=i, cell_background=ImpraIndex.ACCOUNT+1+(1 if i==0 else 0)) + cl.set_sort_column_id(i) + cl.set_resizable(True) + cl.connect('clicked', self.on_column_click) + self.tree.append_column(cl) + + + @Log(Const.LOG_UI) + def populate_index(self): + """""" + self.BLOCK_REPOPULATE = False + if self.index is None or self.index != self.thimpra.impst.idxu.index : + self.index = self.thimpra.impst.idxu.index + data = sorted([(self.index.dic.get(k),k) for i, k in enumerate(self.index.dic) if not k.startswith(self.index.SEP_KEY_INTERN)], reverse=False, key=lambda lst:lst[0][self.index.UID]) + store = self.get('treestore1') + store.clear() + drow = None + i = 0 + tsize = 0 + psize = 0 + accounts = self.thimpra.impst.idxu.getAccountList() + allCatg, allUsers, allAccounts, tmp = [], [], [ accounts[a] for a in accounts], '' + for row, key in data : + + tsize += row[self.index.SIZE] + if self.filterIds==None or row[self.index.UID] in self.filterIds: + + drow = list(row[:-1]) + psize += row[self.index.SIZE] + + if drow[self.index.CATG] not in allCatg : + allCatg.append(drow[self.index.CATG]) + tmp = self.index.getUser(drow[self.index.USER]) + if tmp not in allUsers : + allUsers.append(tmp) + drow[self.index.PARTS] = ('%s' % drow[self.index.PARTS]).rjust(2,' ') + drow[self.index.UID] = ('%s' % drow[self.index.UID]).rjust(4,' ') + drow[self.index.HASH] = '%s' % drow[self.index.HASH][0:6]+'…' + drow[self.index.SIZE] = Sys.readableBytes(row[self.index.SIZE]).rjust(11,' ')+' ' + drow[self.index.USER] = self.index.getUser(drow[self.index.USER]) + drow[self.index.ACCOUNT] = self.index.acclist[drow[self.index.ACCOUNT]] + drow.append('#222222' if i%2!=0 else '#1C1C1C') + drow.append('#640000' if i%2!=0 else '#900000') + store.append(None, drow) + i += 1 + self.progressbar.set_fraction(10+i*90/len(data)/100.0) + + # repopulate only if not search + if self.filterIds==None : + #~ Sys.pwlog([(' Populating search filters...', Const.CLZ_0, True)]) + self.populate_search_filters(allCatg, allUsers, allAccounts) + + self.get('checkbutton1').set_sensitive(True) + + self.get('label12').set_label(' '+(Sys.readableBytes(psize)+' / ' if psize != tsize else '')+Sys.readableBytes(tsize)) + + self.get('button9').set_sensitive(True) + self.get('button12').set_sensitive(True) + return False + + + # ~~ force redrawing odd/even tree rows ~~~~~~~ + + @Log(Const.LOG_UI) + def on_column_click(self, treeviewcolumn, user_param1=None): + """""" + for i, row in enumerate(self.tree.get_model()): + row[self.index.ACCOUNT+1] = '#222222' if i%2!=0 else '#1C1C1C' + row[self.index.ACCOUNT+2] = '#640000' if i%2!=0 else '#900000' + + + @Log(Const.LOG_UI) + def populate_search_filters(self, catgs=(), users=(), accounts=()): + """""" + lst = ((Gtk.ListStore(str), sorted(catgs) , '-- All categories --', self.searchCatg), + (Gtk.ListStore(str), sorted(users) , '-- All users --' , self.searchUser), + (Gtk.ListStore(str), sorted(accounts), '-- All accounts --' , self.searchAccount)) + for data in lst : + data[0].append([data[2]]) + for item in data[1]: + data[0].append([item]) + data[3].set_model(data[0]) + data[3].set_active(0) + + + # ~~ SEARCH ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + def on_search(self, btn, data=None): + """""" + self.get('entry16').set_sensitive(btn.get_active()) + self.get('comboboxtext1').set_sensitive(btn.get_active()) + self.get('comboboxtext4').set_sensitive(btn.get_active()) + self.get('comboboxtext5').set_sensitive(btn.get_active()) + self.get('button8').set_sensitive(btn.get_active()) + if not btn.get_active() : + self.filterIds = None + if not self.BLOCK_REPOPULATE : + self.populate_index() + + + def get_search_filter(self, comboName): + """""" + value = '' + citer = self.get(comboName).get_active_iter() + if citer != None: + model = self.get(comboName).get_model() + if model[citer][0] != model[model.get_iter_first()][0] : + value = model[citer][0] + return value + + + def on_launch_search(self, btn, data=None): + """""" + self.on_search(self.get('checkbutton1')) + slabel = self.get('entry16').get_text() + scatg = self.get_search_filter('comboboxtext1') + suser = self.get_search_filter('comboboxtext4') + sacc = self.get_search_filter('comboboxtext5') + + matchIdsAcc = None + matchIdsCatg = None + matchIdsUser = None + matchIdsCrit = None + matchIds = self.index.getByPattern(slabel) + + if sacc != '' : + matchIdsAcc = [] + for k in self.index.acclist.keys(): + if self.index.acclist[k] == sacc : + print('key : '+str(k)+' - sacc : '+str(sacc)) + matchIdsAcc = self.index.getByAccount(k) + break + matchIds = self.index.getIntersection(matchIds,matchIdsAcc) + + if scatg != '' : matchIdsCatg = self.index.getByCategory(scatg) + if suser != '' : matchIdsUser = self.index.getByUser(suser) + if scatg != '' : matchIdsCrit = self.index.getIntersection(matchIdsCatg,matchIdsUser) if suser != '' else matchIdsCatg + elif suser != '' : matchIdsCrit = matchIdsUser + else : matchIdsCrit = matchIds + + if matchIdsCrit is not None : + self.filterIds = self.index.getIntersection(matchIds,matchIdsCrit) + + self.populate_index() + + + + # ~~ ADD ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_add_file(self, btn, data=None): + """""" + self.get('dialog1').show() + + + @Log(Const.LOG_UI) + def on_add_file_set(self, fc, data=None): + """""" + fname, ext = Sys.getFileExt( fc.get_filename()) + catg = self.index.getAutoCatg(ext) + if (self.get('entry2').get_text() == '' or self.get('checkbutton2').get_active()) and catg!='none' : + self.get('entry2').set_text(catg) + + + @Log(Const.LOG_UI) + def add_file(self, btn, data=None): + """""" + fileName = self.get('filechooserbutton1').get_filename() + label = self.get('entry1').get_text() + category = self.get('entry2').get_text() + btn.set_sensitive(False) + self.on_cancel_add(btn) + self.launch_action(ImpraThread.TASK_ADD, (fileName, label, category)) + + + @Log(Const.LOG_UI) + def on_cancel_add(self, btn, data=None): + """""" + self.get('dialog1').hide() + self.get('filechooserbutton1').set_filename('') + self.get('entry1').set_text('') + self.get('entry2').set_text('') + btn.set_sensitive(True) + + + # ~~ EDIT ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + # double-clic row + @Log(Const.LOG_UI) + def on_row_select(self, treeview, path, view_column, data=None): + """""" + store = self.get('treestore1') + treeiter = store.get_iter(path) + self.editUid = store.get_value(treeiter, self.index.UID) + self.editKey = self.index.getById(self.editUid) + self.editLabel = store.get_value(treeiter, self.index.LABEL) + self.editCategory = store.get_value(treeiter, self.index.CATG) + self.on_edit_file() + + + @Log(Const.LOG_UI) + def on_edit_file(self): + """""" + self.get('label39').set_text(' Editing `'+self.editLabel+'` ('+str(self.editUid)+')') + self.get('entry17').set_text(self.editLabel) + self.get('entry18').set_text(self.editCategory) + self.get('dialog2').show() + + + @Log(Const.LOG_UI) + def on_edit(self, btn, data=None): + """""" + label = self.get('entry17').get_text() + category = self.get('entry18').get_text() + btn.set_sensitive(False) + self.on_cancel_edit(btn) + if label != self.editLabel or category != self.editCategory : + self.launch_action(ImpraThread.TASK_EDIT, (self.editKey, label, category)) + + + @Log(Const.LOG_UI) + def on_cancel_edit(self, btn, data=None): + """""" + self.get('dialog2').hide() + self.get('entry17').set_text('') + self.get('entry18').set_text('') + btn.set_sensitive(True) + + + def get_selected_uid(self): + """""" + model, treeiter = self.tree.get_selection().get_selected() + return int(model[treeiter][0]) + + # ~~ INFOS ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_file_infos(self, btn, data=None): + """""" + self.launch_action(ImpraThread.TASK_INFOS, self.get_selected_uid()) + + + # ~~ GET ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_file_download(self, btn, data=None): + """""" + self.launch_action(ImpraThread.TASK_GET, self.get_selected_uid()) + + + # ~~ REMOVE ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_file_delete(self, btn, data=None): + """""" + self.launch_action(ImpraThread.TASK_REMOVE, self.get_selected_uid()) + + + # ~~ REFRESH ACTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def on_refresh(self, btn, data=None): + """""" + self.BLOCK_REPOPULATE = True + self.get('treestore1').clear() + if btn is not None : + Sys.cli_emit_progress(0) + self.get('checkbutton1').set_sensitive(False) + self.get('checkbutton1').set_active(False) + self.unselect_actions() + self.get('button9').set_sensitive(False) + self.launch_action(ImpraThread.TASK_REFRESH, []) + else : + self.populate_index() + + + def on_remove_pending(self, btn, keyPending, execute=False, keepRetry=True): + """""" + print('on_remove_pending') + if keyPending in self.taskList : + action, params, keep = self.taskList[keyPending] + print('keyPending : '+str(keyPending)) + print('execute : '+str(execute)) + print('keepRetry : '+str(keepRetry)) + print('action : '+str(action)) + print('params : '+str(params)) + print('keep : '+str(keep)) + if keep and keepRetry : + pass + else : + self.taskList.pop(keyPending) + self.get('box19').remove(btn) + if len(self.taskList)==0: + self.get('paned1').set_position(5000) + if execute : + self.launch_action(action, params) + + + def populate_pending_task(self, action, params, execute=False): + """""" + print('populate_pending_task') + self.get('paned1').set_position(1030) + keyPending = 'pending-'+str(self.PENDING_ID) + keep = action==ImpraThread.TASK_ADD_RETRY + self.taskList[keyPending] = (action, params, keep) + w = Gtk.Button(label='Pending task '+self.taskLabel[action], image=Gtk.Image(stock=self.taskStock[action])) + w.set_tooltip_text('click to '+('execute' if keep else 'remove')+' task '+self.taskLabel[action]+('' if keep else ' ('+str(params)+')')) + w.set_property('halign', Gtk.Align.START) + w.connect('clicked', self.on_remove_pending, keyPending, execute, not keep) + w.set_name(keyPending) + self.get('box19').pack_start(w, False, False, 1) + w.show() + #~ self.get('box19').show() + + + def launch_action(self, action, params): + """""" + if not self.threads_work[0] : + self.threads_work[0] = True + self.taskQueue.put_nowait((action, params, self.PENDING_ID)) + + else: + self.populate_pending_task(action, params) + + self.PENDING_ID += 1 + + #~ self.do_pending_task() + + + #~ self.threads_work[1] = True + #~ print('task thread 2') + #~ self.taskQueue.put_nowait((action, params)) + #~ if self.thimpra2 is None : + #~ self.thimpra2 = ImpraThread(self.evtStop, self.taskQueue, self.condition, self.conf, 'impra-2') + #~ self.thimpra2.connect_signals(self.signalsDef, None) + #~ self.thimpra2.start() + + #~ keyPending = 'pending-'+str(self.PENDING_ID) + #~ ltask = ['wait','add','get','refresh','remove','infos','edit','exit'] + #~ self.taskList[keyPending] = (action, params) + #~ w = Gtk.Button(label='remove pending task '+ltask[action]+' ['+str(params)+']', image=Gtk.Image(stock=Gtk.STOCK_REMOVE)) + #~ w.connect('clicked', self.on_remove_pending, keyPending) + #~ self.get('box19').pack_start(w, True, True, 5) + #~ self.PENDING_ID += 1 + + + def do_pending_task(self): + """""" + print('do_pending_task') + wlist = self.get('box19').get_children() + if len(wlist)>0 : + keyPending = wlist[0].get_name() + self.on_remove_pending(wlist[0], keyPending, True) + + + + + + + # ~~ ACTIONS CALLBACK ~~~~~~~~~~~~~~~~~~~~~~~~~ + + def on_has_add_retry(self, thread, usrData): + """""" + print('on_has_add_retry') + self.populate_pending_task(ImpraThread.TASK_ADD_RETRY, None, True) + self.PENDING_ID += 1 + + + def on_common_ending_action(self, thread=None, done=None, key=None, usrData=None): + """""" + if thread.name == 'impra-1' : + self.threads_work[0] = False + self.do_pending_task() + self.on_progress(thread) + + + def on_need_config(self, thread=None, usrData=None): + """""" + self.BAD_CONF = True + if thread.name == 'impra-1' : + self.threads_work[0] = False + self.do_pending_task() + self.get('notebook1').set_current_page(1) + self.get('label42').set_text('Something is wrong in your configuration') + + + def on_index_refresh(self, thread=None, ref=None, updated=None, usrData=None): + """""" + self.on_common_ending_action(thread) + if self.tree.get_column(0) == None : + self.build_treeview_column() + self.on_refresh(None) + + + # ~~ IMPRA THREAD ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Log(Const.LOG_UI) + def launch_thread(self, on_complete, userData=None): + self.evtStop = Event() + self.taskQueue = Queue() + self.condition = Condition(RLock()) + self.thimpra2 = None + self.thimpra = ImpraThread(self.evtStop, self.taskQueue, self.condition, self.conf, 'impra-1') + self.signalsDef = (('completed' , on_complete), + ('interrupted' , self.on_interrupted), + ('progress' , self.on_progress), + ('fileadded' , self.on_common_ending_action), + ('fileget' , self.on_common_ending_action), + ('fileinfos' , self.on_common_ending_action), + ('fileremoved' , self.on_common_ending_action), + ('fileedited' , self.on_common_ending_action), + ('fileaddretry' , self.on_common_ending_action), + ('indexrefreshed', self.on_index_refresh), + ('hasaddretry' , self.on_has_add_retry), + ('needconfig' , self.on_need_config)) + self.thimpra.connect_signals(self.signalsDef, userData) + self.thimpra.start() + + + # ~~ CONFIG PAGE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + def populate_profiles(self): + """""" + l = Gtk.ListStore(str) + l.append(['current profile']) + sections = sorted(self.conf.ini.getSections()) + if len(sections) > 0: + ap = self.conf.ini.get('profile') + sep = '' + for p in sections: + if p != ap and p != '': + l.append([p]) + self.get('comboboxtext3').set_model(l) + self.get('comboboxtext3').set_active(0) + + + def on_profile_change(self, combo, data=None): + """""" + citer = combo.get_active_iter() + if citer != None: + model = combo.get_model() + profile = model[citer][0] + if profile == 'current profile' : + profile = None + self.populate_config(profile) + + + def on_gen_new_key(self, btn, data=None): + """""" + kg = KeyGen(int(self.get('spinbutton1').get_value())) + self.get('entry14').set_text(kg.key) + self.get('entry15').set_text(kg.mark) + # 18 + + + def on_delete_profile(self, btn, data=None): + """""" + p = self.get('entry3').get_text() + if p != self.conf.profile : + self.conf.remProfile(p) + else : + """""" + print('is current') + model = self.get('comboboxtext3').get_model() + citer = self.get('comboboxtext3').get_active_iter() + del(model[citer]) + self.get('comboboxtext3').set_active(0) + # 17 + + + def on_edit_new_profile(self, btn, data=None): + """""" + self.populate_config('new') + self.get('spinbutton1').set_sensitive(True) + self.get('spinbutton2').set_sensitive(True) + self.get('button18').set_sensitive(True) + + + def on_hide_pwd(self, btn, data=None): + """""" + self.get('entry12').set_visibility(not btn.get_active()) + self.get('entry13').set_visibility(not btn.get_active()) + + + @Log(Const.LOG_UI) + def populate_config(self, p=None): + """""" + self.get('button18').set_sensitive(False) + profile = self.conf.profile if p is None else p + self.get('label41').set_label('') + self.get('label42').set_label('') + self.get('label2').set_label('') + self.get('entry3').set_text(profile) + + self.get('entry4').set_text(self.conf.get('name', 'infos', profile,'')) + self.get('entry5').set_text(self.conf.get('host', 'imap', profile,'')) + self.get('entry6').set_text(self.conf.get('port', 'imap', profile,'')) + self.get('entry7').set_text(self.conf.get('user', 'imap', profile,'')) + self.get('entry13').set_text(self.conf.get('pass', 'imap', profile,'')) + + m = self.getMultiAccount() + + l = Gtk.ListStore(str) + l.append(['*new']) + for a in m:l.append([a]) + self.get('comboboxtext2').set_model(l) + self.get('comboboxtext2').set_active(0) + + self.get('entry8').set_text('') + self.get('entry9').set_text('') + self.get('entry10').set_text('') + self.get('entry11').set_text('') + self.get('entry12').set_text('') + + key = self.conf.get('key', 'keys', profile,'') + hasKey = key != '' + self.get('entry14').set_text(key) + self.get('entry14').set_sensitive(False) + self.get('entry15').set_text(self.conf.get('mark', 'keys', profile,'')) + self.get('entry15').set_sensitive(False) + self.get('spinbutton1').set_value(len(key)) + self.get('spinbutton1').set_sensitive(p is not None or self.BAD_CONF) + self.get('spinbutton2').set_value(128) + self.get('spinbutton2').set_sensitive(p is not None or self.BAD_CONF) + self.get('button18').set_sensitive(p is not None or self.BAD_CONF) + self.get('button17').set_sensitive(p is not None or self.BAD_CONF) + + + def getMultiAccount(self): + """""" + if self.conf.ini.has('multi' , self.conf.profile+'.imap'): + m = self.conf.ini.get('multi', self.conf.profile+'.imap') + else : m = None + if m is None : m = [] + else : m = m.split(',') + m = [x for x in m if x] + return sorted(m) + + + def on_add_multiaccount(self, btn, data=None): + """""" + profile = self.get('entry8').get_text() + if profile != '' : + m = self.getMultiAccount() + if profile is not None and profile is not '' : + canAdd = self.on_test_account(None) + if canAdd : + if profile not in m : + m.append(profile) + + self.conf.ini.set('multi', ','.join(m), self.conf.profile+'.imap') + self.conf.ini.set('host' , self.get('entry9').get_text() , profile+'.imap') + self.conf.ini.set('user' , self.get('entry11').get_text() , profile+'.imap') + self.conf.ini.set('pass' , self.get('entry12').get_text() , profile+'.imap') + self.conf.ini.set('port' , self.get('entry10').get_text() , profile+'.imap') + self.conf.ini.save() + self.conf.ini.print(profile) + self.get('entry9').set_text('') + self.get('entry10').set_text('') + self.get('entry11').set_text('') + self.get('entry12').set_text('') + self.populate_config() + + else : + self.get('label2').set_label('can\'t add : '+self.get('label2').get_label()) + else : + self.get('label2').show() + self.get('label2').set_label('need an account name') + + + def test_imap(self, names): + """""" + self.get(names[4]).set_label('testing...') + conf = ImapConfig(self.get(names[0]).get_text(), self.get(names[1]).get_text(), self.get(names[2]).get_text(), self.get(names[3]).get_text()) + done = False + if not ((conf.host != '' and conf.host is not None) and (conf.user != '' and conf.user is not None) and (conf.port != '' and conf.port is not None) and (conf.pwd != '' and conf.pwd is not None)): + msg = 'nodata' + else : + print('test_imap') + try : + msg = '' + ih = ImapHelper(conf, 'INBOX', True) + done = True + except Exception as e: + msg = e.__name__ + print(e) + + self.get(names[4]).set_label('test ok' if done else 'test failed ! ['+msg+']') + self.get(names[4]).show() + return done + + + def on_test_main_imap(self, btn, data=None): + """""" + return self.test_imap(('entry5', 'entry7', 'entry13', 'entry6', 'label41')) + + + def on_test_account(self, btn, data=None): + """""" + return self.test_imap(('entry9', 'entry11', 'entry12', 'entry10', 'label2')) + + + def on_multiaccount_change(self, combo, data=None): + """""" + citer = combo.get_active_iter() + self.get('button15').set_sensitive(True) + if citer != None: + model = combo.get_model() + account = model[citer][0] + self.get('entry8').set_text('') + self.get('label2').set_label('') + if account == '*new' : + self.get('button2').set_label('add account') + self.get('button4').set_sensitive(False) + if account is not None and account is not '' and account != '*new': + self.get('button2').set_label('edit account') + self.get('button4').set_sensitive(True) + self.get('entry8').set_text(account) + self.get('entry9').set_text(self.conf.get('host', 'imap', account,'')) + self.get('entry10').set_text(self.conf.get('port', 'imap', account,'')) + self.get('entry11').set_text(self.conf.get('user', 'imap', account,'')) + self.get('entry12').set_text(self.conf.get('pass', 'imap', account,'')) + + + def on_remove_multiaccount(self, btn, data=None): + """""" + m = self.getMultiAccount() + profile = self.get('entry8').get_text() + citer = self.get('comboboxtext2').get_active_iter() + if citer != None: + model =self.get('comboboxtext2').get_model() + account = model[citer][0] + + #~ if profile != account + if account in m : + m.remove(account) + self.conf.remProfile(account) + self.conf.ini.set('multi', ','.join(m), self.conf.profile+'.imap') + self.populate_config() + + + def on_save_profile(self, btn, data=None): + """""" + profile = self.get('entry3').get_text() + if profile != '' : + usr = self.get('entry4').get_text() + if usr != '' : + canSave = self.on_test_main_imap(None) + if canSave : + if not(self.get('entry14').get_text() != '' and self.get('entry15').get_text()!= '') : + self.on_gen_new_key(None) + + self.get('spinbutton1').set_sensitive(False) + self.get('spinbutton2').set_sensitive(False) + + self.conf.ini.set('name', usr , profile+'.infos') + + self.conf.ini.set('key' , self.get('entry14').get_text(), profile+'.keys') + self.conf.ini.set('mark', self.get('entry15').get_text(), profile+'.keys') + self.conf.ini.set('salt', '-¤-ImpraStorage-¤-' , profile+'.keys') + + self.conf.ini.set('host', self.get('entry5').get_text() , profile+'.imap') + self.conf.ini.set('user', self.get('entry7').get_text() , profile+'.imap') + self.conf.ini.set('pass', self.get('entry13').get_text(), profile+'.imap') + self.conf.ini.set('port', self.get('entry6').get_text() , profile+'.imap') + self.conf.ini.set('box' , '__impra2__' , profile+'.imap') + + self.conf.ini.set('profile', profile, 'main') + self.conf.ini.print(profile) + self.conf.ini.print('main') + + self.conf.ini.save() + if self.BAD_CONF : + self.BAD_CONF = False + self.on_delete_event() + self.launch_thread(self.on_ended) + else : + self.get('label42').set_label('can\'t save : '+self.get('label41').get_label()) + + else : + self.get('label42').set_label('user name is empty') + + else : + self.get('label42').set_label('profile name is empty') + #~ self.get('entry3').set_text(profile) diff --git a/impra/imap.py b/impra/imap.py deleted file mode 100644 index b47aaf1..0000000 --- a/impra/imap.py +++ /dev/null @@ -1,453 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# This file is part of ImpraStorage. -# -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. -# -# ImpraStorage 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 ImpraStorage. If not, see . - - -from binascii import b2a_base64, a2b_base64 -from codecs import register, StreamReader, StreamWriter -from email import message_from_bytes -from email.header import decode_header -from email.message import Message -from imaplib import IMAP4_SSL, Time2Internaldate -from os.path import join -from re import search, split -from time import time, sleep - -from impra.util import __CALLER__, RuTime, bstr, stack, Clz, DEBUG, mprint - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ package imap ~~ - -def _seq_encode(seq,l): - """""" - if len(seq) > 0 : - l.append('&%s-' % str(b2a_base64(bytes(''.join(seq),'utf-16be')),'utf-8').rstrip('\n=').replace('/', ',')) - elif l: - l.append('-') - -def encode(s): - """""" - l, e, = [], [] - for c in s : - if ord(c) in range(0x20,0x7e): - if e : _seq_encode(e,l) - e = [] - l.append(c) - if c == '&' : l.append('-') - else : - e.append(c) - if e : _seq_encode(e,l) - return ''.join(l) - -def encoder(s): - """""" - e = bytes(encode(s),'utf-8') - return e, len(e) - -def _seq_decode(seq,l): - """""" - d = ''.join(seq[1:]) - pad = 4-(len(d)%4) - l.append(str(a2b_base64(bytes(d.replace(',', '/')+pad*'=','utf-16be')),'utf-16be')) - -def decode(s): - """""" - l, d = [], [] - for c in s: - if c == '&' and not d : d.append('&') - elif c == '-' and d: - if len(d) == 1: l.append('&') - else : _seq_decode(d,l) - d = [] - elif d: d.append(c) - else: l.append(c) - if d: _seq_decode(d,l) - return ''.join(l) - -def decoder(s): - """""" - d = decode(str(s,'utf-8')) - return d, len(d) - -def _codec_imap4utf7(name): - """""" - if name == 'imap4-utf-7': - return (encoder, decoder, Imap4Utf7StreamReader, Imap4Utf7StreamWriter) - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class Imap4Utf7StreamWriter ~~ - -class Imap4Utf7StreamReader(StreamReader): - """""" - - def decode(self, s, errors='strict'): - """""" - return decoder(s) - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class Imap4Utf7StreamWriter ~~ - -class Imap4Utf7StreamWriter(StreamWriter): - """""" - - def decode(self, s, errors='strict'): - """""" - return encoder(s) - - -register(_codec_imap4utf7) - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class ImapConfig ~~ - -class ImapConfig: - """""" - - def __init__(self, host, port, user, pwd): - """""" - self.host = host - self.port = port - self.user = user - self.pwd = pwd - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class ImapHelper ~~ - -class ImapHelper: - """""" - - K_HEAD, K_DATA = 0, 1 - """""" - OK = 'OK' - """""" - KO = 'NO' - """""" - ENCODING = 'utf-8' - """""" - REG_SATUS = r'^"(\w*)" \(([^\(]*)\)' - """""" - NO_SELECT = '\\Noselect' - """""" - CHILDREN = '\\HasChildren' - """""" - NO_CHILDREN = '\\HasNoChildren' - """""" - BOX_BIN = '[Gmail]/Corbeille' - """""" - - def __init__(self, conf, box='INBOX', boxBin=None): - """""" - rt = RuTime(eval(__CALLER__('conf,"'+str(box)+'"'))) - self.srv = IMAP4_SSL(conf.host,conf.port) - self.conf = conf - status, resp = self.srv.login(conf.user,conf.pwd) - if DEBUG.level <= DEBUG.ALL : - mprint(status) - mprint(resp) - if status == self.OK: - self.rootBox = box - if boxBin is None : - if search('gmail.com',conf.host) is None: - self.BOX_BIN = 'Trash' - if box != None : - status, resp = self.srv.select(self.rootBox) - if status == self.KO : - self.createBox(self.rootBox) - self.srv.select(self.rootBox) - else : - raise BadLoginException('cannot login with '+conf.user) - rt.stop() - - def status(self,box='INBOX'): - """""" - status, resp = self.srv.status(box, '(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)') - if status == 'OK' : - data = search(self.REG_SATUS,bstr(resp[self.K_HEAD])) - l = split(' ',data.group(2)) - dic = {'BOX' : data.group(1)} - for i in range(len(l)): - if i%2 == 0 : dic[l[i]] = int(l[i+1]) - else : dic = {} - return dic - - def countSeen(self, box='INBOX'): - """""" - s = self.status(box) - return s['MESSAGES']-s['UNSEEN'] - - def countUnseen(self, box='INBOX'): - """""" - return self.status(box)['UNSEEN'] - - def countMsg(self, box='INBOX'): - """""" - return self.status(box)['MESSAGES'] - - def _ids(self, box='INBOX', search='ALL', charset=None, byUid=False): - """""" - status, resp = self.srv.select(box) - if status == self.KO : - self.createBox(box) - self.srv.select(box) - status, resp = self.srv.search(charset, '(%s)' % search) - return split(' ',bstr(resp[self.K_HEAD])) - - def idsUnseen(self, box='INBOX', charset=None): - """""" - return self._ids(box,'UNSEEN', charset) - - def idsMsg(self, box='INBOX', charset=None): - """""" - return self._ids(box,'ALL', charset) - - def idsSeen(self, box='INBOX', charset=None): - """""" - return self._ids(box,'NOT UNSEEN', charset) - - def listBox(self, box='INBOX', pattern='*'): - """""" - status, resp = self.srv.list(box,pattern) - l = [] - for r in resp : - name = bstr(r).split(' "/" ') - l.append((name[0][1:-1].split(' '),decode(name[1][1:-1]))) - return l - - def createBox(self, box): - """""" - rt = RuTime(eval(__CALLER__(box))) - status, resp = self.srv.create(encode(box)) - rt.stop() - return status==self.OK - - def deleteBox(self, box): - """""" - rt = RuTime(eval(__CALLER__(box))) - status, resp = self.srv.delete(encode(box)) - rt.stop() - return status==self.OK - - def subject(self, mid, byUid=False): - """""" - status, resp = self.fetch(mid, '(UID BODY[HEADER.FIELDS (SUBJECT)])', byUid) - subject = decode_header(str(resp[self.K_HEAD][1][9:-4], 'utf-8'))[0] - s = subject[0] - if subject[1] : - s = str(s,subject[1]) - return s - - def search(self, query, byUid=False): - if byUid : - status, resp = self.srv.uid('search', None, query) - else : - status, resp = self.srv.search(None, query) - ids = [m for m in resp[0].split()] - return ids - - def searchBySubject(self, subject, byUid=False): - return self.search('(SUBJECT "%s")' % subject, byUid) - - def getUid(self, mid): - """""" - value = '' - status, resp = self.srv.fetch(mid, '(UID)') - if status==self.OK : - value = resp[0][len(str(mid))+3:-1] - return value - - def fetch(self, mid, query, byUid=False): - """""" - if not byUid : - status, resp = self.srv.fetch(mid, query) - else: - status, resp = self.srv.uid('fetch', mid, query) - return status, resp - - def headerField(self, field, mid, byUid=False): - """""" - value = '' - status, resp = self.fetch(mid, '(UID BODY[HEADER.FIELDS (%s)])' % field.upper(), byUid) - if status==self.OK and resp[0]!=None: - value = str(resp[0][1][len(field)+2:-4],'utf-8') - return value - - def email(self, mid, byUid=False): - """""" - status, resp = self.fetch(mid,'(UID RFC822)', byUid) - if status == self.OK and resp[0]!=None: - msg = message_from_bytes(resp[0][1]) - else : - msg = None - return msg - - def deleteBin(self): - """""" - rt = RuTime(eval(__CALLER__()),DEBUG.INFO) - self.srv.select(self.BOX_BIN) - ids = self.search('ALL',True) - if len(ids) > 0 and ids[0]!='' and ids[0]!=None: - mprint() - #print(str(ids[0],'utf-8').split()) - for mid in ids: - #~ uid = bytes(mid) - #~ mprint(type(mid)) - #~ mprint(mid) - #status, resp = self.srv.store(mid, '+FLAGS', '\\Deleted') - status, resp = self.srv.uid('store', mid, '+FLAGS (\\Deleted)' ) - mprint(' ',end='') - Clz.print(' deleting msg ',Clz.fgN7+Clz.bg1, False) - Clz.print(str(int(mid)) ,Clz.bg1+Clz.fgB3) - if DEBUG.level <= DEBUG.NOTICE: - mprint(status) - mprint(resp) - self.srv.expunge() - mprint() - self.srv.select(self.rootBox) - rt.stop() - - def delete(self, mid, byUid=False, expunge=True): - """""" - rt = RuTime(eval(__CALLER__('%i' % int(mid)))) - status = None - if int(mid) > 0 : - if byUid: - status, resp = self.srv.uid( 'store', mid, '+FLAGS (\\Deleted)' ) - else : - status, resp = self.srv.store(mid, '+FLAGS', 'Deleted') - - Clz.print(' flag msg ' , Clz.fgn7, False) - Clz.print(str(int(mid)), Clz.fgB1, False) - Clz.print(' as deleted', Clz.fgn7) - - if expunge : - Clz.print('\n expunge, waiting server...\n', Clz.fgB1) - self.srv.expunge() - sleep(0.5) - - rt.stop() - return status == self.OK - - def downloadAttachment(self, msg, toDir='./', byUid=False): - """""" - rt = RuTime(eval(__CALLER__('%i' % int(msg))),DEBUG.INFO) - if not isinstance(msg, Message) : - msg = self.email(msg,byUid) - for part in msg.walk(): - filename = part.get_filename() - if filename != None and DEBUG.level <= DEBUG.NOTICE : mprint(filename) - if part.get_content_maintype() == 'multipart' or not filename : continue - fp = open(join(toDir, filename), 'wb') - #print(part.get_payload(decode=True)[::-1]) - fp.write(part.get_payload(decode=True)) - fp.close() - rt.stop() - - def send(self, msg, box='INBOX'): - """""" - rt = RuTime(eval(__CALLER__())) - mid = None - date = Time2Internaldate(time()) - status, resp = self.srv.append(box, '\Draft', date, bytes(msg,'utf-8')) - if status==self.OK: - if DEBUG.level <= DEBUG.NOTICE: - mprint(status) - mprint(resp) - m = search(b']', resp[0]) - mid = str(resp[0],'utf-8')[11:-(len(resp[0])-m.start())].split(' ') - rt.stop() - return mid - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class BadLoginException ~~ - -class BadLoginException(BaseException): - pass - -if __name__ == '__main__': - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - iconf = ImapConfig("imap.gmail.com", 993, 'gpslot.001', '__gpslot#22') - ih = ImapHelper(iconf,'__SMILF') - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - print('\n--------------------------------------------------------------------') - print('-- STATUS DEFAULT BOX --') - print(str(ih.status())) - print('-- STATUS BOX __SMILF --') - print(str(ih.status('__SMILF'))) - print('-- UNSEEN COUNT --') - print(str(ih.countUnseen('__SMILF'))) - print('-- SEEN COUNT --') - print(str(ih.countSeen('__SMILF'))) - print('-- MESSAGE COUNT --') - print(str(ih.countMsg('__SMILF'))) - print('-- UNSEEN IDS --') - print(ih.idsUnseen('__SMILF')) - print('-- MESSAGES IDS --') - print(ih.idsMsg('__SMILF')) - print('-- SEEN IDS --') - lunseen = ih.idsSeen('__SMILF') - print(lunseen) - print('-- LIST BOX --') - lb = ih.listBox('') - print(lb[5][1]) - print('-- SUBJECT ID 1 --') - print(ih.subject(lunseen[0])) - print('-- BODY ID 1 --') - #print(ih.body(lunseen[0])) - print('-- EMAIL ID 1 --') - # 'partial', ('1', 'RFC822', 1, 1024)), - #status, resp = ih.srv.fetch(lunseen[0],'(UID RFC822)') - #status, resp = ih.srv.fetch('4','(UID body[header.fields (from to subject date)])') - #status, resp = ih.srv.fetch(lunseen[1],'(UID RFC822.SIZE)') - #status, resp = ih.srv.fetch(lunseen[1],'(UID RFC822.HEADER)') - #status, resp = ih.srv.fetch(lunseen[1],'(UID BODYSTRUCTURE)') - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - #msg = ih.email(lunseen[0]) - #print(type(msg)) - #print(msg) - #print('-- ATTACHMENT ID 1 --') - #ih.downloadAttachment(lunseen[0]) - - - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # ['MIME-Version', 'Received', 'Date', 'Message-ID', 'Subject', 'From', 'To', 'Content-Type'] - print('-- CREATE BOX __SMILF/böx --') - print(ih.createBox("__SMILF/böx")) - print('-- DELETE BOX böx --') - print(ih.deleteBox("böx")) - #~ OK - #~ [b'Success'] - #~ True - #~ NO - #~ [b'[ALREADYEXISTS] Duplicate folder name b\xc3\xb6x (Failure)'] - #~ True - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/impra/index.py b/impra/index.py new file mode 100755 index 0000000..80958ba --- /dev/null +++ b/impra/index.py @@ -0,0 +1,730 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/index.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module index ~~ + +from binascii import a2b_base64 +from collections import Counter +from json import dumps as jdumps, loads as jloads +from re import split as regsplit, match as regmatch, compile as regcompile, search as regsearch +from psr.sys import Sys, Io, Const +from psr.log import Log +from psr.imap import BadLoginException +from impra.mail import MailBuilder +from kirmah.crypt import Kirmah, BadKeyException + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImpraIndex ~~ + +class ImpraIndex: + """A representation of the index stored on the server""" + + SEP_KEY_INTERN = '§' + """Separator used for internal key such categories""" + UID = 0 + """""" + HASH = 1 + """""" + LABEL = 2 + """""" + SIZE = 3 + """""" + PARTS = 4 + """""" + EXT = 5 + """""" + USER = 6 + """""" + CATG = 7 + """""" + ACCOUNT = 8 + """""" + KEY = 9 + """""" + FILE_BINARY = 'b' + """""" + FILE_CRYPT = 'c' + """""" + COLS = ('ID','HASH','LABEL','SIZE','PART','TYPE','USER','CATEGORY','ACCOUNT','KEY') + """""" + KEY_EXT = '.key' + + + @Log(Const.LOG_BUILD) + def __init__(self, key, path, dicCategory={}, accountList={}, emit=False): + """Initialize the index with rsa and encoded data + + :Parameters: + `key` : str + appropriate key to decrypt/encrypt data + `mark` : str + appropriate mark to check correct key + `encdata` : str + initial content of the index encrypted with Kirmah Algorythm + and representing a dic index as json string + """ + self.pathPlain = path[:-len(Kirmah.EXT)] + self.keyPath = self.pathPlain+self.KEY_EXT + self.path = path + Io.set_data(self.keyPath, key) + self.dic = {} + self.acclist = accountList + encdata = Io.get_data(path, True) if Io.file_exists(path) else b'' + + if encdata == b'' : + self.dic = {} + self.id = 1 + else : + self.dic = self.decrypt(path) + l = [self.dic[k][self.UID] for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] + if len(l) > 0 : + self.id = max(l)+1 + else: self.id = 1 + for k in dicCategory : + if k == 'users' : + for k1 in dicCategory[k]: + if self.SEP_KEY_INTERN+k in self.dic: + if k1 not in self.dic[self.SEP_KEY_INTERN+k]: + self.dic[self.SEP_KEY_INTERN+k][k1] = dicCategory[k][k1] + else : + if not self.SEP_KEY_INTERN+k in self.dic: + self.dic[self.SEP_KEY_INTERN+k] = dicCategory[k] + + + @Log() + def add(self, key, label, count, ext='', usr='', cat='', fhash='', size=0, account=''): + """Add an entry to the index + """ + if self.get(fhash) == None : + self.dic[fhash] = (self.id, fhash, label, size, count, ext, usr, cat, account, key) + self.id +=1 + return self.id-1 + else : + Sys.dprint(label+' already exist') + + + @Log() + def addUser(self, nameFrom, hashName): + """""" + if not self.hasUser(hashName): + self.dic[self.SEP_KEY_INTERN+'users'][hashName] = nameFrom + + + @Log(Const.LOG_ALL) + def hasUser(self, hashName): + """""" + if not self.SEP_KEY_INTERN+'users' in self.dic: + self.dic[self.SEP_KEY_INTERN+'users'] = {} + return hashName in self.dic[self.SEP_KEY_INTERN+'users'] + + + @Log(Const.LOG_ALL) + def getUser(self, hashName): + """""" + usrName = 'Anonymous' + if self.hasUser(hashName): + usrName = self.dic[self.SEP_KEY_INTERN+'users'][hashName] + return usrName + + + def getAllCatgs(self): + """""" + return self.dic[self.SEP_KEY_INTERN+'catg'].split(',') if self.SEP_KEY_INTERN+'catg' in self.dic else [] + + + @Log() + def rem(self,label): + """Remove the selected label from the index""" + self.dic.pop(label, None) + + + @Log() + def getAutoCatg(self,ext): + """""" + catg = 'none' + if regsearch('\.(jpg|jpeg|gif|png)',ext): + catg = 'images' + elif regsearch('\.(txt|doc|odt|csv|pdf)',ext): + catg = 'doc' + elif regsearch('\.(sh|py|c|cpp|h|php|bash)',ext): + catg = 'code' + elif regsearch('\.(mp4|avi|mpg|mpeg|flv|ogv)',ext): + catg = 'films' + elif regsearch('\.(mp3|ogg|flac)',ext): + catg = 'music' + elif regsearch('\.(zip|7z|tar|gz|rar|bz|xz|jar|bz2)',ext): + catg = 'archives' + return catg + + + @Log() + def isEmpty(self): + """""" + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] + return len(r) == 0 + + + @Log() + def getLabel(self, key): + """Get label corresponding to key in the index + :Returns: `str`|None label + """ + value = '' + row = self.get(key) + if row is not None : + value = row[self.LABEL] + + + @Log() + def get(self, key): + """Get the corresponding key in the index + :Returns: `tuple` row + """ + row = None + if key in self.dic : row = self.dic.get(key) + return row + + + @Log() + def edit(self, key, label=None, category=None): + """Get the corresponding key in the index + :Returns: `tuple` row + """ + done = False + row = self.dic[key] + r = list(row) + if label != None : + try : + name, ext = Sys.getFileExt(label) + r[self.LABEL] = name + if ext is not '' : + r[self.EXT] = ext + except Exception as e : + r[self.LABEL] = label + if category != None : + r[self.CATG] = category + self.dic[key] = tuple(r) + done = row != self.dic[key] + return done + + + @Log() + def getById(self, sid): + """Get the corresponding id in the index + :Returns: `str`|None key + """ + l = None + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and self.dic[k][self.UID] == int(sid)] + if len(r)==1 : l = r[0] + return l + + + @Log() + def fixAccount(self,account): + """""" + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] + for k in r: + t = list(self.dic[k]) + if len(t)-1 < self.ACCOUNT: + t.append(account) + else: + t[self.ACCOUNT] = account + self.dic[k] = tuple(t) + + + @Log() + def getLightestAccount(self,l): + """""" + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] + t = {} + for k in r: + if not self.dic[k][self.ACCOUNT] in t: t[self.dic[k][self.ACCOUNT]] = self.dic[k][self.SIZE] + else: t[self.dic[k][self.ACCOUNT]] += int(self.dic[k][self.SIZE]) + profile = None + r = [] + for a in l: + if not a in t : + profile = a + break + else : + r.append((t[a],a)) + if profile is None : + d = sorted(r, reverse=False, key=lambda lst:lst[0]) + profile = d[0][1] + return profile + + + @Log() + def fixDuplicateIds(self): + """Get corresponding keys of duplicate ids in the index + :Returns: `str`|None key + """ + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)] + l = [(k,self.dic[k][self.UID]) for k in r] + l2 = [k[1] for k in l] + if len(l2)> 0 : + mxid = max(l2) + l3 = [x for x, y in Counter(l2).items() if y > 1] + d = [k[0] for k in l if any( k[1] == v for v in l3)] + for k in d: + mxid += 1 + #mprint(self.dic[k]) + t = list(self.dic[k]) + t[self.UID] = mxid + #mprint(t) + self.dic[k] = tuple(t) + self.id = mxid+1 + else: + self.id = 1 + d = () + return len(d)>0 + + + @Log() + def getByLabel(self,label): + """Get the corresponding label in the index + :Returns: `str`|None key + """ + l = None + r = [k for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and self.dic[k][self.LABEL] == label] + if len(r)==1: l = r[0] + return l + + + @Log() + def getByPattern(self,pattern): + """Get ids corresponding to label matching the pattern in the index + :Returns: `[uid]`|None matchIds + """ + l = None + r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(pattern,self.dic[k][self.LABEL]) is not None ] + l = [self.dic[k][self.UID] for k in r] + return l + + + @Log() + def getByCategory(self,category): + """Get ids corresponding to category + :Returns: `[uid]`|None matchIds + """ + l = None + r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(category,self.dic[k][self.CATG]) is not None ] + l = [self.dic[k][self.UID] for k in r] + return l + + + @Log() + def getByAccount(self,account): + """Get ids corresponding to account + :Returns: `[uid]`|None matchIds + """ + l = None + r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and account==self.dic[k][self.ACCOUNT] ] + l = [self.dic[k][self.UID] for k in r] + return l + + + @Log() + def getByUser(self,user): + """Get ids corresponding to category + :Returns: `[uid]`|None matchIds + """ + l = None + r = [ k for i,k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN) and regsearch(user,self.getUser(self.dic[k][self.USER])) is not None ] + l = [self.dic[k][self.UID] for k in r] + return l + + + @Log() + def getIntersection(self,list1, list2): + """Get ids intercept list1 and list2 + :Returns: `[uid]`|None matchIds + """ + l = [ i for i in set(list1).intersection(set(list2))] + return l + + + @Log() + def encrypt(self, fromPath=None): + """""" + if fromPath is None : + fromPath = self.pathPlain + Sys.pwlog([(' Encrypt Index... ' , Const.CLZ_0, True)]) + Io.set_data(fromPath, jdumps(self.dic)) + call = ' '.join([Sys.executable, 'kirmah-cli.py', 'enc', '-qfj2' if Sys.isUnix() else '-qf', fromPath, '-z', '-r', '-m', '-o', fromPath+Kirmah.EXT, '-k', self.keyPath ]) + #~ print(call) + Sys.sysCall(call) + Io.removeFile(fromPath) + Sys.pwlog([(' done', Const.CLZ_2, True)]) + return Io.get_data(fromPath+Kirmah.EXT, True) + + + @Log(Const.LOG_APP) + def decrypt(self, fromPath=None): + """""" + done = False + try : + if fromPath is None : + fromPath = self.path + toPath = fromPath[:-len(Kirmah.EXT)] if fromPath.endswith(Kirmah.EXT) else fromPath+'.dump' + if Io.file_exists(fromPath) : + Sys.pwlog([(' Decrypt Index... ' , Const.CLZ_0, True)]) + call = ' '.join([Sys.executable, 'kirmah-cli.py', 'dec', '-qfj2' if Sys.isUnix() else '-qf', fromPath, '-z', '-r', '-m', '-o', toPath, '-k', self.keyPath ]) + print(call) + Sys.sysCall(call) + data = jloads(Io.get_data(toPath)) + Io.removeFile(toPath) + else : + data = {} + done = True + except ValueError as e: + raise BadKeyException(e) + Sys.pwlog([(' done'if done else ' ko' , Const.CLZ_2 if done else Const.CLZ_1, True)]) + return data + + + @Log(Const.LOG_ALL) + def print(self,order='ID', matchIds=None): + """Print index content as formated bloc""" + #~ Sys.clear() + #~ Cli.print_header() + #~ AbstractCli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + inv = order.startswith('-') + if inv : order = order[1:] + orderIndex = self.COLS.index(order) + if orderIndex is None : orderIndex = self.COLS.index('ID') + d = sorted([(self.dic.get(k),k) for i, k in enumerate(self.dic) if not k.startswith(self.SEP_KEY_INTERN)], reverse=inv, key=lambda lst:lst[0][orderIndex]) + + sizeid = 1+Sys.ceil(len(str(len(d)))) + if sizeid < 3 : sizeid = 3 + addsize = abs(3 - sizeid); + + sort = '^' if inv else '_' #'ↆ' + + space = (4+addsize, 8, 38, 10, 3, 5, 11, 24-addsize, 13) + for i, s in enumerate(self.COLS[:-1]): + symb, c = sort if order == s else ' ', Sys.Clz.BG4+Sys.Clz.fgB7 if order != s else Sys.Clz.BG7+Sys.Clz.fgB4 + Sys.echo ((' '+s+symb).ljust(space[i],' ') , c, False, False) + Sys.echo('', c) + Sys.echo(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN, Sys.CLZ_HEAD_LINE) + + a = '' + tsize = 0 + psize = 0 + acc = {} + wrap = '… ' if Sys.isUnix() else '/ ' + for v,k in d : + if matchIds==None or v[self.UID] in matchIds: + if v[self.SIZE] == '' : v[self.SIZE] = 0 + a = '' + Sys.echo(str(v[self.UID]).rjust(sizeid+1,' ') , Sys.Clz.bg1+Sys.Clz.fgB7, False) + Sys.echo(' '+str(k).ljust(9,' ')[0:6]+wrap , Sys.Clz.fgN2, False) + if len(v[self.LABEL])>36 : a = wrap + try: + Sys.echo(str(v[self.LABEL][:36]+a).ljust(38,' ') , Sys.Clz.fgN7, False) + except: + pass + j = 0 + for c in v[self.LABEL][:36] : + try: + Sys.echo(str(c) , Sys.Clz.fgN7, False, False) + except: + Sys.echo('?' , Sys.Clz.fgN7, False, False) + j += 1 + Sys.echo(''.ljust(38-j,' ') , Sys.Clz.fgN7, False, False) + + a = '' + Sys.echo(Sys.readableBytes(v[self.SIZE])[:9].rjust(9,' ')+' '*2 , Sys.Clz.fgN5, False) + Sys.echo(str(v[self.PARTS]).rjust(2 ,'0') +' '*2 , Sys.Clz.fgN1, False) + Sys.echo(str(v[self.EXT][:6]).ljust(7,' ') , Sys.Clz.fgn3, False) + Sys.echo(self.getUser(str(v[self.USER])).ljust(11 ,' ') , Sys.Clz.fgn7, False) + #~ Sys.echo(str(v[self.CATG]).ljust(30 ,' ') , Clz.fgN3) + if len(v[self.CATG])>22 : a = wrap + Sys.echo(str(v[self.CATG][:22]+a).ljust(24 ,' ') , Sys.Clz.fgN3, False) + a = '' + if len(v)-2==self.ACCOUNT: + if v[self.ACCOUNT] in self.acclist : + if len(self.acclist[v[self.ACCOUNT]])>11 : a = '…' + Sys.echo(str(self.acclist[v[self.ACCOUNT]][:11]+a).ljust(12 ,' ') , Sys.Clz.fgN4) + else : + Sys.echo(str(v[self.ACCOUNT][:11]+'!').ljust(12 ,' ') , Sys.Clz.fgN4) + if v[self.ACCOUNT] in acc : + acc[v[self.ACCOUNT]] += int(v[self.SIZE]) + else : acc[v[self.ACCOUNT]] = int(v[self.SIZE]) + else: Sys.dprint() + + psize += int(v[self.SIZE]) + tsize += int(v[self.SIZE]) + if len(d)==0: + Sys.echo(' empty', Sys.Clz.fgB1) + + Sys.echo(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN, Sys.CLZ_HEAD_LINE) + c = Sys.Clz.fgB2 + if psize != tsize : c = Sys.Clz.fgB7 + Sys.echo(' size : ', Sys.Clz.fgB3, False) + Sys.echo(Sys.readableBytes(psize)[:9].rjust(9,' '), c, False) + if psize != tsize : + Sys.echo(' / ', Sys.Clz.fgB3, False) + Sys.echo(Sys.readableBytes(tsize), Sys.Clz.fgB2, False) + Sys.dprint() + Sys.echo(Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN, Sys.CLZ_HEAD_LINE) + + #~ Sys.echo(' '*4+'[', Sys.Clz.fgB7, False) + #~ sep = '' + #~ for k in acc: + #~ if k!= '': + #~ Sys.echo(sep+k,Sys.Clz.fgB3,False) + #~ Sys.echo(':',Sys.Clz.fgB7,False) + #~ Sys.echo(Sys.readableBytes(acc[k]),Sys.Clz.fgB2,False) + #~ if sep=='':sep = ',' + #~ Sys.echo(']', Sys.Clz.fgB7, False) + #~ mprint() + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class IndexUpdater ~~ + +class IndexUpdater: + """""" + @Log(Const.LOG_BUILD) + def __init__(self, ih, conf, wkdir='./', emit=None): + """""" + self.idx = None + self.index = None + self.emit = emit + self.delids = [] + self.ih = ih + self.conf = conf + self.pathIdx = wkdir+'.index.'+self.conf.profile+Kirmah.EXT + self.mb = MailBuilder(self.conf.get('salt','keys')) + self.rootBox = self.conf.get('box','imap') + self.get() + + + @Log(Const.LOG_DEBUG) + def _getId(self, notAssign=False): + """""" + idx = None + ids = self.ih.searchBySubject(self.mb.getHashName('index'),True) + if len(ids) > 0 and int(ids[0]) >= 0 : + idx = ids[-1] + if not notAssign: self.delids = ids[:-1] + if not notAssign: + self.idx = idx + return idx + + + @Log() + def get(self, forceRefresh=False): + """""" + self.switchFileAccount(self.conf.profile) + index = None + uid = self.conf.get('uid' ,'index') + date = self.conf.get('date' ,'index') + tstamp = self.conf.get('time' ,'index') + refresh = forceRefresh + delta = None if tstamp is None else Sys.datetime.now() - Sys.datetime.strptime(tstamp[:-7], '%Y-%m-%d %H:%M:%S') + if not refresh and tstamp is not None and delta < Sys.timedelta(minutes = 3) : + # getFromFile + if uid != None and Io.file_exists(self.pathIdx): # int(self.idx) == int(uid) + self.idx = uid + Sys.pwlog([(' Get index from cache ' , Const.CLZ_7), + ('(' , Const.CLZ_0), + (str(int(self.idx)) , Const.CLZ_2), + (')' , Const.CLZ_0, True)]) + else: refresh = True + else: refresh = True + self.irefresh = refresh + if refresh : + Sys.pwlog([(' Checking index...', Const.CLZ_0, True)]) + self._getId() + if self.idx : + if int(self.idx) != int(uid) or not Io.file_exists(self.pathIdx): + Sys.pwlog([(' Refreshing index (local:', Const.CLZ_0), + (str(int(uid)) , Const.CLZ_2), + (' / remote:' , Const.CLZ_0), + (str(int(self.idx)) , Const.CLZ_1), + (')' , Const.CLZ_0, True)]) + + date = self.ih.headerField(self.idx, 'date', True) + self.conf.sets((['uid' , str(int(self.idx)) , 'index'], + ['date' , date , 'index'], + ['time' , str(Sys.datetime.now()), 'index'])) + self._saveLocalIndex() + else : + Sys.pwlog([(' Get index from cache ' , Const.CLZ_7), + ('(' , Const.CLZ_0), + (str(int(self.idx)) , Const.CLZ_2), + (')' , Const.CLZ_0, True)]) + self.conf.set('time',str(Sys.datetime.now()),'index') + self.build() + + + @Log() + def build(self): + Sys.pwlog([(' Reading index, please wait...', Const.CLZ_7, True)]) + self.index = ImpraIndex(self.conf.get('key','keys'), self.pathIdx, self.getIndexDefaultCatg(), self.getAccountList()) + defUsers = self.conf.get('users','catg') + if not ImpraIndex.SEP_KEY_INTERN+'users' in self.index.dic: + self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'] = {} + for k in self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users']: + if self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'][k] not in [ i.strip() for i in defUsers.split(',')]: + self.conf.set('users',defUsers+', '+self.index.dic[ImpraIndex.SEP_KEY_INTERN+'users'][k],'catg') + + + @Log(Const.LOG_DEBUG) + def getAccountList(self): + l = {} + pl = self.conf.get('multi','imap') + if pl is not None and len(pl)>0 : + pl = pl.split(',') + if len(pl) > 0: + if not self.conf.profile in pl: + pl.append(self.conf.profile) + else : pl = [self.conf.profile] + for p in pl : l[p] = self.conf.get('user','imap',p) + return l + + + @Log(Const.LOG_DEBUG) + def getIndexDefaultCatg(self): + """""" + usrName = self.conf.get('name','infos') + defUsers = self.conf.get('users','catg').split(',') + dic = {'catg':self.conf.get('types','catg'), 'users':{ ('%s' % self.mb.getHashName('all')) : 'all', ('%s' % self.mb.getHashName(usrName)) : usrName}} + for u in defUsers : + dic['users'][('%s' % self.mb.getHashName(u.strip()))] = u.strip() + return dic + + + @Log(Const.LOG_DEBUG) + def _saveLocalIndex(self): + """""" + if not self.idx : self._getId() + if self.idx : + msg = self.ih.getEmail(self.idx, True) + content = b'' + for part in msg.walk(): + content += part.get_payload(decode=True) + Io.set_data(self.pathIdx, a2b_base64(content), True) + + + @Log() + def removeLocal(self): + """""" + self.conf.rem('*','index') + self.conf.save() + self.idx = None + Io.removeFile(self.pathIdx) + + + @Log() + def remove(self): + """""" + self._getId() + try: + if self.idx!= None : self.delids.append(Io.bytes(self.idx)) + self.ih.delete(self.delids, True) + self.idx = None + except Exception as e : + Sys.dprint('error : ') + Sys.dprint(e) + + self.ih.clearTrash() + self.removeLocal() + + + @Log(Const.LOG_APP) + def update(self): + """""" + self.switchFileAccount(self.conf.profile) + try: + if self.idx != None : + if not isinstance(self.idx,bytes): + self.idx = Io.bytes(self.idx) + self.delids.append(self.idx) + except Exception as e : + Sys.dprint('error : ') + Sys.dprint(e) + + self.index.fixDuplicateIds() + #~ self.index.fixAccount('gmail5') + self.index.encrypt() + msgIndex = self.mb.buildIndex(self.pathIdx) + _, self.idx = self.ih.send(msgIndex.as_string(), self.rootBox) + date = self.ih.headerField(self.idx, 'date', True) + self.conf.sets((['uid' , self.idx , 'index'], + ['date' , date , 'index'], + ['time' , str(Sys.datetime.now()), 'index'])) + + Sys.pwlog([(' Index updated (' , Const.CLZ_0), + (str(int(self.idx)) , Const.CLZ_2), + (') ' , Const.CLZ_0), + (str(date) , Const.CLZ_7, True)]) + + try : + self.ih.delete(self.delids, True) + except : + Sys.dprint('error : ') + Sys.dprint(e) + self.ih.clearTrash() + return True + + @Log() + def switchFileAccount(self, profile=None, force=False): + """""" + pl = self.conf.get('multi','imap') + if pl is not None and len(pl)>0 : + pl = pl.split(',') + if len(pl) > 0: + if not self.conf.profile in pl: + pl.append(self.conf.profile) + iconf = self.ih.conf + account = self.conf.get('user','imap',profile) + if True or iconf.user != account : + # reinit + iconf.user = None + try : + if profile is None : profile = self.index.getLightestAccount(pl) + if profile in pl : + iconf.user = self.conf.get('user','imap',profile) + iconf.pwd = self.conf.get('pass','imap',profile) + iconf.host = self.conf.get('host','imap',profile) + iconf.port = self.conf.get('port','imap',profile) + self.ih.switchAccount(iconf, self.rootBox, force) + except BadLoginException as e: + Sys.dprint('Error : ') + Sys.dprint(e) + Sys.dprint('check your connection or your imap config for profile '+profile) + if profile is None: profile = self.conf.profile + return profile diff --git a/impra/ini.py b/impra/ini.py new file mode 100755 index 0000000..526b558 --- /dev/null +++ b/impra/ini.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/ini.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module ini ~~ + +from re import split as regsplit +from psr.sys import Sys, Io, Const +from psr.ini import IniFile +from psr.log import Log +from kirmah.crypt import KeyGen + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class IniFile ~~ + +class KiniFile(IniFile): + """Read and write inifile""" + + @Log(Const.LOG_BUILD) + def __init__(self, path, keyPath=None): + """""" + self.path = path + self.dic = {} + self.keyPath = path+'.key' if keyPath is None else keyPath + if not Io.file_exists(self.keyPath) : + kg = KeyGen() + Io.set_data(self.keyPath, kg.key) + if not Io.file_exists(path) : + self.set('profile' , 'default', 'main') + self.set('key' ,kg.key,'default.keys') + self.set('mark' ,kg.mark,'default.keys') + self.set('salt' ,'-*-ImpraStorage-*-','default.keys') + self.save() + self.read() + + + @Log() + def save(self,path=None,notAssign=False): + """""" + path = path if path is not None else self.path + Io.set_data(path, '# last updated : '+str(Sys.datetime.now())+Const.LF+self.toString()) + call = ' '.join(['python3', 'kirmah-cli.py', 'enc', '-qf', path, '-z', '-r', '-m', '-o', path+'.kmh', '-k', self.keyPath ]) + Sys.sysCall(call) + Io.removeFile(path) + if not notAssign : self.path = path + + + @Log() + def read(self): + """""" + try: + call = ' '.join([Sys.executable, 'kirmah-cli.py', 'dec', '-qf', self.path+'.kmh', '-z', '-r', '-m', '-o', self.path, '-k', self.keyPath ]) + Sys.sysCall(call) + with Io.rfile(self.path, False) as fi: + csection = 'main' + self.dic[csection] = {} + for l in fi: + l = l.rstrip().lstrip() + if len(l) > 0 and not l[0]=='#' : + d = regsplit(' *= *', l , 1) + if len(d)> 1: + self.dic[csection][d[0]] = d[1] if d[1] is not None else '' + elif len(l)>0 and l[0]=='[': + csection = l.strip('[]') + self.dic[csection] = {} + Io.removeFile(self.path) + except IOError : + pass + + + @Log() + def delete(self): + Io.removeFile(self.path+'.kmh') + self.dic = {} diff --git a/impra/mail.py b/impra/mail.py new file mode 100755 index 0000000..032fd78 --- /dev/null +++ b/impra/mail.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/mail.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module mail ~~ +from binascii import b2a_base64 +from email.encoders import encode_base64 +from email.header import Header +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formatdate +from psr.log import Log +from psr.sys import Io, Sys, Const +from kirmah.crypt import hash_sha256 + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class MailBuilder ~~ + +class MailBuilder: + """A simple mail builder to create mails for ImpraIndex and parts attAchments""" + + DOMAIN_NAME = 'impra.storage' + """Domain name used for from and to mail fields""" + + @Log(Const.LOG_BUILD) + def __init__(self, salt=''): + """""" + self.salt = salt + + + @Log(Const.LOG_DEBUG) + def getHashName(self, name): + """Return a simplified hash of specified name + :Returns: `str` + """ + return hash_sha256(self.salt+name)[0:12] + + + @Log() + def build(self, nameFrom, nameTo, subject, filePath): + """Build mail with attachment part + :Returns: 'email.message.Message' + """ + msg = MIMEMultipart() + msg['From'] = self.getHashName(nameFrom)+'@'+self.DOMAIN_NAME + msg['To'] = self.getHashName(nameTo)+'@'+self.DOMAIN_NAME + msg['Date'] = formatdate(localtime=True) + msg['Subject'] = Header(subject,'utf-8') + part = MIMEBase('application', 'octet-stream') + part.set_payload(open(filePath, 'rb').read()) + encode_base64(part) + part.add_header('Content-Disposition','attachment; filename="%s"' % Sys.basename(filePath)) + msg.attach(part) + return msg + + + @Log() + def buildIndex(self, fromPath): + """Build mail for ImpraIndex + :Returns: 'email.message.Message' + """ + msg = MIMEText(Io.str(b2a_base64(Io.get_data(fromPath, True))), 'plain', 'utf-8') + msg['From'] = self.getHashName('system')+'@'+self.DOMAIN_NAME + msg['To'] = self.getHashName('all')+'@'+self.DOMAIN_NAME + msg['Date'] = formatdate(localtime=True) + msg['Subject'] = Header(self.getHashName('index'),'utf-8') + return msg diff --git a/impra/ui.py b/impra/ui.py new file mode 100755 index 0000000..03036d6 --- /dev/null +++ b/impra/ui.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# impra/ui.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module ui ~~ + +from gi.repository import Pango +from gi.repository.Gdk import threads_enter, threads_leave +from gi.repository.Gtk import AboutDialog, Builder, main as main_enter, main_quit, MessageDialog, MessageType, ButtonsType, ResponseType, PackType +from gi.repository.GdkPixbuf import Pixbuf +from gi.repository.GObject import threads_init, GObject, idle_add, SIGNAL_RUN_LAST, TYPE_NONE, TYPE_STRING, TYPE_FLOAT, TYPE_BOOLEAN +from threading import Thread, current_thread, enumerate as thread_enum +from multiprocessing import Event +from psr.sys import Sys, Io, Const +from psr.log import Log +from impra import conf +from impra.cli import Cli + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Gui ~~ + +class Gui(): + + + @Log(Const.LOG_BUILD) + def __init__(self, wname): + """""" + threads_init() + self.wname = wname + self.builder = Builder() + self.builder.add_from_file(conf.PRG_GLADE_PATH) + self.builder.connect_signals(self) + self.widgetByThread = {} + self.win = self.get(wname) + self.win.connect('destroy', self.onDeleteWindow) + self.win.connect('delete-event', self.onDeleteWindow) + self.win.set_title(conf.PRG_NAME+' v'+conf.PRG_VERS) + self.win.show_all() + self.on_start() + main_enter() + + + @Log(Const.LOG_DEBUG) + def buildTxtTags(self, textbuffer): + tags = {} + tags[Const.CLZ_TIME] = textbuffer.create_tag(Const.CLZ_TIME , foreground="#208420", weight=Pango.Weight.BOLD) + tags[Const.CLZ_SEC] = textbuffer.create_tag(Const.CLZ_SEC , foreground="#61B661", weight=Pango.Weight.BOLD) + tags[Const.CLZ_DEFAULT] = textbuffer.create_tag(Const.CLZ_DEFAULT , foreground="#FFEDD0") + tags[Const.CLZ_IO] = textbuffer.create_tag(Const.CLZ_IO , foreground="#EB3A3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_FUNC] = textbuffer.create_tag(Const.CLZ_FUNC , foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_CFUNC] = textbuffer.create_tag(Const.CLZ_CFUNC , foreground="#EBB33A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_DELTA] = textbuffer.create_tag(Const.CLZ_DELTA , foreground="#397BE8", weight=Pango.Weight.BOLD) + tags[Const.CLZ_ARGS] = textbuffer.create_tag(Const.CLZ_ARGS , foreground="#A1A1A1") + tags[Const.CLZ_ERROR] = textbuffer.create_tag(Const.CLZ_ERROR , background="#830005", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_ERROR_PARAM] = textbuffer.create_tag(Const.CLZ_ERROR_PARAM , background="#830005", foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_WARN] = textbuffer.create_tag(Const.CLZ_WARN , background="#A81459", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_WARN_PARAM] = textbuffer.create_tag(Const.CLZ_WARN_PARAM , background="#A81459", foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_PID] = textbuffer.create_tag(Const.CLZ_PID , background="#5B0997", foreground="#E4C0FF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_CPID] = textbuffer.create_tag(Const.CLZ_CPID , background="#770997", foreground="#F4CDFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_SYMBOL] = textbuffer.create_tag(Const.CLZ_SYMBOL , background="#61B661", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_OK] = textbuffer.create_tag(Const.CLZ_OK , background="#167B3B", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_KO] = textbuffer.create_tag(Const.CLZ_KO , background="#7B1716", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_TITLE] = textbuffer.create_tag(Const.CLZ_TITLE , foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_TASK] = textbuffer.create_tag(Const.CLZ_TASK , foreground="#61B661", weight=Pango.Weight.BOLD) + tags[Const.CLZ_ACTION] = textbuffer.create_tag(Const.CLZ_ACTION , background="#3F8C5C", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_INIT] = textbuffer.create_tag(Const.CLZ_INIT , background="#1F566D", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_APP] = textbuffer.create_tag(Const.CLZ_HEAD_APP , background="#2B5BAB", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_SEP] = textbuffer.create_tag(Const.CLZ_HEAD_SEP , foreground="#A1A1A1") + tags[Const.CLZ_HEAD_KEY] = textbuffer.create_tag(Const.CLZ_HEAD_KEY , foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_VAL] = textbuffer.create_tag(Const.CLZ_HEAD_VAL , foreground="#397BE8", weight=Pango.Weight.BOLD) + tags[Const.CLZ_0] = textbuffer.create_tag(Const.CLZ_0 , foreground="#B6B6B6") + tags[Const.CLZ_1] = textbuffer.create_tag(Const.CLZ_1 , foreground="#D5756A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_2] = textbuffer.create_tag(Const.CLZ_2 , foreground="#6AD592", weight=Pango.Weight.BOLD) + tags[Const.CLZ_3] = textbuffer.create_tag(Const.CLZ_3 , foreground="#E0D76A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_4] = textbuffer.create_tag(Const.CLZ_4 , foreground="#6AB3D5", weight=Pango.Weight.BOLD) + tags[Const.CLZ_5] = textbuffer.create_tag(Const.CLZ_5 , foreground="#6AD5C3", weight=Pango.Weight.BOLD) + tags[Const.CLZ_6] = textbuffer.create_tag(Const.CLZ_6 , foreground="#C86AD5", weight=Pango.Weight.BOLD) + tags[Const.CLZ_7] = textbuffer.create_tag(Const.CLZ_7 , foreground="#FFFFFF", weight=Pango.Weight.BOLD) + + return tags + + + def initWidgetByThread(self, thname, wview=None, wbuf=None, wpbar=None, wtag=None): + """""" + self.widgetByThread[thname] = { 'view' : wview, 'buf' : wbuf, 'pbar': wpbar, 'tags' : wtag } + + + @Log(Const.LOG_UI) + def onDeleteWindow(self, *args): + """""" + mthread = current_thread() + try: + self.join_threads(True) + self.cleanResources() + + except Exception as e: + pass + + finally: + main_quit(*args) + + + @Log(Const.LOG_UI) + def list_threads(self): + """""" + print('thread list : ') + for th in thread_enum(): + print(th) + + + @Log(Const.LOG_UI) + def join_threads(self, join_main=False): + """""" + mthread = current_thread() + try: + for th in thread_enum(): + if th is not mthread : + th.join() + if join_main: mthread.join() + + except Exception as e: + pass + + + @Log(Const.LOG_UI) + def on_about(self, btn): + """""" + about = AboutDialog() + about.set_program_name(conf.PRG_NAME) + about.set_version('v '+conf.PRG_VERS) + about.set_copyright(conf.PRG_ABOUT_COPYRIGHT) + about.set_comments(conf.PRG_ABOUT_COMMENTS) + about.set_website(conf.PRG_WEBSITE) + about.set_website_label(conf.PRG_WEBSITE) + about.set_license(Io.get_data(conf.PRG_LICENSE_PATH)) + pixbuf = Pixbuf.new_from_file_at_size(conf.PRG_LOGO_PATH, conf.PRG_ABOUT_LOGO_SIZE, conf.PRG_ABOUT_LOGO_SIZE) + about.set_logo(pixbuf) + pixbuf = Pixbuf.new_from_file_at_size(conf.PRG_LOGO_PATH, conf.PRG_ABOUT_LOGO_SIZE, conf.PRG_ABOUT_LOGO_SIZE) + about.set_icon(pixbuf) + about.run() + about.destroy() + + + @Log(Const.LOG_DEBUG) + def get(self, name): + """""" + return self.builder.get_object(name) + + + @Log(Const.LOG_DEBUG) + def disable(self, name, disabled): + """""" + self.get(name).set_sensitive(not disabled) + + + @Log(Const.LOG_DEBUG) + def repack(self, name, expandfill=False, packStart=True): + w = self.get(name) + w.get_parent().set_child_packing(w, expandfill, expandfill, 0, PackType.START if packStart else PackType.END ) + return w + + + @Log(Const.LOG_DEBUG) + def detachWidget(self, name, hideParent=True): + w = self.get(name) + wp = w.get_parent() + if wp is not None : + wp.remove(w) + w.unparent() + if hideParent : wp.hide() + + + @Log(Const.LOG_DEBUG) + def attachWidget(self, widget, parentName, expandfill=None, showParent=True): + if widget is not None : + wp = self.get(parentName) + if wp is not None : + if expandfill is None : wp.add(widget) + else : + wp.pack_start(widget,expandfill,expandfill,0) + if showParent : wp.show() + + + @Log(Const.LOG_UI) + def thread_finished(self, thread, ref): + thread = None + self.on_proceed_end(False) + + + @Log(Const.LOG_UI) + def on_proceed_end(self, abort=False): + """""" + + + @Log(Const.LOG_UI) + def on_interrupted(self, thread, ref): + thread = None + self.end_progress(thread.name) + self.on_proceed_end(False) + + + def getTxtViewByThread(thname): + """""" + if thname=='impra-1': + return self.textview + + + @Log(Const.LOG_NEVER) + def on_progress(self, thread=None, progress=None, ref=None): + #~ print('thread_progress', thread.name, progress) + while not Sys.g.LOG_QUEUE.empty(): + cth, data = Sys.g.LOG_QUEUE.get() + #~ print('*'+str(cth)) + if data is not None : + if data is Sys.g.SIGNAL_STOP : + Sys.dprint('STOP') + if thread is not None : thread.cancel() + elif data is Sys.g.SIGNAL_CLEAR : + self.clearLog(thname=cth) + else: + self.printTextView(data, thname=cth) + + if progress is not None : self.update_progress(progress, thname=thread.name) + + + def clearLog(self, thname): + """""" + self.widgetByThread[thname]['buf'].set_text('') + + + def printTextView(self, data, thname=None): + """""" + #~ print('printTextView : '+str(thname)) + textbuffer = self.widgetByThread[thname]['buf'] + tags = self.widgetByThread[thname]['tags'] + for item in data : + ei = textbuffer.get_end_iter() + offs = ei.get_offset() + textbuffer.insert_at_cursor(item[0]) + ei = textbuffer.get_end_iter() + oi = textbuffer.get_iter_at_offset(offs) + tagName = item[1] + textbuffer.apply_tag(tags[tagName], oi, ei) + textbuffer.insert_at_cursor('\n') + self.scroll_end(thname) + + + @Log(Const.LOG_NEVER) + def update_progress(self, progress, lvl=20, thname=None): + #~ print('update_progress : '+str(thname)) + if True : + pbar = self.widgetByThread[thname]['pbar'] + if progress > 0 : + pbar.set_text(str(progress)) + lp = pbar.get_fraction() + diff = (progress/100.0 - lp) + if diff > 0 : + for i in range(lvl): + nf = lp+(i*diff/lvl) + if nf < progress/100.0 : + pbar.set_fraction(nf) + Sys.sleep(0.015) + pbar.set_fraction(progress/100.0) + else : + pbar.set_fraction(pbar.get_fraction()+0.01) + + + @Log(Const.LOG_NEVER) + def end_progress(self, thname=None): + #~ print('end_progress : '+str(thname)) + self.update_progress(100, 10, thname=thname) + + + @Log(Const.LOG_NEVER) + def scroll_end(self, thname=None): + #~ print('end_progress : '+str(thname)) + if True or Sys.g.UI_AUTO_SCROLL : + textbuffer = self.widgetByThread[thname]['buf'] + if textbuffer is not None : + insert_mark = textbuffer.get_insert() + ei = textbuffer.get_end_iter() + if ei is not None and insert_mark is not None: + textbuffer.place_cursor(ei) + self.widgetByThread[thname]['view'].scroll_to_mark(insert_mark , 0.0, True, 0.0, 1.0) + + + @Log(Const.LOG_UI) + def cleanResources(self): + """""" + + + @Log(Const.LOG_UI) + def on_start(self): + """""" + + + @Log(Const.LOG_UI) + def warnDialog(self, intro, ask): + """""" + dialog = MessageDialog(self.get(self.wname), 0, MessageType.WARNING, ButtonsType.OK_CANCEL, intro) + dialog.format_secondary_text(ask) + response = dialog.run() + dialog.destroy() + return response == ResponseType.OK; + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class IdleObject ~~ + +class IdleObject(GObject): + """ + Override gi.repository.GObject to always emit signals in the main thread + by emmitting on an idle handler + """ + + @Log(Const.LOG_UI) + def __init__(self): + """""" + GObject.__init__(self) + + + @Log(Const.LOG_NEVER) + def emit(self, *args): + """""" + idle_add(GObject.emit, self, *args) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class CliThread ~~ + +class CliThread(Thread, IdleObject): + """ + Cancellable thread which uses gobject signals to return information + to the GUI. + """ + __gsignals__ = { # signal type signal return signal args + "completed" : ( SIGNAL_RUN_LAST, TYPE_NONE, ()), + "interrupted" : ( SIGNAL_RUN_LAST, TYPE_NONE, ()), + "progress" : ( SIGNAL_RUN_LAST, TYPE_NONE, (TYPE_FLOAT,)) + } + + + @Log(Const.LOG_DEBUG) + def __init__(self, rwargs, event): + Thread.__init__(self) + IdleObject.__init__(self) + self.setName('CliThread') + self.cliargs = rwargs + self.event = event + + @Log(Const.LOG_DEBUG) + def run(self): + """""" + self.cancelled = False + Sys.g.MPEVENT.clear() + Cli('./', Sys.getpid(), self.cliargs, self, Sys.g.LOG_LEVEL) + self.emit("completed") + + + @Log(Const.LOG_NEVER) + def progress(self, value): + """""" + self.emit("progress", value) + + + @Log(Const.LOG_NEVER) + def cancel(self): + """ + Threads in python are not cancellable, so we implement our own + cancellation logic + """ + self.cancelled = True + self.event.set() + + + @Log(Const.LOG_NEVER) + def stop(self): + """""" + if self.isAlive(): + self.cancel() + if current_thread().getName()==self.getName(): + try: + self.emit("interrupted") + Sys.thread_exit() + except RuntimeError as e : + print(str(self.getName()) + ' COULD NOT BE TERMINATED') + raise e diff --git a/impra/util.py b/impra/util.py deleted file mode 100644 index d7a76ed..0000000 --- a/impra/util.py +++ /dev/null @@ -1,515 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# This file is part of ImpraStorage. -# -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. -# -# ImpraStorage 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 ImpraStorage. If not, see . - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ package util ~~ - -from base64 import urlsafe_b64encode -from inspect import stack -from errno import EEXIST -from hashlib import sha256 -from math import log, floor, ceil -from os import urandom, popen, sep, makedirs, system -from os.path import dirname, realpath, abspath, join, getsize -from random import choice -from re import split as regsplit, search as regsearch, finditer as regfinditer -from subprocess import PIPE, Popen -from sys import stderr, executable as pyexec, stdout -import platform -#~ from sys.stdout import isatty -from time import time - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class Debug ~~ -class Debug: - - ALL = 0 - WARN = 1 - NOTICE = 2 - INFO = 3 - - def __init__(self, active=True, level=3): - """""" - self.active = active - self.level = level - -COLOR_MODE = True -DEBUG = Debug(True,Debug.INFO) - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ methods ~~ - -def represents_int(s): - """""" - try: - int(s) - return True - except ValueError: - return False - -def quote_escape(data): - """Escape simple quote - :Returns: `str` - """ - return data.replace('\'', r'\'') - -def linefeed_escape(data): - """Escape simple quote - :Returns: `str` - """ - return data.replace('\n', '\\n') - -def get_file_content(fileName): - """Get file content of `fileName` - :Returns: `str` - """ - r = open(fileName, 'rt') - data = r.read() - r.close() - return data - -def get_file_binary(fileName): - """Get file content of `fileName` - :Returns: `str` - """ - r = open(fileName, 'rb') - data = r.read() - r.close() - return data - -def hash_sha256(data): - """Get a sha256 hash of str `data` - :Returns: `str` - """ - return str(sha256(bytes(data,'utf-8')).hexdigest()) - -def randomFrom(val, sval=0): - """Get a random number from range `sval=0` to `val` - :Returns: `int` - """ - lst = list(range(sval,val)) - return choice(lst) - -def is_binary(filename): - """Check if given filename is binary.""" - done = False - fp = open(filename, 'rb') - try: - CHUNKSIZE = 1024 - while 1: - chunk = fp.read(CHUNKSIZE) - if b'\0' in chunk: done = True # found null byte - if done or len(chunk) < CHUNKSIZE: break - finally: - fp.close() - return done - -def get_file_path(val): - """""" - return abspath(dirname(val))+sep - -def file_exists(path): - """""" - try: - with open(path) as f: - exist = True - except IOError as e: - exist = False - return exist - -def mkdir_p(path): - """""" - try: - makedirs(path) - except OSError as e: # Python >2.5 - if e.errno == EEXIST: - pass - else: raise - -def formatBytes(b, p=2): - """Give a human representation of bytes size `b` - :Returns: `str` - """ - units = ['B', 'KB', 'MB', 'GB', 'TB']; - b = max(b,0); - if b == 0 : lb= 0 - else : lb = log(b) - p = floor(lb/log(1024)) - p = min(p, len(units)- 1) - #Uncomment one of the following alternatives - b /= pow(1024,p) - #b /= (1 << (10 * p)) - return str(round(b, 1))+' '+units[p] - -def bstr(b,enc='utf-8'): - """""" - return str(b, encoding=enc) - -def run(cmdline): - """""" - try: - p = Popen(cmdline, shell=True,stdout=PIPE, stderr=PIPE) - cmdout, cmderr = p.communicate() - rcode = p.wait() - if rcode < 0: - mprint((stderr,"Child was terminated by signal",rcode)) - else: - return (rcode,cmdout,cmderr) - except OSError as e : - return (e,cmdout,cmderr) - -def __CALLER__(args=''): - """Give basic information of caller method - usage :: - - eval(__CALLER()) - eval(__CALLER('"%s","%s"' % (arg1,arg2))) - - :Returns: `str` - """ - #global DEBUG_LEVEL, DEBUG, DEBUG_WARN - val = "self.__class__.__name__+'.%s' % stack()[1][3]+'("+quote_escape(args)+") " - if DEBUG.active and DEBUG.level<=DEBUG.WARN : val += "l:'+str(stack()[1][2])+' ' " - else: val += "'" - return val - -def mprint(d='',end='\n'): - if DEBUG.active : print(d,end=end) - -if platform.system() == 'Windows' : - SYS_CLEAR = 'cls' -else : - SYS_CLEAR = 'clear' -clear = lambda: system(SYS_CLEAR) - - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class RuTime ~~ - -class RuTime: - """Give basics time stats""" - - def __init__(self,label,lvl=DEBUG.NOTICE): - """Initialize duration with appropriate label""" - self.debug = DEBUG.active and DEBUG.level <= lvl - self.debugStart = self.debug and lvl < DEBUG.INFO - self.lvl = lvl - self.label = label - self._start() - - - def _start(self): - global Clz - if self.debug : - Clz.print(' ==> ', Clz.fgb1, False) - self._paramize(self.label) - Clz.print('', Clz.OFF) - self.sc = time() - - def stop(self): - """Stop duration and print basics stats duration on console""" - self.ec = time() - if self.debug: self._stats() - - def _paramize(self,data): - global Clz - sp = [m.start() for m in regfinditer('\(', data)] - ep = [m.start() for m in regfinditer('\)', data)] - if len(sp) > 0 : - Clz.print(data[:sp[0]+1], Clz.fgb3, False) - Clz.print(data[sp[0]+1:ep[0]], Clz.fgn7, False) - Clz.print(data[ep[0]:], Clz.fgb3, False) - else: - Clz.print(data, Clz.fgb3, False, True) - - def _stats(self): - global Clz - Clz.print(' <== ', Clz.fgb1, False) - self._paramize(self.label) - Clz.print('%.5f' % (self.ec-self.sc), Clz.fgN4) - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class IniFile ~~ - -class IniFile: - """Read a write inifile""" - - def __init__(self,path): - """""" - self.path = path - self.dic = {} - self.read() - - def isEmpty(self): - """""" - return len(self.dic)==0 - - def has(self, key, section='main'): - """""" - d = self.hasSection(section) and (key in self.dic[section]) - return d - - def hasSection(self, section): - """""" - d = (section in self.dic) - return d - - def get(self, key, section='main'): - """""" - v = None - if self.has(key,section) : v = self.dic[section][key] - return v - - def set(self, key, val, section='main'): - """""" - v = None - if not section in self.dic: - self.dic[section] = {} - if key in self.dic[section]: - v = self.dic[section].pop(key) - self.dic[section][key] = val - return v - - def rem(self, key, section): - """""" - v = None - if section in self.dic : - if key == '*' : - v = self.dic.pop(section) - elif key in self.dic[section]: - v = self.dic[section].pop(key) - return v - - def write(self,path=None): - """""" - if path == None : path = self.path - content = self.toString() - with open(path, mode='w', encoding='utf-8') as o: - o.write(content) - - def getSections(self): - """""" - l = {} - for s in self.dic: - section = s.split('.') - if len(section)> 1 and not section[0] in l : - l[section[0]] = 1 - return [k for i,k in enumerate(l)] - - def toString(self,section='*'): - """""" - content = '' - main = '' - for s in sorted(self.dic): - if section=='*' or section+'.'==s[:len(section)+1]: - if s!='main': - #~ if section=='*': content += '\n['+s+']\n' - #~ else : content += '\n['+s[len(section)+1:]+']\n' - content += '\n['+s+']\n' - for k in sorted(self.dic[s]): - if s!='main' : - content += k.rstrip(' ')+' = '+str(self.dic[s][k])+'\n' - else : main += k.rstrip(' ')+' = '+str(self.dic[s][k])+'\n' - return main + content - - def print(self,section='*', withoutSectionName=False): - """""" - a = '' - for s in self.dic: - if section=='*' or section+'.'==s[:len(section)+1]: - if s!='main': - #~ if section=='*': content += '\n['+s+']\n' - #~ else : content += '\n['+s[len(section)+1:]+']\n' - mprint() - if not withoutSectionName : - Clz.print('['+s+']', Clz.fgB3) - else: Clz.print('['+s.split('.')[1]+']', Clz.fgB3) - for k in sorted(self.dic[s]): - k = k.rstrip(' ') - if s!='main' : - a = '' - if len(self.dic[s][k]) > 50: a = '...' - Clz.print(k.ljust(10,' ')+' = ' , Clz.fgn7, False) - if Clz.isUnix or k is not 'key' : - try : - Clz.print(self.dic[s][k][:50]+a, Clz.fgN2) - except Exception as e: - Clz.print('value is masked - generate errors in your os', Clz.fgb1) - pass - else: Clz.print('key is masked', Clz.fgb1) - - def read(self): - """""" - try: - with open(self.path, encoding='utf-8') as o: - csection = 'main' - self.dic[csection] = {} - for l in o: - l = l.rstrip() - d = regsplit(' *= *',l,1) - if len(d)> 1: - self.dic[csection][d[0]] = d[1] - elif len(l)>0 and l[0]=='[': - csection = l.strip('[]') - self.dic[csection] = {} - except IOError : pass - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class StrIterator ~~ - -class StrIterator: - - MAX_ITER = 1000 - - def __init__(self,data): - self.l = len(data) - self.p = ceil(self.l/self.MAX_ITER) - self.d = [] - for x in range(self.p): - self.d.append(data[x*self.MAX_ITER:x*self.MAX_ITER+self.MAX_ITER]) - - def __iter__(self): - self.i = 0 - return self - - def __next__(self): - if self.i > len(self.d)-1 : - raise StopIteration - self.i += 1 - return self.d[self.i-1] - - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# ~~ class Coloriz ~~ - -class Coloriz: - - _MARKER = '!§' - """""" - _SEP = ';' - """""" - _PATTERN_COLOR = '^'+_MARKER[0]+'[nfNFbB][0-7]'+_SEP+'$' - """""" - _wFH = 0x0008 - """""" - _wBH = 0x0080 - """""" - _uOFF = '\033[1;m' - """""" - _wOFF = None - """""" - _LF = '\n' - """""" - OFF = _MARKER+_MARKER[0]+'OFF'+_SEP+_MARKER - """""" - isUnix = platform.system() != 'Windows' - """""" - - def __init__(self): - """Colors for both plateform are : 0: black - 1: red - 2:green - 3: yellow - 4: blue - 5: purple - 6: cyan - 7: white - available class members : - foreground normal (same as bold for w32): - self.fgn0 -> self.fgn7 - foreground bold : - self.fgb0 -> self.fgb7 - foreground high intensity (same as bold high intensity for w35): - self.fgN0 -> self.fgN7 - foreground bold high intensity : - self.fgB0 -> self.fgB7 - background - self.bg0 -> self.bg7 - background high intensity - self.BG0 -> self.BG7 - default colors : - self.OFF - - usage : - pc = PColor() - pc.print('%smon label%s:%sma value%s' % (pc.BG4+pc.fgN7, pc.OFF+pc.fgn1, pc.fgb4, pc.OFF)) - """ - global COLOR_MODE - self.active = COLOR_MODE - if not self.isUnix: - j = 0 - for i in (0,4,2,6,1,5,3,7): - exec('self._wf%i = 0x000%i' % (i,j) + '\nself._wb%i = 0x00%i0' % (i,j) + '\nself._wF%i = 0x000%i | self._wFH' % (i,j) + '\nself._wB%i = 0x00%i0 | self._wBH' % (i,j)) - # normal eq bold - exec('self._wn%i = self._wf%i' % (i,i)) - # normal high intensity eq bold high intensity - exec('self._wN%i = self._wF%i' % (i,i)) - j += 1 - - if not self.isUnix : - import impra.w32color as w32cons - self._wOFF = w32cons.get_text_attr() - self._wOFFbg = self._wOFF & 0x0070 - self._wOFFfg = self._wOFF & 0x0007 - self.setColor = w32cons.set_text_attr - - for i in range(0,8): - # foreground normal - exec('self.fgn%i = self._MARKER + self._MARKER[0] + "n%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._un%i = "\\033[0;3%im"' % (i,i)) - # foreground bold - exec('self.fgb%i = self._MARKER + self._MARKER[0] + "f%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._uf%i = "\\033[1;3%im"' % (i,i)) - # foreground high intensity - exec('self.fgN%i = self._MARKER + self._MARKER[0] + "N%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._uN%i = "\\033[0;9%im"' % (i,i)) - # foreground bold high intensity - exec('self.fgB%i = self._MARKER + self._MARKER[0] + "F%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._uF%i = "\\033[1;9%im"' % (i,i)) - # background - exec('self.bg%i = self._MARKER + self._MARKER[0] + "b%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._ub%i = "\\033[4%im"' % (i,i)) - # background high intensity - exec('self.BG%i = self._MARKER + self._MARKER[0] + "B%i" + self._SEP + self._MARKER' % (i,i)) - if True or isUnix : exec('self._uB%i = "\\033[0;10%im"' % (i,i)) - - def print(self,data,colors,endLF=True,endClz=True): - """""" - if DEBUG.active: - if not endLF : ev = '' - else: ev = self._LF - if self.active : - tokens = [c.lstrip(self._MARKER[0]).rstrip(self._SEP) for c in colors.split(self._MARKER) if c is not ''] - if self.isUnix : - if endClz : data += self._uOFF - mprint(eval('self._u'+'+self._u'.join(tokens))+data,end=ev) - else : - self.setColor(eval('self._w'+'|self._w'.join(tokens))) - mprint(data,end=ev) - stdout.flush() - if endClz : self.setColor(self._wOFF) - else: - mprint(data,end=ev) - -Clz = Coloriz() diff --git a/impra/w32color.py b/impra/w32color.py deleted file mode 100644 index 0d8bd80..0000000 --- a/impra/w32color.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# This file is part of ImpraStorage. -# -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. -# -# ImpraStorage 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 ImpraStorage. If not, see . - -""" -Colors text in console mode application (win32). -Uses ctypes and Win32 methods SetConsoleTextAttribute and -GetConsoleScreenBufferInfo. - -$Id: color_console.py 534 2009-05-10 04:00:59Z andre $ -""" - -from ctypes import windll, Structure as Struct, c_short as SHORT, c_ushort as WORD, byref - -class Coord(Struct): - """struct in wincon.h.""" - _fields_ = [("X", SHORT),("Y", SHORT)] - -class SmallRect(Struct): - """struct in wincon.h.""" - _fields_ = [("Left", SHORT),("Top", SHORT),("Right", SHORT),("Bottom", SHORT)] - -class ConsoleScreenBufferInfo(Struct): - """struct in wincon.h.""" - _fields_ = [("dwSize", Coord),("dwCursorPosition", Coord),("wAttributes", WORD),("srWindow", SmallRect),("dwMaximumWindowSize", Coord)] - -# winbase.h -STD_INPUT_HANDLE = -10 -STD_OUTPUT_HANDLE = -11 -STD_ERROR_HANDLE = -12 - -stdout_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) -SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute -GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo - -def get_text_attr(): - """Returns the character attributes (colors) of the console screen - buffer.""" - csbi = ConsoleScreenBufferInfo() - GetConsoleScreenBufferInfo(stdout_handle, byref(csbi)) - return csbi.wAttributes - -def set_text_attr(color): - """Sets the character attributes (colors) of the console screen - buffer. Color is a combination of foreground and background color, - foreground and background intensity.""" - SetConsoleTextAttribute(stdout_handle, color) diff --git a/imprastorage.py b/imprastorage.py index e9eb8c2..b680e8c 100644 --- a/imprastorage.py +++ b/imprastorage.py @@ -1,30 +1,31 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# This file is part of ImpraStorage. +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org # -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# ImpraStorage 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. +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . # -# You should have received a copy of the GNU General Public License -# along with ImpraStorage. If not, see . from impra.core import ImpraConf, ImpraStorage, realpath, dirname, abspath, sep from impra.util import IniFile, RuTime, get_file_path, Clz, mprint diff --git a/kirmah-cli.py b/kirmah-cli.py new file mode 100755 index 0000000..48d4336 --- /dev/null +++ b/kirmah-cli.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah-cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +from psr.sys import Sys, Const +from kirmah.cli import Cli + +def main(): + try: + c = 0 + Cli('.'+Sys.sep) + except Exception as e : + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + #~ raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/kirmah/__init__.py b/kirmah/__init__.py new file mode 100755 index 0000000..8b13789 --- /dev/null +++ b/kirmah/__init__.py @@ -0,0 +1 @@ + diff --git a/kirmah/app.py b/kirmah/app.py new file mode 100755 index 0000000..f530bec --- /dev/null +++ b/kirmah/app.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/app.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module app ~~ + +from os.path import splitext +from threading import Thread, Timer, Event, get_ident, enumerate as thread_enum, current_thread +from kirmah import conf +from kirmah.crypt import KeyGen, Kirmah, KirmahHeader +from psr.sys import Sys, Io, Const, init +from psr.log import Log + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class KirmahApp ~~ + +class KirmahApp: + + @Log(Const.LOG_BUILD) + def __init__(self, debug=False, color=True, loglvl=Const.LOG_NEVER): + """""" + self.encmode = conf.DEFVAL_ENCMODE + self.splitmode = False + self.compression = conf.DEFVAL_COMP + self.mix = conf.DEFVAL_MIXMODE + self.random = conf.DEFVAL_RANDOMMODE + self.nproc = conf.DEFVAL_NPROC + self.src = None + self.dst = None + self.kpath = None + Sys.g.GUI = True + init(conf.PRG_NAME, debug, Sys.getpid(), color, loglvl) + + + @Log(Const.LOG_DEBUG) + def getDefaultKeyPath(self): + """""" + return conf.DEFVAL_UKEY_PATH+conf.DEFVAL_UKEY_NAME + + + @Log(Const.LOG_DEBUG) + def createDefaultKeyIfNone(self): + """""" + kpath = self.getDefaultKeyPath() + if not Io.file_exists(kpath): + #if Sys.isUnix() : + if not Sys.isdir(conf.DEFVAL_UKEY_PATH) : + Sys.mkdir_p(conf.DEFVAL_UKEY_PATH) + k = KeyGen(conf.DEFVAL_UKEY_LENGHT) + print(k) + content = k.key + print('content') + Io.set_data(kpath, content) + print('set content') + self.selectKey(kpath) + + + @Log(Const.LOG_DEBUG) + def createNewKey(self, filename, size): + """""" + if not Sys.isdir(Sys.dirname(filename)): + Sys.mkdir_p(Sys.dirname(filename)) + Io.set_data(filename,KeyGen(size).key[:size]) + + + @Log(Const.LOG_DEBUG) + def getKeyInfos(self, filename=None): + """""" + if filename is None : filename = self.getDefaultKeyPath() + if not Io.file_exists(filename): + raise FileNotFoundException(filename) + print(filename) + print('toto2') + k = Io.get_data(filename) + print('get data2') + s = len(k) + # print(s) + m = KeyGen(s).getMark(k) + return k, s, m + + + @Log(Const.LOG_DEBUG) + def selectKey(self, filename): + """""" + print('selectKey : ') + print(filename) + if not Io.file_exists(filename): + raise FileNotFoundException(filename) + self.kpath = filename + + + @Log(Const.LOG_DEBUG) + def setCompression(self, value=1): + """""" + self.compression = value + + + @Log(Const.LOG_DEBUG) + def setMixMode(self, enable=True): + """""" + self.mix = enable + + + @Log(Const.LOG_DEBUG) + def setRandomMode(self, enable=True): + """""" + self.random = enable + + + @Log(Const.LOG_DEBUG) + def setMultiprocessing(self, nproc): + """""" + # disable + if nproc is None or nproc is False or nproc < conf.DEFVAL_NPROC_MIN : + self.nproc = 0 + # enable + else : + self.nproc = nproc if nproc <= conf.DEFVAL_NPROC_MAX else conf.DEFVAL_NPROC_MAX + + + @Log(Const.LOG_DEBUG) + def switchEncMode(self, isEnc=True): + """""" + self.encmode = isEnc + + + @Log(Const.LOG_DEBUG) + def switchFormatMode(self, isTxt=True): + self.splitmode = not isTxt + + + @Log(Const.LOG_DEBUG) + def setSourceFile(self, filename): + """""" + if not Io.file_exists(filename) : + raise FileNotFoundException() + else : + self.src = filename + + + @Log(Const.LOG_DEBUG) + def hasSrcFile(self): + """""" + return Io.file_exists(self.src) + + + @Log(Const.LOG_DEBUG) + def setDestFile(self, path): + """""" + if path is not None : + self.dst = ''.join([path, Sys.sep, '' if self.src is None else Sys.basename(self.src)]) + if self.encmode: + self.dst = ''.join([self.dst, Kirmah.EXT if not self.splitmode else Kirmah.EXT_TARK]) + else : + self.dst, ext = Sys.getFileExt(self.dst) + if not ext == (Kirmah.EXT if not self.splitmode else Kirmah.EXT_TARK): + self.dst += ext + #~ if Io.file_exists(self.dst): + #~ raise FileNeedOverwriteException(self.dst) + else : self.dst = None + + + @Log(Const.LOG_DEFAULT) + def getCall(self): + q = '' + action = ('enc' if self.encmode else 'dec') if not self.splitmode else ('split' if self.encmode else 'merge') + comp = '-a' if self.compression==1 else ('-z' if self.compression==2 else '-Z') + mproc = '' if self.nproc==0 or self.splitmode else '-j'+str(self.nproc) + rmode = '-r' if self.random else '-R ' + mmode = '-m' if self.mix else '-M' + debug = '-fd' if Sys.g.DEBUG else '-f' + key = '-k'+q+self.kpath+q if self.kpath != self.getDefaultKeyPath() else '' + #~ q = '"' + call = ['kirmah-cli.py',debug, action,q+self.src+q,comp,mproc,rmode,mmode,'-o',q+self.dst+q,key] + print('python3 '+(' '.join(call))) + return call + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class FileNotFoundException ~~ + +class FileNotFoundException(BaseException): + """""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class FileNeedOverwriteException ~~ + +class FileNeedOverwriteException(BaseException): + """""" diff --git a/kirmah/cli.py b/kirmah/cli.py new file mode 100755 index 0000000..5daad1d --- /dev/null +++ b/kirmah/cli.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module cli ~~ + +from optparse import OptionGroup +from psr.sys import Sys, Io, Const, init +from psr.log import Log +from psr.cli import AbstractCli +from kirmah.cliapp import CliApp +import kirmah.conf as conf + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Cli ~~ + +class Cli(AbstractCli): + + def __init__(self, path, remote=False, rwargs=None, thread=None, loglvl=Const.LOG_DEFAULT): + """""" + AbstractCli.__init__(self, conf, self) + + + Cli.HOME = conf.DEFVAL_USER_PATH + Cli.DIRKEY = Cli.HOME+'.'+conf.PRG_NAME.lower()+Sys.sep + if not Sys.isUnix() : + CHQ = '"' + self.HOME = 'C:'+Sys.sep+conf.PRG_NAME.lower()+Sys.sep + self.DIRKEY = self.HOME+'keys'+Sys.sep + Sys.mkdir_p(Cli.DIRKEY) + + gpData = OptionGroup(self.parser, '') + gpData.add_option('-a', '--fullcompress' , action='store_true' ) + gpData.add_option('-z', '--compress' , action='store_true' ) + gpData.add_option('-Z', '--nocompress' , action='store_true' ) + gpData.add_option('-r', '--random' , action='store_true' ) + gpData.add_option('-R', '--norandom' , action='store_true' ) + gpData.add_option('-m', '--mix' , action='store_true' ) + gpData.add_option('-M', '--nomix' , action='store_true' ) + gpData.add_option('-j', '--multiprocess' , action='store') + gpData.add_option('-k', '--keyfile' , action='store') + gpData.add_option('-l', '--length' , action='store', default=1024) + gpData.add_option('-p', '--parts' , action='store', default=22) + gpData.add_option('-o', '--outputfile' , action='store') + self.parser.add_option_group(gpData) + + # rewrite argv sended by remote + if rwargs is not None : + import sys + sys.argv = rwargs + + (o, a) = self.parser.parse_args() + + Sys.g.QUIET = o.quiet + Sys.g.THREAD_CLI = thread + Sys.g.GUI = thread is not None + + init(conf.PRG_NAME, o.debug, remote, not o.no_color, loglvl) + + + if not a: + try : + if not o.help or not o.version: + self.parser.error_cmd(('no command specified',), True) + else : + Sys.clear() + Cli.print_help() + except : + if not o.version : + self.parser.error_cmd(('no command specified',), True) + else : + Cli.print_header() + + else: + if a[0] == 'help': + Sys.clear() + Cli.print_help() + + elif a[0] in ['key','enc','dec','split','merge'] : + + app = CliApp(self.HOME, path, self, a, o) + + if a[0]=='key' : + app.onCommandKey() + else : + if not len(a)>1 : + self.parser.error_cmd((('an ',('inputFile',Sys.Clz.fgb3),' is required !'),), True) + elif not Io.file_exists(a[1]): + self.parser.error_cmd((('the file ',(a[1], Sys.Clz.fgb3), ' doesn\'t exists !'),), True) + + elif a[0]=='enc' : app.onCommandEnc() + elif a[0]=='dec' : app.onCommandDec() + elif a[0]=='split': app.onCommandSplit() + elif a[0]=='merge': app.onCommandMerge() + + Sys.dprint('PUT END SIGNAL') + if Sys.g.LOG_QUEUE is not None : + Sys.g.LOG_QUEUE.put(Sys.g.SIGNAL_STOP) + + else : + self.parser.error_cmd((('unknow command ',(a[0],Sys.Clz.fgb3)),), True) + + if not o.quiet : Sys.dprint() + + + @staticmethod + def print_usage(data, withoutHeader=False): + """""" + if not withoutHeader : Cli.print_header() + + Sys.echo(' USAGE :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('help ' , Sys.CLZ_HELP_CMD) + + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('key ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('[ -l ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('length' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('outputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + Sys.echo(' '+Cli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('enc ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('inputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('} ' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('[' , Sys.CLZ_HELP_ARG, False) + Sys.echo(' -z|Z|a -r|R -m|M -j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('numProcess' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('keyFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('outputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + Sys.echo(' '+conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('dec ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('inputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('} ' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('[' , Sys.CLZ_HELP_ARG, False) + Sys.echo(' -j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('numProcess' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('keyFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('outputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + Sys.echo(' '+conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('split ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('inputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('} ' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('[' , Sys.CLZ_HELP_ARG, False) + Sys.echo(' -p ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('numParts' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('keyFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('tarOutputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + Sys.echo(' '+conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('merge ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('inputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('} ' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('[' , Sys.CLZ_HELP_ARG, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('keyFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('outputFile' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + + @staticmethod + def print_options(): + """""" + Sys.dprint('\n') + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + Sys.echo(' MAIN OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-v'.ljust(13,' ')+', --version' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display programm version' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-d'.ljust(13,' ')+', --debug' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable debug mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-f'.ljust(13,' ')+', --force' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'force rewriting existing files without alert' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-q'.ljust(13,' ')+', --quiet' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'don\'t print status messages to stdout' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-h'.ljust(13,' ')+', --help' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display help' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' KEY OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-l ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('LENGTH'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --length'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('LENGTH'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified key length (128 to 4096 - default:1024)' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --outputfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified key output filename' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' ENCRYPT OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-a'.ljust(13,' ')+', --fullcompress' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable full compression mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-z'.ljust(13,' ')+', --compress' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable compression mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-Z'.ljust(13,' ')+', --nocompress' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'disable compression mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-r'.ljust(13,' ')+', --random' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable random mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-R'.ljust(13,' ')+', --norandom' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'disable random mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-m'.ljust(13,' ')+', --mix' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable mix mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-M'.ljust(13,' ')+', --nomix' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'disable mix mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --multiprocess'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'number of process for encryption (2 to 8)' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --keyfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'key filename used to encrypt' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --outputfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified encrypted output filename' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' DECRYPT OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --multiprocess'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'number of process for decryption (2 to 8)' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --keyfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'key filename used to decrypt' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --outputfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified decrypted output filename' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' SPLIT OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-p ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --part'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('COUNT'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'count part to split' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --keyfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'key filename used to split' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('TARFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --outputfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('TARFILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified tar output filename' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' MERGE OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --keyfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'key filename used to merge' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --outputfile'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('FILE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'specified decrypted output filename' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + + + @staticmethod + def print_help(): + """""" + Cli.print_header() + Sys.echo(Cli.conf.PRG_DESC, Sys.CLZ_HELP_DESC) + Cli.print_usage('',True) + Cli.print_options() + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint() + Sys.echo(' EXEMPLES :\n', Sys.CLZ_HELP_CMD) + CHQ = "'" + + Sys.echo(' '*4+'command key :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# generate a new crypted key of 2048 length', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('key -l ', Sys.CLZ_HELP_CMD, False) + Sys.echo('2048 ', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# generate a new crypted key (default length is 1024) in a specified location', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('key -o ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.DIRKEY+'.myNewKey', Sys.CLZ_HELP_PARAM) + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command encrypt :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# encrypt specified file with default crypted key and default options', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('enc ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'mySecretTextFile.txt', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# encrypt specified file with specified crypted key (full compression, no random but mix mode)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+'# on specified output location', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('enc ', Sys.CLZ_HELP_CMD, False) + Sys.echo('mySecretTextFile.txt', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -aRm -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(Cli.DIRKEY+'.myNewKey', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('test.kmh', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# encrypt specified file with default crypted key (no compression but random & mix mode and multiprocessing)', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('enc ', Sys.CLZ_HELP_CMD, False) + Sys.echo('myBigTextFile.txt', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -Zrm -j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('4', Sys.CLZ_HELP_PARAM) + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command decrypt :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# decrypt specified file with default crypted key', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('dec ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'mySecretFile.kmh', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# decrypt specified file with specified crypted key on specified output location', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+Cli.conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('dec ', Sys.CLZ_HELP_CMD, False) + Sys.echo('myEncryptedSecretFile.kmh', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(Cli.HOME+'.kirmah'+Sys.sep+'.myNewKey', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('myDecryptedSecretFile.txt', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# decrypt specified file with default crypted key and multiprocessing', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('dec ', Sys.CLZ_HELP_CMD, False) + Sys.echo('myEncryptedSecretFile.kmh', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -j ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('4' , Sys.CLZ_HELP_PARAM) + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command split :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# split specified file with default crypted key', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('split ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'myBigBinaryFile.avi', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# split specified file on 55 parts with specified crypted key on specified output location', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('split ', Sys.CLZ_HELP_CMD, False) + Sys.echo('myBigBinaryFile.avi', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -p ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('55' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(Cli.DIRKEY+'.myNewKey', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('myBigBinaryFile.encrypted', Sys.CLZ_HELP_PARAM) + + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.echo('\n'+' '*4+'command merge :', Sys.CLZ_HELP_CMD) + + Sys.echo(' '*8+'# merge specified splitted file with default crypted key', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('merge ', Sys.CLZ_HELP_CMD, False) + Sys.echo(Cli.HOME+'6136bd1b53d84ecbad5380594eea7256176c19e0266c723ea2e982f8ca49922b.kcf', Sys.CLZ_HELP_PARAM) + + Sys.echo(' '*8+'# merge specified tark splitted file with specified crypted key on specified output location', Sys.CLZ_HELP_COMMENT) + Sys.echo(' '*8+conf.PRG_CLI_NAME+' ', Sys.CLZ_HELP_PRG, False) + Sys.echo('merge ', Sys.CLZ_HELP_CMD, False) + Sys.echo('myBigBinaryFile.encrypted.tark', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -k ' , Sys.CLZ_HELP_ARG, False) + Sys.echo(Cli.DIRKEY+'.myNewKey', Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -o ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('myBigBinaryFile.decrypted.avi', Sys.CLZ_HELP_PARAM) + + Cli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.dprint() diff --git a/kirmah/cliapp.py b/kirmah/cliapp.py new file mode 100755 index 0000000..5328c31 --- /dev/null +++ b/kirmah/cliapp.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/cliapp.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module cliapp ~~ + +import kirmah.conf as conf +from kirmah.crypt import KirmahHeader, Kirmah, BadKeyException, represents_int, KeyGen +from psr.sys import Sys, Const, Io +from psr.log import Log +import tarfile + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class CliApp ~~ + +class CliApp: + + @Log(Const.LOG_BUILD) + def __init__(self, home, path, parser, a, o): + """""" + self.parser = parser + self.a = a + self.o = o + self.home = home + self.stime = Sys.datetime.now() + if not self.o.keyfile : + self.o.keyfile = self.home+'.kirmah'+Sys.sep+'.default.key' + + + @Log(Const.LOG_DEBUG) + def onCommandKey(self): + """""" + if int(self.o.length) >= 128 and int(self.o.length) <= 4096 : + self.parser.print_header() + if not self.o.outputfile : self.o.outputfile = self.home+'.kirmah'+Sys.sep+'.default.key' + kg = KeyGen(int(self.o.length)) + done = True + if Io.file_exists(self.o.outputfile) and not self.o.force : + + Sys.pwarn((('the key file ',(self.o.outputfile, Sys.Clz.fgb3), ' already exists !'), + 'if you rewrite this file, all previous files encrypted with the corresponding key will be', + 'unrecoverable !')) + + done = Sys.pask('Are you sure to rewrite this file') + self.stime = Sys.datetime.now() + if done : + Io.set_data(self.o.outputfile, kg.key) + Sys.pstep('Generate key file', self.stime, done) + + if done : + Sys.echo(' '*5+Sys.realpath(self.o.outputfile), Sys.Clz.fgB1, True) + + else : + self.parser.error_cmd((('invalid option ',('-l, --length', Sys.Clz.fgb3), ' value (', ('128',Sys.Clz.fgb3),' to ', ('4096',Sys.Clz.fgb3),')'),)) + + + @Log(Const.LOG_DEBUG) + def onCommandEnc(self): + """""" + done = True + if self.o.outputfile is None : + self.o.outputfile = Sys.basename(self.a[1]) + if self.o.outputfile[-len(Kirmah.EXT):] != Kirmah.EXT : + self.o.outputfile += Kirmah.EXT + + d = self.getDefaultOption((self.o.compress,self.o.fullcompress,self.o.nocompress)) + compress = (KirmahHeader.COMP_END if d == 0 or (d is None and Io.is_binary(self.a[1])) else (KirmahHeader.COMP_ALL if d==1 or d is None else KirmahHeader.COMP_NONE)) + random = True if (self.o.random is None and self.o.norandom is None) or self.o.random else False + mix = True if (self.o.mix is None and self.o.nomix is None) or self.o.mix else False + + if (self.o.multiprocess is not None and not represents_int(self.o.multiprocess)) or (not self.o.multiprocess is None and not(int(self.o.multiprocess)>=2 and int(self.o.multiprocess) <=8)) : + self.parser.error_cmd((('invalid option ',('-j, --multiprocess', Sys.Clz.fgb3), ' value (', ('2',Sys.Clz.fgb3),' to ', ('8',Sys.Clz.fgb3),')'),)) + + nproc = int(self.o.multiprocess) if not self.o.multiprocess is None and int(self.o.multiprocess)>=2 and int(self.o.multiprocess) <=8 else 1 + + if not Sys.g.QUIET : self.parser.print_header() + + if Io.file_exists(self.o.outputfile) and not self.o.force: + Sys.pwarn((('the file ',(self.o.outputfile, Sys.Clz.fgb3), ' already exists !'),)) + done = Sys.pask('Are you sure to rewrite this file') + self.stime = Sys.datetime.now() + + if done : + + try : + Sys.ptask() + + key = Io.get_data(self.o.keyfile) + km = Kirmah(key, None, compress, random, mix) + + km.encrypt(self.a[1], self.o.outputfile, nproc) + + except Exception as e : + done = False + print(e) + raise e + pass + + if not Sys.g.QUIET : + self.onend_cmd('Kirmah Encrypt', self.stime, done, self.o.outputfile) + + + @Log(Const.LOG_DEBUG) + def onCommandDec(self): + """""" + done = True + if self.o.outputfile is None : + self.o.outputfile = self.a[1][:-4] if self.a[1][-4:] == Kirmah.EXT else self.a[1] + + if not Sys.g.QUIET : self.parser.print_header() + + if Io.file_exists(self.o.outputfile) and not self.o.force: + Sys.pwarn((('the file ',(self.o.outputfile, Sys.Clz.fgb3), ' already exists !'),)) + done = Sys.pask('Are you sure to rewrite this file') + self.stime = Sys.datetime.now() + + if done : + + try : + + if (self.o.multiprocess is not None and not represents_int(self.o.multiprocess)) or (not self.o.multiprocess is None and not(int(self.o.multiprocess)>=2 and int(self.o.multiprocess) <=8)) : + self.parser.error_cmd((('invalid option ',('-j, --multiprocess', Sys.Clz.fgb3), ' value (', ('2',Sys.Clz.fgb3),' to ', ('8',Sys.Clz.fgb3),')'),)) + + nproc = int(self.o.multiprocess) if not self.o.multiprocess is None and int(self.o.multiprocess)>=2 and int(self.o.multiprocess) <=8 else 1 + + Sys.ptask() + + key = Io.get_data(self.o.keyfile) + km = Kirmah(key) + + km.decrypt(self.a[1], self.o.outputfile, nproc) + + except BadKeyException: + done = False + Sys.pwarn((('BadKeyException : ',('wrong key ',Sys.CLZ_WARN_PARAM), ' !'),), False) + + if not Sys.g.QUIET : + self.onend_cmd('Kirmah Decrypt', self.stime, done, self.o.outputfile) + + + @Log(Const.LOG_DEBUG) + def onCommandSplit(self): + """""" + done = True + Sys.cli_emit_progress(1) + if not self.o.parts is None and not(int(self.o.parts)>=12 and int(self.o.parts) <=62) : + self.parser.error_cmd((('invalid option ',('-p, --parts', Sys.Clz.fgb3), ' value (', ('12',Sys.Clz.fgb3),' to ', ('62',Sys.Clz.fgb3),')'),)) + else : self.o.parts = int(self.o.parts) + + if not Sys.g.QUIET : self.parser.print_header() + if self.o.outputfile is not None : + if self.o.outputfile[-5:]!='.tark' : self.o.outputfile += '.tark' + if Io.file_exists(self.o.outputfile) and not self.o.force: + Sys.pwarn((('the file ',(self.o.outputfile, Sys.Clz.fgb3), ' already exists !'),)) + done = Sys.pask('Are you sure to rewrite this file') + self.stime = Sys.datetime.now() + + if done : + + try : + Sys.ptask() + Sys.cli_emit_progress(2) + key = Io.get_data(self.o.keyfile) + km = Kirmah(key) + hlst = km.ck.getHashList(Sys.basename(self.a[1]), self.o.parts, True) + Sys.cli_emit_progress(3) + kcf = km.splitFile(self.a[1], hlst) + t = int(Sys.time()) + times = (t,t) + p = 85 + Sys.cli_emit_progress(p) + Io.touch(kcf, times) + frav = 0.24 + for row in hlst['data']: + p += frav + Io.touch(row[1]+km.EXT,times) + Sys.cli_emit_progress(p) + if self.o.outputfile is not None : + d = Sys.datetime.now() + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.ptask('Preparing tark file') + hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[4]) + with tarfile.open(self.o.outputfile, mode='w') as tar: + tar.add(kcf, arcname=Sys.basename(kcf)) + p = 90 + for row in hlst['data']: + tar.add(row[1]+km.EXT, arcname=Sys.basename(row[1]+km.EXT)) + p += frav + Sys.cli_emit_progress(p) + Sys.pstep('Packing destination file', d, True) + d = Sys.datetime.now() + Sys.ptask('Finalize') + for row in hlst['data']: + Io.removeFile(row[1]+km.EXT) + p += frav + Sys.cli_emit_progress(p) + Io.removeFile(kcf) + Sys.pstep('Cleaning', d, True) + + except Exception as e : + done = False + if Sys.g.DEBUG : + print('split exception') + print(e) + + #~ raise e + elif not Sys.g.QUIET : + Sys.pwarn((str(e),)) + + if not Sys.g.QUIET: + Sys.cli_emit_progress(100) + self.onend_cmd('Kirmah Split', self.stime, done, self.o.outputfile) + + + + @Log(Const.LOG_DEBUG) + def onCommandMerge(self): + """""" + done = True + + if not Sys.g.QUIET : self.parser.print_header() + + if done : + toPath = None + try : + Sys.ptask() + + key = Io.get_data(self.o.keyfile) + km = Kirmah(key) + kcf = None + istar = True + try: + import tarfile + dpath = Sys.dirname(Sys.realpath(self.o.outputfile))+Sys.sep if self.o.outputfile is not None else Sys.dirname(Sys.realpath(self.a[1]))+Sys.sep + if self.o.outputfile is None : + self.o.outputfile = dpath + with tarfile.open(self.a[1], mode='r') as tar: + #~ print(dpath) + tar.extractall(path=dpath) + kcf = None + for tarinfo in tar: + #~ print(tarinfo.name) + if tarinfo.isreg() and tarinfo.name[-4:]=='.kcf': + #~ print(dpath+tarinfo.name) + kcf = dpath+tarinfo.name + if kcf is not None : + km.DIR_OUTBOX = dpath + toPath = km.mergeFile(kcf, self.o.outputfile) + except BadKeyException: + Sys.pwarn((('BadKeyException : ',('wrong key ',Sys.CLZ_WARN_PARAM), ' !'),), False) + done = False + + except Exception : + istar = False + toPath = km.mergeFile(self.a[1], self.o.outputfile) + + #~ if self.o.outputfile is not None : + #~ Io.rename(toPath, self.o.outputfile) + #~ toPath = self.o.outputfile + + except BadKeyException: + Sys.pwarn((('BadKeyException : ',('wrong key ',Sys.CLZ_WARN_PARAM), ' !'),), False) + done = False + + except Exception as e : + done = False + if Sys.g.DEBUG : + print(e) + elif not Sys.g.QUIET : + Sys.pwarn((str(e),)) + if not done : + if istar : + with tarfile.open(self.a[1], mode='r') as tar: + for tarinfo in tar: + Sys.removeFile(dpath+tarinfo.name) + + if not Sys.g.QUIET : + self.onend_cmd('Kirmah Merge', self.stime, done, toPath) + + + @Log(Const.LOG_ALL) + def getDefaultOption(self, args): + """""" + c = None + for i, a in enumerate(args) : + if a : + c = i + break + return c + + + @Log(Const.LOG_DEBUG) + def onend_cmd(self, title, stime, done, outputfile): + """""" + s = Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN + Sys.echo(s, Sys.CLZ_HEAD_LINE) + Sys.wlog([(s, Const.CLZ_HEAD_SEP)]) + Sys.pstep(title, stime, done, True) + Sys.echo(s, Sys.CLZ_HEAD_LINE) + Sys.wlog([(s, Const.CLZ_HEAD_SEP)]) + if done and outputfile is not None: + Sys.cli_emit_progress(100) + Sys.echo(' '*5+Sys.realpath(outputfile), Sys.Clz.fgB1, False) + Sys.echo(' ('+Sys.getFileSize(outputfile)+')', Sys.Clz.fgB3) + bdata = [(' '*5+Sys.realpath(outputfile), 'io'),(' ('+Sys.getFileSize(outputfile)+')','func')] + Sys.wlog(bdata) + Sys.wlog(Sys.dprint()) diff --git a/kirmah/conf.py b/kirmah/conf.py new file mode 100755 index 0000000..1ad0981 --- /dev/null +++ b/kirmah/conf.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/conf.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module conf ~~ + +from getpass import getuser as getUserLogin +from os import sep +from os.path import dirname, realpath, isdir, join + +PRG_NAME = 'Kirmah' +PRG_PACKAGE = PRG_NAME.lower() +PRG_SCRIPT = PRG_NAME.lower() +PRG_CLI_NAME = PRG_SCRIPT+'-cli' +PRG_VERS = '2.18' +PRG_AUTHOR = 'a-Sansara' +PRG_COPY = 'pluie.org' +PRG_YEAR = '2013' +PRG_WEBSITE = 'http://kirmah.sourceforge.net' +PRG_LICENSE = 'GNU GPL v3' +PRG_RESOURCES_PATH = '/usr/share/'+PRG_PACKAGE+sep +if not isdir(PRG_RESOURCES_PATH): + PRG_RESOURCES_PATH = dirname(dirname(realpath(__file__)))+sep+'resources'+sep+PRG_PACKAGE+sep +#~ print(PRG_RESOURCES_PATH) +PRG_GLADE_PATH = PRG_RESOURCES_PATH+'glade'+sep+PRG_PACKAGE+'.glade' +PRG_LICENSE_PATH = PRG_RESOURCES_PATH+'/LICENSE' +PRG_LOGO_PATH = join(PRG_RESOURCES_PATH,'..'+sep,'pixmaps'+sep,PRG_PACKAGE+sep,PRG_PACKAGE+'.png') +PRG_LOGO_ICON_PATH = join(PRG_RESOURCES_PATH,'..'+sep,'pixmaps'+sep,PRG_PACKAGE+sep,PRG_PACKAGE+'_ico.png') +PRG_ABOUT_LOGO_SIZE = 160 +PRG_ABOUT_COPYRIGHT = '(c) '+PRG_AUTHOR+' - '+PRG_COPY+' '+PRG_YEAR +PRG_ABOUT_COMMENTS = ''.join(['Kirmah simply encrypt/decrypt files','\n', 'license ',PRG_LICENSE]) +PRG_DESC = """ + Encryption with symmetric-key algorithm Kirmah. + + three modes are available to encrypt : + + - compression (full / disabled or only final step) + - random (simulate a random order - based on crypted key - to randomize data) + - mix (mix data according to a generated map - based on crypted key - with addition of noise) + + + Process is as follow : + + for encryption : + file > [ compression > ] encryption > [randomiz data > mix data > compression > ] file.kmh + + default options depends on file type (binary or text). + - binary files are compressed only at the end of process + - text files have a full compression mode + - random and mix modes are enabled on all files + + for decryption : + file.kmh > [ uncompression > unmix data > unrandomiz data] > decryption > [uncompression > ] file + + + multiprocessing is avalaible for reducing encryption/decryption time. + + + for encrypt large binary files, a fastest alternative is possible : + the split command. + + the split command consist on splitting file into severals parts (with noise addition) according to + a generated map based on the crypted key. + the map is fully encrypted as a configuration file (.kcf) which is required to merge all parts + + the merge command is the opposite process. +""" + + +DEFVAL_NPROC = 2 +DEFVAL_NPROC_MAX = 8 +DEFVAL_NPROC_MIN = 2 +DEFVAL_COMP = False +DEFVAL_ENCMODE = True +DEFVAL_MIXMODE = True +DEFVAL_RANDOMMODE = True +DEFVAL_USER_PATH = ''.join([sep,'home',sep,getUserLogin(),sep]) +#~ DEFVAL_USER_PATH = ''.join(['C:',sep,sep,'dev',sep,sep,getUserLogin(),sep,sep]) +DEFVAL_UKEY_PATH = ''.join([DEFVAL_USER_PATH, '.', PRG_PACKAGE,sep]) +#~ DEFVAL_UKEY_PATH = ''.join([DEFVAL_USER_PATH, PRG_PACKAGE,sep,sep]) +DEFVAL_UKEY_NAME = 'default.key' +DEFVAL_UKEY_LENGHT = 1024 +DEFVAL_CRYPT_EXT = '.kmh' + +DEBUG = True +UI_TRACE = True +PCOLOR = True + +GUI_LABEL_PROCEED = 'Proceed' +GUI_LABEL_OK = 'OK' +GUI_LABEL_CANCEL = 'Cancel' + +def redefinePaths(path): + + PRG_GLADE_PATH = path+PRG_PACKAGE+sep+'glade'+sep+PRG_PACKAGE+'.glade' + PRG_LICENSE_PATH = path+PRG_PACKAGE+sep+'LICENSE' + PRG_LOGO_PATH = path+'pixmaps'+sep+PRG_PACKAGE+sep+PRG_PACKAGE+'.png' + PRG_LOGO_ICON_PATH = path+'pixmaps'+sep+PRG_PACKAGE+sep+PRG_PACKAGE+'_ico.png' diff --git a/kirmah/crypt.py b/kirmah/crypt.py new file mode 100755 index 0000000..008cac7 --- /dev/null +++ b/kirmah/crypt.py @@ -0,0 +1,1218 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/crypt.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module crypt ~~ + +from base64 import urlsafe_b64encode, b64decode +from binascii import b2a_base64, a2b_base64 +from hashlib import sha256, md5 +from math import log, floor, ceil +from random import choice +from os import urandom +from re import sub +from mmap import mmap +from ast import literal_eval +from psr.sys import Sys, Io, Const +from psr.log import Log +from psr.mproc import Manager + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ methods ~~ + +@Log(Const.LOG_ALL) +def hash_sha256(data): + """Get a sha256 hash of str `data` + :Returns: `str` + """ + return str(sha256(bytes(data,'utf-8')).hexdigest()) + + +@Log(Const.LOG_ALL) +def hash_sha256_file(path): + """Get a sha256 hash of str `data` + :Returns: `str` + """ + return sha256(open(path, mode='rb').read()).hexdigest() + + +@Log(Const.LOG_ALL) +def hash_md5_file(path): + """Get a md5 hash of file from path + :Returns: `str` + """ + return md5(open(path, mode='rb').read()).hexdigest() + + +@Log(Const.LOG_ALL) +def randomFrom(val, sval=0): + """Get a random number from range `sval=0` to `val` + :Returns: `int` + """ + lst = list(range(sval,val)) + return choice(lst) + + +@Log(Const.LOG_NEVER) +def represents_int(s): + """""" + try: + if s is None : return False + int(s) + return True + except ValueError: + return False + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class KeyGen ~~ + +class KeyGen : + + CHSET = [33, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 880, 881, 882, 883, 884, 885, 886, 887, 891, 892, 893, 894, 901, 902, 903, 904, 905, 906, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 3871, 3872, 3873, 3874, 3875, 3876, 3877, 3878, 3879, 3880, 3881, 3882, 3883, 3884, 3885, 3886, 3887, 3888, 3889, 3890, 3891, 3892, 3893, 3894, 3895, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4347, 4348, 4352, 4353, 4354, 4355, 4356, 4357, 4358, 4359, 4360, 4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, 4371, 4372, 4373, 4374, 4375, 4376, 4377, 4378, 4379, 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 4400, 4401, 4402, 4403, 4404, 4405, 4406, 4407, 4408, 4409, 4410, 4411, 4412, 4413, 4414, 4415, 4416, 4417, 4418, 4419, 4420, 4421, 4422, 4423, 4424, 4425, 4426, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435, 4436, 4437, 4438, 4439, 4440, 4441, 4640, 4641, 4642, 4643, 4644, 4645, 4646, 4647, 4648, 4649, 4650, 4651, 4652, 4653, 4654, 4655, 4656, 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, 4672, 4673, 4674, 4675, 4676, 4677, 4678, 4705, 4706, 4707, 4708, 4709, 4710, 4711, 4712, 4713, 4714, 4715, 4716, 4717, 4718, 4719, 4720, 4721, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4730, 4731, 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, 4740, 4741, 4742, 4753, 4754, 4755, 4756, 4757, 4758, 4759, 4760, 4761, 4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, 4780, 4781, 4782, 4825, 4826, 4827, 4828, 4829, 4830, 4831, 4832, 4833, 4834, 4835, 4836, 4837, 4838, 4839, 4840, 4841, 4842, 4843, 4844, 4845, 4846, 4847, 4848, 4849, 4850, 4851, 4852, 4853, 4854, 4855, 4856, 4857, 4858, 4859, 4860, 4861, 4862, 4863, 4864, 4865, 4866, 4867, 4868] #, 4869, 4870, 4871, 4872, 4873, 4874, 4875, 4876, 4877, 4878, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, 5072, 5073, 5074, 5075, 5076, 5077, 5078, 5079, 5080, 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5196, 5197, 5198, 5199, 5200, 5201, 5202, 5203, 5204, 5205, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5214, 5215, 5216, 5217, 5218, 5219, 5220, 5221, 5222, 5223, 5224, 5225, 5226, 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, 5235, 5236, 5237, 5238, 5239, 5240, 5241, 5242, 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5260, 5261, 5262, 5263, 5264, 5265, 5266, 5267, 5268, 5269, 5270, 5271, 5272, 5273, 5274, 5275, 5276, 5277, 5278, 5279, 5280, 5281, 5282, 5283, 5284, 5285, 5286, 5287, 5288, 5289, 5290, 5291, 5292, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 5349, 5350, 5351, 5352, 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, 5361, 5362, 5363, 5364, 5365, 5366, 5367, 5368, 5369, 5370, 5371, 5372, 5373, 5374, 5375, 5376, 5377, 5378, 5379, 5380, 5381, 5382, 5383, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5421, 5422, 5423, 5424, 5425, 5426, 5427, 5428, 5429, 5430, 5431, 5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, 5441, 5442, 5443, 5444, 5445, 5446, 5447, 5448, 5449, 5450, 5451, 5452, 5453, 5454, 5455, 5456, 5457, 5458, 5459, 5460, 5461, 5462, 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, 5471, 5472, 5473, 5474, 5475, 5476, 5477, 5478, 5479, 5480, 5481, 5482, 5483, 5484, 5485, 5486, 5487, 5488, 5489, 5490, 5491, 5492, 5493, 5494, 5495, 5496, 5497, 5498, 5499, 5500, 5501, 5502, 5503, 5504, 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, 5517, 5518, 5519, 5520, 5521, 5522, 5523, 5524, 5525, 5526, 5527, 5528, 5529, 5530, 5531, 5532, 5533, 5534, 5535, 5536, 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, 5551, 5556, 5557, 5558, 5559, 5560, 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5570, 5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5579, 5580, 5581, 5582, 5583, 5584, 5585, 5586, 5587, 5588, 5589, 5590, 5591, 5592, 5593, 5594, 5595, 5596, 5597, 5598, 5599, 5600, 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5612, 5613, 5614, 5615, 5616, 5617, 5618, 5619, 5620, 5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629, 5630, 5631, 5632, 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5640, 5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, 5660, 5661, 5662, 5663, 5664, 5665, 5666, 5667, 5668, 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, 5681, 5682, 5683, 5684, 5685, 5686, 5687, 5688, 5689, 5690, 5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, 5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5715, 5716, 5717, 5718, 5719, 5720, 5721, 5722, 5723, 5724, 5725, 5726, 5727, 5728, 5729, 5730, 5731, 5732, 5733, 5734, 5735, 5736, 5737, 5738, 5739, 5740, 5741, 5742, 5743, 5744, 5745, 5746, 5747, 5748, 5749, 5750, 5751, 5752, 5753, 5754, 5755, 5756, 5757, 5758, 5759, 5793, 5794, 5795, 5796, 5797, 5798, 5799, 5800, 5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, 5809, 5810, 5811, 5812, 5813, 5814, 5815, 5816, 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, 5825, 5826, 5827, 5828, 5829, 5830, 5831, 5832, 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, 5841, 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, 5851, 5852, 5853, 5854, 5855, 5856, 5857, 5858, 5859, 5860, 5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, 7425, 7426, 7427, 7428, 7429, 7430, 7431, 7432, 7433, 7434, 7435, 7436, 7437, 7438, 7439, 7440, 7441, 7442, 7443, 7444, 7445, 7446, 7447, 7448, 7449, 7450, 7451, 7452, 7453, 7454, 7455, 7456, 7457, 7458, 7459, 7460, 7461, 7462, 7463, 7464, 7465, 7466, 7467, 7468, 7469, 7470, 7471, 7472, 7473, 7474, 7475, 7476, 7477, 7478, 7479, 7480, 7481, 7482, 7483, 7484, 7485, 7486, 7487, 7488, 7489, 7490, 7491, 7492, 7493, 7494, 7495, 7496, 7497, 7498, 7499, 7500, 7501, 7502, 7503, 7504, 7505, 7506, 7507, 7508, 7509, 7510, 7511, 7512, 7513, 7514, 7515, 7516, 7517, 7518, 7519, 7520, 7521, 7522, 7523, 7524, 7525, 7526, 7527, 7528, 7529, 7530, 7531, 7532, 7533, 7534, 7535, 7536, 7537, 7538, 7539, 7540, 7541, 7542, 7543, 7544, 7545, 7546, 7547, 7548, 7549, 7550, 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558, 7559, 7560, 7561, 7562, 7563, 7564, 7565, 7566, 7567, 7568, 7569, 7570, 7571, 7572, 7573, 7574, 7575, 7576, 7577, 7578, 7579, 7580, 7581, 7582, 7583, 7584, 7585, 7586, 7587, 7588, 7589, 7590, 7591, 7592, 7593, 7594, 7595, 7596, 7597, 7598, 7599, 7600, 7601, 7602, 7603, 7604, 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 7613, 7614, 7681, 7682, 7683, 7684, 7685, 7686, 7687, 7688, 7689, 7690, 7691, 7692, 7693, 7694, 7695, 7696, 7697, 7698, 7699, 7700, 7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, 7711, 7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722, 7723, 7724, 7725, 7726, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734, 7735, 7736, 7737, 7738, 7739, 7740, 7741, 7742, 7743, 7744, 7745, 7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, 7776, 7777, 7778, 7779, 7780, 7781, 7782, 7783, 7784, 7785, 7786, 7787, 7788, 7789, 7790, 7791, 7792, 7793, 7794, 7795, 7796, 7797, 7798, 7799, 7800, 7801, 7802, 7803, 7804, 7805, 7806, 7807, 7808, 7809, 7810, 7811, 7812, 7813, 7814, 7815, 7816, 7817, 7818, 7819, 7820, 7821, 7822, 7823, 7824, 7825, 7826, 7827, 7828, 7829, 7830, 7831, 7832, 7833, 7834, 7835, 7836, 7837, 7838, 7839, 7840, 7841, 7842, 7843, 7844, 7845, 7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, 7857, 7858, 7859, 7860, 7861, 7862, 7863, 7864, 7865, 7866, 7867, 7868, 7869, 7870, 7871, 7872, 7873, 7874, 7875, 7876, 7877, 7878, 7879, 7880, 7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, 7890, 7891, 7892, 7893, 7894, 7895, 7896, 7897, 7898, 7899, 7900, 7901, 7902, 7903, 7904, 7905, 7906, 7907, 7908, 7909, 7910, 7911, 7912, 7913, 7914, 7915, 7916, 7917, 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7928, 7929, 7930, 7931, 7932, 7933, 7934, 7935, 7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943, 7944, 7945, 7946, 7947, 7948, 7949, 7950, 7951, 7952, 7953, 7954, 7955, 7956, 7957, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 8040, 8041, 8042, 8043, 8044, 8045, 8046, 8047, 8048, 8049, 8050, 8051, 8052, 8053, 8054, 8055, 8056, 8057, 8058, 8059, 8060, 8061, 4305, 8065, 8066, 8067, 8068, 8069, 8070, 8071, 8072, 8073, 8074, 8075, 8076, 8077, 8078, 8079, 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099, 8100, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 8116, 8449, 8450, 8451, 8452, 8453, 8454, 8455, 8456, 8457, 8458, 8459, 8460, 8461, 8462, 8463, 8464, 8465, 8466, 8467, 8468, 8469, 8470, 8471, 8472, 8473, 8474, 8475, 8476, 8477, 8478, 8479, 8480, 8481, 8482, 8483, 8484, 8485, 8486, 8487, 8488, 8489, 8490, 8491, 8751, 8752, 8753, 8754, 8755, 8756, 8757, 8758, 8759, 8760, 8761, 8762, 8763, 8764, 8765, 8766, 8767, 8768, 8769, 8770, 8771, 8772, 8773, 8774, 8775, 8776, 8777, 8778, 8779, 8780, 8781, 8782, 8783, 8784, 8785, 8786, 8787, 8788, 8789, 8790, 8791, 8792, 8793, 8794, 8795, 8796, 8797, 8798, 8799, 8800, 8801, 8802, 8803, 8804, 8805, 8806, 8807, 8808, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4347, 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366, 1370, 1371, 1372, 1373, 1374, 1375, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1521, 1522, 1523, 1524, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956] + """""" + LEN_FOOTPRINT = 24 + """""" + SALT = '-¤-Kirmah-¤-' + """""" + + @Log(Const.LOG_BUILD) + def __init__(self, length=1024, salt=None): + """""" + self.new(length, salt) + + + @Log(Const.LOG_PRIVATE) + def _build(self,l): + """""" + r = Randomiz(len(self.CHSET),self.CHSET) + self.key = ksin = kfoo = '' + dic = {} + for i in range(l): + self.key += chr(r.get(False)) + if not self.key[i] in dic: dic[self.key[i]] = 1 + for c in dic: ksin += c + for c in ksin[::-5]: + if len(kfoo)>=self.LEN_FOOTPRINT: break + kfoo += c + self.mark = hash_sha256(self.salt+kfoo) + + + @Log(Const.LOG_DEBUG) + def getMark(self, key=None): + """""" + dic = {} + ksin = kfoo = '' + if key is None : + key = self.key + for i in range(len(key)): + if not key[i] in dic: dic[key[i]] = 1 + for c in dic: ksin += c + for c in sorted(ksin)[::-5]: + if len(kfoo)>=self.LEN_FOOTPRINT: break + kfoo += c + return hash_sha256(self.salt+kfoo) + + + @Log(Const.LOG_DEBUG) + def new(self, length, salt=None): + """""" + if salt == None : self.salt = self.SALT + else : self.salt = salt + self._build(length) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ConfigKey ~~ + +class ConfigKey: + + @Log(Const.LOG_BUILD) + def __init__(self, key=None, salt=None, psize=19710000): + """""" + self.key = bytes(key,'utf-8') if key is not None else self._build() + self.salt = str(self.key[::-10]) if salt is None else salt + self.psize = psize + self.noiser = Noiser(self.key) + self.rdmz = Randomiz(1) + + + @staticmethod + @Log(Const.LOG_ALL) + def sumNumber(s,count): + """""" + return sum([ int(c) for j,c in enumerate(s) if represents_int(c)][0:count]) + + + @Log(Const.LOG_DEBUG) + def getHashList(self,name,count,noSorted=False): + """""" + self.rdmz.new(count) + dic, lst, hroot = {}, [], hash_sha256(self.salt+name) + + srdl = Kirmah.getRandomListFromKey(self.key, count) + #~ srdl = getRandomListFromKey(self.key, count) + for i in range(count) : + self.noiser.build(i,ConfigKey.sumNumber(hash_sha256(str(i)+self.salt+name),1 if i%2 else 2)) + d = str(i).rjust(2,'0') + # part n°, hash, lns, lne, pos + hpart = hash_sha256(self.salt+name+'.part'+d)[:-3]+str(ord(hroot[i])).rjust(3,'0') + lst.append((i, hpart, self.noiser.lns, self.noiser.lne, self.rdmz.get(), srdl[i])) + dic['head'] = [name,count,hroot,self.getKey()] + if not noSorted : + lst = sorted(lst, key=lambda lst: lst[4]) + dic['data'] = lst + return dic + + + @Log(Const.LOG_PRIVATE) + def _build(self,l=48): + """""" + kg = KeyGen(l) + k = urlsafe_b64encode(bytes(kg.key,'utf-8')) + return k + + + @Log(Const.LOG_DEBUG) + def getKey(self): + """""" + return str(self.key,'utf-8') + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Kirmah ~~ + +class KirmahHeader: + + COMP_NONE = 0 + COMP_ALL = 1 + COMP_END = 2 + + POS_VERS = 5 + POS_COMP = 7 + POS_RAND = 12 + POS_MIX = 15 + POS_SEC = 18 + POS_END = 22 + + ID = b'\x05\xd9\x83MH' + MODE_COMP = b'Z' + MODE_RAND = b'R' + MODE_MIX = b'M' + MODE_SEC = b'S' + + """ +  + ex : كMH02Z2499R42M26S0055 +  + 5o : كMH - File type id (FIXED) + + + 2o : 02 - Version (majeur number) + version.rjust(2,'0') + + 5o : Z?? - Compression Mode + '?' ord(chr mark pos)%2==0 + ? compression ON + : compression OFF + '?' ord(chr mark pos)%2==0 + ? compression all + : compression end + + 3o : R? - Random Mode + '?' ord(chr mark pos)%2==0 + ? random ON + : randon OFF + + 3o : M? - Mix Mode + '?' ord(chr mark pos)%2==0 + ? mix ON + : mix OFF + + 3o : S??? - Secure Mode + '?' mark[dlen%len(mark)].rjust(3,'0') + + """ + + @Log(Const.LOG_BUILD) + def __init__(self, version, mark, cmode=1, rmode=True, mmode=True): + """""" + self.version = bytes(str(int(float(version))).rjust(2,'0'),'utf-8') + self.mark = mark + self.cmode = cmode + self.rmode = rmode + self.mmode = mmode + + + @Log(Const.LOG_DEBUG) + def getPositionnalChar(self, sindex, test=True): + """""" + pc = None + for i, c in enumerate(self.mark[sindex:]) : + if c % 2 == 0 and test or not test and c % 2 != 0 : + pc = i+sindex + break + return pc + + + @Log(Const.LOG_DEBUG) + def checkPositionnalChar(self, pc): + """""" + return ord(self.mark[pc:pc+1])%2==0 + + + @Log() + def buildHeader(self, dlen, cmode=None, rmode=None, mmode=None): + """""" + if cmode is None : cmode = self.cmode + if rmode is None : rmode = self.rmode + if mmode is None : mmode = self.mmode + nocomp = cmode is self.COMP_NONE + cmpc1 = self.getPositionnalChar(1, not nocomp) + cmpc2 = self.getPositionnalChar(cmpc1+5, cmode is self.COMP_ALL) + rmpc = self.getPositionnalChar(cmpc2+5, rmode) + mmpc = self.getPositionnalChar(rmpc+5, mmode) + smpc = self.mark[dlen%len(self.mark)] + head = [self.ID, self.version, self.MODE_COMP, Io.bytes(str(cmpc1).rjust(2,'0')), Io.bytes(str(cmpc2).rjust(2,'0')), self.MODE_RAND, Io.bytes(str(rmpc).rjust(2,'0')), self.MODE_MIX, Io.bytes(str(mmpc).rjust(2,'0')), self.MODE_SEC, Io.bytes(str(smpc).rjust(3,'0'))] + return b''.join(head) + + + @Log() + def readHeader(self, header): + """""" + + isKmh, vers, cmode, rmode, mmode, smode, pc1, badKmh = header[:self.POS_VERS] == self.ID, None, None , None, None, None, None, False + if isKmh : + vers = int(header[self.POS_VERS:self.POS_VERS+2]) + if header[self.POS_COMP:self.POS_COMP+1] == self.MODE_COMP : + if self.checkPositionnalChar(int(header[self.POS_COMP+1:self.POS_COMP+3])) : + cmode = self.COMP_ALL if self.checkPositionnalChar(int(header[self.POS_COMP+3:self.POS_COMP+5])) else self.COMP_END + else : + cmode = self.COMP_NONE + else : + badKmh = True + + if header[self.POS_RAND:self.POS_RAND+1] == self.MODE_RAND : + rmode = self.checkPositionnalChar(int(header[self.POS_RAND+1:self.POS_RAND+3])) + else : + badKmh = True + + if header[self.POS_MIX:self.POS_MIX+1] == self.MODE_MIX : + mmode = self.checkPositionnalChar(int(header[self.POS_MIX+1:self.POS_MIX+3])) + else : + badKmh = True + + if header[self.POS_SEC:self.POS_SEC+1] == self.MODE_SEC : + smode = chr(int(header[self.POS_SEC+1:self.POS_SEC+4])) + else : badKmh = True + + return { 'version':vers, 'cmode':cmode, 'rmode':rmode, 'mmode':mmode,'smode':smode} if isKmh and not badKmh else {} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Kirmah ~~ + +class Kirmah: + + VERSION = '2.1' + EXT = '.kmh' + EXT_TARK = '.tark' + DIR_OUTBOX = '' + DIR_INBOX = '' + DIR_DEPLOY = '' + DIR_TEMP = '' + KMP_FILE = '.kmp' + + + @Log(Const.LOG_BUILD) + def __init__(self, key, mark=None, headcompress=2, headrandom=True, headmix=True): + """""" + self.key = Io.bytes(key) + self.mark = KeyGen(len(key)).getMark(key) if mark is None else mark + self.mark2 = hash_sha256(self.mark) + self.mark[::-1] + self.ck = ConfigKey(self.mark2) + self.kh = KirmahHeader(Kirmah.VERSION, Io.bytes(self.mark), headcompress, headrandom, headmix) + + + @Log() + def compress_start(self, fromPath, toPath, compress=True, lvl=9, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + data = fi.read() if not compress else Io.gzcompress(fi.read(), lvl) + fo.write(b2a_base64(data)) + + + @Log() + def uncompress_start(self, fromPath, toPath, decompress=True, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + data = a2b_base64(fi.read()) + fo.write(data if not decompress else Io.gzdecompress(data)) + + + @Log() + def compress_end(self, fromPath, toPath, compress=True, lvl=9, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + data = fi.read() + if compress : data = Io.gzcompress(data, lvl) + header = self.kh.buildHeader(len(data)) + fo.write(header) + fo.write(data) + + + @Log() + def uncompress_end(self, fromPath, toPath, decompress=True, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + fi.seek(self.kh.POS_END) + fo.write(fi.read() if not decompress else Io.gzdecompress(fi.read())) + + @Log(Const.LOG_ALL) + def encryptStr(self, data, emit=True): + """""" + if not Sys.is_cli_cancel(): + s, lk, i = [], len(self.key), 0 + for c in data: + if lk-i <= 0: + i = 0 + if Sys.is_cli_cancel(): break + s.append(chr(c + i//4 + (self.key[i] if c + self.key[i] + i//4 < 11000 else -self.key[i]))) + i += 1 + return Io.bytes(''.join(s)) + + + @Log() + def encryptToFile(self, fromPath, toPath, i=0, event=None, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.ufile(fromPath) as fi : + with Io.wfile(toPath, False) as fo : + s, lk = [], len(self.key) + for c in Io.read_utf8_chr(fi): + if i >= lk: + i = 0 + if Sys.is_cli_cancel(event) : + Sys.pwarn((('terminating child process ',(str(Sys.getpid()),Sys.CLZ_WARN_PARAM), ' !'),), False) + break + fo.write(chr(ord(c) + i//4 + (self.key[i] if ord(c) + self.key[i] + i//4 < 11000 else -self.key[i]))) + i += 1 + + + @Log() + def decryptToFile(self, fromPath, toPath, i=0, event=None, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.ufile(fromPath) as fi : + with Io.rfile(fromPath) as fi2 : + s = fi2.read() + with Io.wfile(toPath, False) as fo : + s, lk = [], len(self.key) + + for c in Io.read_utf8_chr(fi): + if i >= lk: + i = 0 + if Sys.is_cli_cancel(event) : + Sys.pwarn((('terminating child process ',(str(Sys.getpid()),Sys.CLZ_WARN_PARAM), ' !'),), False) + break + try : + fo.write(chr(ord(c)- i//4 + (-self.key[i] if ord(c) + self.key[i] +i//4 < 110000 else self.key[i]))) + except Exception as e : + Sys.pwarn((('decryptToFile : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'), + ('ord c : ',(str(ord(c)),Sys.CLZ_ERROR_PARAM), ' - self.key[',(str(i),Sys.CLZ_ERROR_PARAM), '] : ',(str(self.key[i]),Sys.CLZ_ERROR_PARAM)), + ), True) + raise e + i += 1 + + + @Log() + def randomFileContent(self, fromPath, toPath, emit=True): + """""" + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + fsize, chsize, size = Kirmah.getSizes(fromPath) + lst, rest, data = Kirmah.getRandomListFromKey(self.ck.key, size), chsize - fsize%chsize, ['b']*size + if rest == chsize : rest = 0 + for piece, i in Io.read_in_chunks(fi, chsize): + fo.seek(lst[i]*chsize-(rest if lst[i] > lst[size-1] else 0)) + fo.write(piece[::-1]) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Random mode', d, c) + + + @Log() + def unRandomFileContent(self, fromPath, toPath, emit=True): + """""" + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + fsize, chsize, size = Kirmah.getSizes(fromPath) + lst, rest, piece, data = Kirmah.getRandomListFromKey(self.ck.key, size), chsize - fsize%chsize, b'', [] + if rest == chsize : rest = 0 + for i, pos in enumerate(lst): + dp = pos*chsize-(rest if pos >= lst[size-1] and pos!=0 else 0) + if dp >= 0 : fi.seek(dp) + piece = fi.read(chsize) + if i == size-1 and rest > 0 : + piece = piece[:-rest] if lst[i]==0 else piece[rest:] + fo.write(piece[::-1]) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Random mode - inv', d, c) + + + @Log() + def mixdata(self, fromPath, toPath, encryptNoise=False, label='kirmah', cpart=22, emit=True): + """""" + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + hlst = self.ck.getHashList(label, cpart, False) + hlst['data'] = sorted(hlst['data'], key=lambda hlst: hlst[0]) + size = Sys.getsize(fromPath) + psize = ceil(size/cpart) + cp = 0 + rsz = 0 + for row in hlst['data']: rsz += row[2]+row[3] + with Io.rfile(fromPath) as fi: + with Io.wfile(toPath) as fo : + bdata, adata = '', '' + for row in hlst['data']: + bdata, adata = self.ck.noiser.getNoise(row[2]), self.ck.noiser.getNoise(row[3]) + if encryptNoise : + bdata, adata = self.encryptStr(bdata)[:row[2]], self.encryptStr(adata)[:row[3]] + fi.seek(psize*row[5]) + fo.write(bdata[:row[2]] + fi.read(psize) + adata[:row[3]]) + cp += 1 + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Mix mode', d, c) + + + @Log(Const.LOG_DEBUG) + def getNoiseLenBeforeIndex(self, hlst, psize, rest, size): + """""" + if not Sys.is_cli_cancel(): + lst, l, df, sou, f = [], 0, psize, False, 0 + lst.append(l) + mxp = size // psize + if size % psize == 0 : mxp -= 1 + for row in hlst: + if row[5] == mxp : + df = rest + elif row[5] > mxp : + df = 0 + else : df = psize + l += row[2]+ row[3] + df + lst.append(l) + return lst + + + @Log() + def unmixdata(self, fromPath, toPath, label='kirmah', cpart=22, emit=True): + """""" + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + rsz, cp, hlst = 0, 0, self.ck.getHashList(label, cpart, True) + for row in hlst['data']: + rsz += row[2]+row[3] + size = Sys.getsize(fromPath)-rsz + psize = ceil(size/cpart) + rest = size % psize + if rest == 0 : rest = psize + lbi = self.getNoiseLenBeforeIndex(hlst['data'],psize,rest, size) + hlst['data'] = sorted(hlst['data'], key=lambda hlst: hlst[5]) + mxp = size // psize + if size % psize == 0 : mxp -= 1 + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + header = fi.read(self.kh.POS_END) + fi.seek(0,Io.SEEK_CUR) + for row in hlst['data']: + fi.seek(lbi[row[0]]+row[2]) + dp = fi.read(psize if row[5] <= mxp else (rest if rest!=psize or (psize*cpart==size) else 0)) + cp += 1 + if fo.tell() + len(dp) > size : + fo.write(dp[:rest]) + break + fo.write(dp) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Mix mode - inv', d, c) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # SPLIT # # + + @Log() + def splitFile(self, fromPath, hlst, nproc=1): + """""" + if not Sys.is_cli_cancel(): + d = Sys.datetime.now() + Sys.cli_emit_progress(2) + self.split(fromPath, hlst) + Sys.cli_emit_progress(70) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Splitting file', d, True) + return self.kcfEnc(hlst) + + + @Log() + def kcfEnc(self, hlst, nproc=1): + if not Sys.is_cli_cancel(): + d = Sys.datetime.now() + theStr = {'name': hlst['head'][0], 'count': hlst['head'][1] } + Io.set_data(self.DIR_DEPLOY+hlst['head'][2]+'.tmp', str(theStr)) + self.encrypt(self.DIR_DEPLOY+hlst['head'][2]+'.tmp', self.DIR_DEPLOY+hlst['head'][2]+'.kcf', nproc, KirmahHeader(self.VERSION, Io.bytes(self.mark), KirmahHeader.COMP_NONE, True, True), False) + Sys.removeFile(self.DIR_DEPLOY+hlst['head'][2]+'.tmp') + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + Sys.pstep('Encrypting Kirmah configuration file', d, True) + Sys.cli_emit_progress(75) + return self.DIR_DEPLOY+hlst['head'][2]+'.kcf' + + + @Log() + def split(self, fromPath, hlst): + """""" + if not Sys.is_cli_cancel(): + f = open(fromPath, 'rb+') + m, p, rsz = mmap(f.fileno(), 0), 0, 0 + fsize = Sys.getsize(fromPath) + Sys.cli_emit_progress(3) + for row in hlst['data']: rsz += row[2]+row[3] + # ensure correct order + hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[0]) + + self.splheader = self.kh.buildHeader(fsize) + psize = ceil(fsize/hlst['head'][1]) + Sys.cli_emit_progress(4) + perc = 5 + frav = 1.40 + while m.tell() < m.size(): + perc += frav + Sys.cli_emit_progress(perc) + self.splitPart(m, psize, hlst['data'][p]) + perc += frav + Sys.cli_emit_progress(perc) + p += 1 + m.close() + + # ensure random order + hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[4]) + hlst['head'].append(psize) + return hlst + + + @Log() + def splitPart(self, mmap, size, phlst): + """""" + if not Sys.is_cli_cancel(): + with Io.wfile(self.DIR_OUTBOX+phlst[1]+self.EXT) as fo : + bdata, adata, part = self.ck.noiser.getNoise(phlst[2], False)[len(self.splheader):], self.ck.noiser.getNoise(phlst[3], False), int(phlst[0]) + zd = Io.gzcompress(bdata+mmap.read(size)+adata) + hz = Io.bytes(self.offuscate(zd[:self.kh.POS_END], part)) + lhz = Io.bytes(str(part + len(hz)).rjust(3,'0')) + fo.write(self.splheader+lhz+hz+zd[self.kh.POS_END:]) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # MERGE # # + + @Log() + def mergeFile(self, fromPath, toPath=None, uid=''): + """""" + if not Sys.is_cli_cancel(): + Sys.cli_emit_progress(2) + self.decrypt(fromPath, '.cfg') + Sys.cli_emit_progress(5) + data = Io.get_data('.cfg') + clist = literal_eval(data) + Sys.removeFile('.cfg') + theList = self.ck.getHashList(clist['name'], clist['count'], True) + ext = '' + if toPath is None : + toPath = clist['name'] + elif Sys.isdir(toPath) : + toPath += clist['name'] + toPath, ext = Sys.getFileExt(toPath) + dirs = (Sys.dirname(Sys.realpath(toPath)) if toPath is not None else Sys.dirname(Sys.realpath(fromPath)))+Sys.sep + Sys.cli_emit_progress(10) + toPath = self.merge(theList, toPath, ext, uid, dirs) + Sys.removeFile(fromPath) + Sys.cli_emit_progress(90) + return toPath + + + @Log() + def merge(self, hlst, fileName, ext='', uid='', dirs=None, fake=False): + """""" + if not Sys.is_cli_cancel(): + p = 0 + # ensure correct order + hlst['data'] = sorted(hlst['data'], key=lambda lst: lst[0]) + #~ print(hlst['head']) + #~ for row in hlst['data']: + #~ print(row) + #~ if dirs is not None and dirs!='none' : + #~ dirPath = Sys.join(self.DIR_DEPLOY,dirs)+Sys.sep + #~ Sys.mkdir_p(dirPath) + #~ else: dirPath = self.DIR_DEPLOY + #~ print('dirPath') + #~ print(dirPath) + #~ filePath = dirPath+fileName + filePath = fileName + if Io.file_exists(filePath+ext): + filePath += '-'+str(uid) + filePath += ext + depDir = dirs + perc = 10 + frav = len(hlst['data']) + with Io.wfile(filePath) as fo : + while p < hlst['head'][1] : + perc = p/3*100/len(hlst['data']) + Sys.cli_emit_progress(perc) + try: + self.mergePart(fo, hlst['data'][p], depDir) + except Exception as e: + Sys.pwarn((('merge : ',(str(e),Sys.CLZ_WARN_PARAM), ' !'),), True) + raise e + perc = p*100/len(hlst['data']) + Sys.cli_emit_progress(perc) + p += 1 + return filePath + + + @Log() + def mergePart(self, fo, phlst, depDir): + """""" + if not Sys.is_cli_cancel(): + with Io.rfile(depDir+phlst[1]+self.EXT) as fi: + part, head = int(phlst[0]), fi.read(self.kh.POS_END) + fo.write(Io.gzdecompress(self.deoffuscate(Io.str(fi.read(int(fi.read(3))-part)), part) + fi.read())[phlst[2]-self.kh.POS_END:-phlst[3]]) + Sys.removeFile(depDir+phlst[1]+self.EXT) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # ENCRYPT # # + + @Log() + def mpMergeFiles(self,hlstPaths, toPath, noRemove=False, emit=True): + """""" + if not Sys.is_cli_cancel(): + with Io.wfile(toPath) as fo: + for fromPath in hlstPaths : + with Io.rfile(fromPath) as fi : + fo.write(fi.read()) + if not noRemove : Sys.removeFile(fromPath) + #~ if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + #~ Sys.pstep('Encrypt Data (multiprocessing)', d, c) + + + @Log() + def encrypt_sp_start(self, fromPath, toPath, header=None, emit=True): + """""" + if not Sys.is_cli_cancel(): + if header is not None : + self.kh = header + fsize = Sys.getsize(fromPath) + if fsize > 0 : + strh = self.kh.buildHeader(fsize) + decHeader = self.kh.readHeader(strh) + self.tmpPath1 = self.DIR_TEMP + Sys.basename(fromPath) + '.tmp' + self.tmpPath2 = self.DIR_TEMP + Sys.basename(fromPath) + '.tmp2' + compend, compstart = not decHeader['cmode']== KirmahHeader.COMP_NONE, decHeader['cmode']== KirmahHeader.COMP_ALL + fp, tp = fromPath, self.tmpPath1 + if emit : Sys.cli_emit_progress(2) + d = Sys.datetime.now() + if compstart : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Compressing data') + self.compress_start(fp, tp, compstart, emit=emit) + if compstart : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + if emit : Sys.cli_emit_progress(5) + return fp, tp, decHeader['rmode'], decHeader['mmode'], compend + + + @Log() + def encrypt_sp_end(self, fp, tp, toPath, rmode, mmode, compend, emit=True): + """""" + if not Sys.is_cli_cancel(): + if rmode : + #~ self.mpRandomFileContent(fp, tp, 4) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Randomizing data') + self.randomFileContent(fp, tp, emit=emit) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + if emit : Sys.cli_emit_progress(75) + + if mmode : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Mixing data') + self.mixdata(fp, tp, True, emit=emit) + + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + if emit : Sys.cli_emit_progress(85) + + if compend : + d = Sys.datetime.now() + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Compressing data') + self.compress_end(fp, toPath, compend, emit=emit) + if emit : Sys.cli_emit_progress(95) + + if compend : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) + + # clean tmp files + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Cleaning') + + try : + Sys.removeFile(self.tmpPath1) + Sys.removeFile(self.tmpPath2) + except: + pass + if emit : Sys.cli_emit_progress(97) + + + @Log() + def prepare_mproc_encode(self, fp, nproc): + """""" + if not Sys.is_cli_cancel(): + self.mproc_fsize = [] + fsize = Sys.getsize(fp) + chsize = (fsize//nproc)+1 + if fsize % chsize == 0 : chsize -= 1 + + hlstPaths = [] + with Io.rfile(fp) as fi : + for pdata, part in Io.read_in_chunks(fi, chsize): + self.mproc_fsize.append(len(pdata)) + Io.set_data(self.KMP_FILE+'_'+str(Sys.getpid())+'_'+str(part), pdata, True) + hlstPaths.append(self.KMP_FILE+'enc_'+str(Sys.getpid())+'_'+str(part)) + return hlstPaths + + + @Log() + def mproc_encode_part(self, id, event=None, emit=True): + """""" + if not Sys.is_cli_cancel(): + mpfile, mpfilenc = self.KMP_FILE+'_'+str(Sys.g.MAIN_PROC)+'_'+str(id), self.KMP_FILE+'enc_'+str(Sys.g.MAIN_PROC)+'_'+str(id) + self.encryptToFile(mpfile, mpfilenc, self.getSubStartIndice(id), event, emit=emit) + Sys.removeFile(mpfile) + + + @Log() + def encrypt_mproc(self, fp, tp, nproc=1, emit=True): + """""" + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Encrypting data') + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + if nproc == 1 : + self.encryptToFile(fp, tp) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Encrypt data', d, c) + else : + hlstPaths = self.prepare_mproc_encode(fp, nproc) + mg = Manager(self.mproc_encode_part, nproc, None, Sys.g.MPEVENT) + mg.run() + self.mpMergeFiles(hlstPaths, tp, emit=emit) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Encrypt data (multiproc)', d, c) + if emit : Sys.cli_emit_progress(70) + + + @Log() + def encrypt(self, fromPath, toPath, nproc=1, header=None, emit=True): + """""" + if emit : Sys.cli_emit_progress(0) + if not Sys.is_cli_cancel(): + fp, tp, rmode, mmode, compend = self.encrypt_sp_start(fromPath, toPath, header, emit=True) + self.encrypt_mproc(fp, tp, nproc, emit=True) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + self.encrypt_sp_end(fp, tp, toPath, rmode, mmode, compend, emit=True) + if emit : Sys.cli_emit_progress(100) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # DECRYPT # # + + @Log() + def decrypt_sp_start(self, fromPath, toPath, emit=True): + """""" + if not Sys.is_cli_cancel(): + if Sys.getsize(fromPath) > 0 : + self.tmpPath1 = self.DIR_TEMP + Sys.basename(fromPath) + '.tmp' + self.tmpPath2 = self.DIR_TEMP + Sys.basename(fromPath) + '.tmp2' + fsize = Sys.getsize(fromPath) + fsize -= self.kh.POS_END + with Io.rfile(fromPath) as f : + d = Sys.datetime.now() + if emit : Sys.cli_emit_progress(1) + decHeader = self.kh.readHeader(f.read(self.kh.POS_END)) + if emit : Sys.cli_emit_progress(2) + #~ print(decHeader) + if len(decHeader) > 0 : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if decHeader['smode'] == self.mark[fsize%len(self.mark)] : + if not Sys.g.QUIET : Sys.pstep('Reading Header', d, True) + else : + if not Sys.g.QUIET : Sys.pstep('Reading Header', d, False, False, False) + raise BadKeyException('wrong key') + + compend, compstart = not decHeader['cmode']== KirmahHeader.COMP_NONE, decHeader['cmode']== KirmahHeader.COMP_ALL + fp, tp = fromPath, self.tmpPath1 + + if emit : Sys.cli_emit_progress(3) + if compend : + d = Sys.datetime.now() + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Uncompressing data') + self.uncompress_end(fp, tp, compend, emit=emit) + if emit : Sys.cli_emit_progress(10) + if compend : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + + + if decHeader['mmode'] : + d = Sys.datetime.now() + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Sorting data') + self.unmixdata(fp, tp, emit=emit) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + if emit : Sys.cli_emit_progress(20) + if decHeader['rmode'] : + d = Sys.datetime.now() + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Reordering data') + self.unRandomFileContent(fp, tp, emit=emit) + fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 + if emit : Sys.cli_emit_progress(25) + return fp, tp, compstart + + + @Log() + def decrypt_sp_end(self, fromPath, toPath, compstart, emit=True): + """""" + if not Sys.is_cli_cancel(): + d = Sys.datetime.now() + if emit : Sys.cli_emit_progress(80) + if compstart : + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Uncompressing data') + self.uncompress_start(fromPath, toPath, compstart, emit=emit) + if emit : Sys.cli_emit_progress(90) + if compstart: + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) + + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Cleaning') + if emit : Sys.cli_emit_progress(95) + try : + Sys.removeFile(self.tmpPath1) + Sys.removeFile(self.tmpPath2) + except: + pass + if emit : Sys.cli_emit_progress(97) + + + @Log() + def decrypt_mproc(self, fromPath, toPath, nproc=1, emit=True): + """""" + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.ptask('Decrypting data') + d = Sys.datetime.now() + c = not Sys.is_cli_cancel() + if c: + if emit : Sys.cli_emit_progress(30) + if nproc == 1 : + self.decryptToFile(fromPath, toPath) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Decrypt data', d, True) + else : + hlstPaths = self.prepare_mproc_decode(fromPath, nproc) + mg = Manager(self.mproc_decode_part, nproc, None, Sys.g.MPEVENT, emit=True) + mg.run() + self.mpMergeFiles(hlstPaths, toPath, emit=emit) + if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) + if not Sys.g.QUIET : Sys.pstep('Decrypt data (multiproc)', d, True) + + + + @Log() + def prepare_mproc_decode(self, fp, nproc): + """""" + if not Sys.is_cli_cancel(): + self.mproc_fsize = [] + fsize = Sys.getsize(fp) + chsize = (fsize//nproc)+1 + if fsize % chsize == 0 : chsize -= 1 + + hlstPaths = [] + with Io.rfile(fp) as fi : + content = '' + for pdata, part in Io.read_in_chunks(fi, chsize, True): + content = Io.str(pdata) + self.mproc_fsize.append(len(content)) + Io.set_data(self.KMP_FILE+'_'+str(Sys.getpid())+'_'+str(part), content) + hlstPaths.append(self.KMP_FILE+'dec_'+str(Sys.getpid())+'_'+str(part)) + + return hlstPaths + + + @Log() + def mproc_decode_part(self, id, event=None, emit=True): + """""" + if emit : Sys.cli_emit_progress(-1) + if not Sys.is_cli_cancel(): + mpfile, mpfiledec = self.KMP_FILE+'_'+str(Sys.g.MAIN_PROC)+'_'+str(id), self.KMP_FILE+'dec_'+str(Sys.g.MAIN_PROC)+'_'+str(id) + self.decryptToFile(mpfile, mpfiledec, self.getSubStartIndice(id), event, emit=emit) + Sys.removeFile(mpfile) + + + @Log() + def decrypt(self, fromPath, toPath, nproc=1, emit=True): + """""" + Sys.cli_emit_progress(0) + if not Sys.is_cli_cancel(): + fp, tp, compstart = self.decrypt_sp_start(fromPath, toPath, emit=emit) + self.decrypt_mproc(fp, tp, nproc, emit=emit) + self.decrypt_sp_end(tp, toPath, compstart, emit=emit) + + Sys.cli_emit_progress(100) + + + + @Log(Const.LOG_DEBUG) + def offuscate(self, data, index): + """""" + if not Sys.is_cli_cancel(): + adata, lim = [], len(data) + for i, c in enumerate((self.mark2)[index:]) : + if i >= lim : break + d = ord(c) + data[i] + adata.append(chr(d)) + return ''.join(adata) + + + @Log(Const.LOG_DEBUG) + def deoffuscate(self, adata, index): + """""" + if not Sys.is_cli_cancel(): + data, lim = [], len(adata) + for i, c in enumerate((self.mark2)[index:]) : + if i >= lim : break + d = ord(adata[i]) - ord(c) + data.append(d) + return bytes(bytearray(data)) + + + @staticmethod + @Log(Const.LOG_DEBUG) + def getSizes(fromPath): + #~ if not Sys.is_cli_cancel(): + fsize = Sys.getsize(fromPath) + s = (22,44,122,444,1222,14444,52222,244444,522222,1444444) + a = (2,3,7,9,21,33,87,151,427) + m, g = 4000, 3 + chsize = None + if fsize <= 22 : + chsize = ceil(fsize/4)+1 + else : + for i, v in enumerate(s[:-1]) : + if fsize >= v and fsize < s[i+1]: + chsize = ceil(fsize/v)+a[i] + break + if chsize is None : + chsize = ceil(fsize/1444444)+739 + if chsize == 0 : chsize = 1 + while ceil(fsize/chsize) > 4000 : + chsize *= 3 + return fsize, chsize, ceil(fsize/chsize) + + + @staticmethod + @Log() + def getRandomListFromKey(key, size): + """""" + #~ if not Sys.is_cli_cancel(): + j, ok, lk, r, ho, hr, lv, hv, rev = 0, False, len(key), None, [], [], 0, size-1, False + for i in range(size) : + if j >= lk : j = 0 + r = key[j] + ok = r < size and not r in ho + if not ok: + r = hv if not rev else lv + while r in ho : + r = r - 1 if not rev else r + 1 + if r > size-1 : r = 0 + elif r < 0 : r = size - 1 + if not rev : hv = r + else : lv = r + ok = not r in ho + if ok : ho.append(r) + j += 1 + rev = not rev + return Kirmah.getSimulRandomList(ho, Kirmah.getSimulNumber(key, size//5 if not size//5==0 else size*2, size//10 if not size//10 ==0 else size)) + + + @staticmethod + @Log(Const.LOG_DEBUG) + def getSimulRandomList(lst, chsize): + """""" + #~ if not Sys.is_cli_cancel(): + return Kirmah._getSimulRandomList(list(reversed(Kirmah._getSimulRandomList(Kirmah._getSimulRandomList(lst, chsize), 4))),4) + + + @staticmethod + @Log(Const.LOG_PRIVATE) + def _getSimulRandomList(lst, chsize): + """""" + #~ if not Sys.is_cli_cancel(): + size, rlst, pos = len(lst), [], 0 + if chsize > 0 : + for i in range(chsize+1): + for j in range(ceil(size/chsize)+1): + pos = j*chsize+i + if pos in lst and not lst[pos] in rlst: + rlst.append(lst[pos]) + else : rlst = lst + return rlst + + + @staticmethod + @Log(Const.LOG_DEBUG) + def getSimulNumber(key, lim, delta=12): + """""" + #~ if not Sys.is_cli_cancel(): + s = 0 + for c in key[::-1] : + if represents_int(chr(c)): c = int(chr(c)) + if c > 2 and (lim-delta > c + s or c + s < lim + delta ) : + s += c + return s + + + @Log(Const.LOG_DEBUG) + def getSubStartIndice(self, idx): + """""" + return sum([ s for j, s in enumerate(self.mproc_fsize) if j < idx ])%len(self.key) + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Randomiz ~~ + +class Randomiz: + """""" + + @Log(Const.LOG_BUILD) + def __init__(self,count,chl=None): + """""" + if chl ==None : self.lst = list(range(0,count)) + else: self.lst = chl + self.count = len(self.lst) + + + @Log(Const.LOG_DEBUG) + def new(self,count=None, chl=None): + """""" + if count : self.count = count + self.__init__(self.count,chl) + + + def get(self,single=True): + """""" + pos = choice(self.lst) + if single: del self.lst[self.lst.index(pos)] + return pos + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Noiser ~~ + +class Noiser: + + @Log(Const.LOG_BUILD) + def __init__(self, key, part=0): + """""" + self.key = key + self.build(part) + + @Log(Const.LOG_DEBUG) + def build(self, part, vord=22): + """""" + if not part < len(self.key)-1 : raise Exception('part exceed limit') + else : + self.part, v = part, vord + v = int(ceil((self.key[vord]+v)/4.20583)) + self.lns = abs(int(ceil(v/2))-self.key[self.part]+self.key[7]) + self.lne = abs(int(v-self.lns-self.key[self.part+2]-self.key[44]/2.1934)) + if self.lns < 24 : self.lns += 24 + if self.lne < 10 : self.lne += 10 + + @Log(Const.LOG_DEBUG) + def getNoise(self, l, b64encode=True, noBytes=False): + """""" + n = urandom(l) + if b64encode : n = urlsafe_b64encode(n) + if noBytes: + n = str(n,'utf-8') + return n[:l] + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class BadKeyException ~~ + +class BadKeyException(BaseException): + """""" diff --git a/kirmah/gui.py b/kirmah/gui.py new file mode 100755 index 0000000..1cfcdeb --- /dev/null +++ b/kirmah/gui.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah.gui.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module gui ~~ + +from gi.repository import Gtk, GObject, GLib, Gdk, Pango +from os import sep, remove +from os.path import abspath, dirname, join, realpath, basename, getsize, isdir, splitext +from base64 import b64decode, b64encode +from time import time, sleep +from getpass import getuser as getUserLogin +from mmap import mmap +from math import ceil + +from kirmah.crypt import KeyGen, Kirmah, KirmahHeader, ConfigKey, BadKeyException, b2a_base64, a2b_base64, hash_sha256_file +from kirmah.app import KirmahApp, FileNotFoundException, FileNeedOverwriteException +from kirmah.ui import Gui, CliThread +from kirmah import conf +from psr.sys import Sys, Io, Const +from psr.log import Log +from psr.mproc import Manager +import pdb + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class AppGui ~~ + +class AppGui(Gui): + + DEFAULT_KEY = 0 + EXISTING_KEY = 1 + NEW_KEY = 2 + + IS_SOURCE_DEF = False + IS_DEST_DEF = True + + MODE_CRYPT = True + COMPRESSION = True + NPROC = 2 + PROCEED = False + + curKey = 0 + start = False + poslog = 0 + + + @Log(Const.LOG_BUILD) + def __init__(self, wname='window1'): + """""" + self.app = KirmahApp(conf.DEBUG, conf.PCOLOR) + super().__init__(wname) + + + @Log(Const.LOG_UI) + def on_start(self): + """""" + self.app.createDefaultKeyIfNone() + key, size, mark = self.app.getKeyInfos() + + self.curKey = self.DEFAULT_KEY + print('-'*15) + v = self.app.getDefaultKeyPath() + print(v) + self.get('filechooserbutton1').set_filename(self.app.getDefaultKeyPath()) + print(conf.DEFVAL_USER_PATH) + self.get('filechooserbutton3').set_current_folder(conf.DEFVAL_USER_PATH) + devPath = '/home/dev/git_repos/kirmah2.15/' + #~ self.get('filechooserbutton3').set_current_folder(devPath) + self.get('checkbutton1').set_active(conf.DEFVAL_NPROC>=2) + self.get('checkbutton3').set_active(True) + self.get('checkbutton4').set_active(True) + self.get('spinbutton2').set_value(conf.DEFVAL_NPROC) + if conf.DEFVAL_NPROC >= 2: + self.disable('spinbutton2', False) + self.get('checkbutton2').set_active(conf.DEFVAL_MIXMODE) + self.get('checkbutton4').set_active(conf.DEFVAL_RANDOMMODE) + self.get('entry1').set_text(mark) + + Sys.g.UI_AUTO_SCROLL = True + self.textview = self.get('textview1') + self.textview.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1.0)) + self.textview.modify_font(Pango.font_description_from_string ('DejaVu Sans Mono Book 11')) + self.textbuffer = self.textview.get_buffer() + self.tags = self.buildTxtTags(self.textbuffer) + self.progressbar = self.get('progressbar1') + cbt = self.get('comboboxtext1') + cbt.connect("changed", self.on_compression_changed) + tree_iter = cbt.get_model().get_iter_first() + print(cbt.get_model().get_string_from_iter(tree_iter)) + tree_iter = cbt.get_model().get_iter_from_string('3') + cbt.set_active_iter(tree_iter) + cbt = self.get('comboboxtext2') + cbt.connect("changed", self.on_logging_changed) + tree_iter = cbt.get_model().get_iter_first() + tree_iter = cbt.get_model().get_iter_from_string('4') + cbt.set_active_iter(tree_iter) + Sys.clear() + Sys.dprint('INIT UI') + self.start = True + self.thkmh = None + + + @Log(Const.LOG_UI) + def launch_thread(self, *args): + self.progressbar.show() + def getKmhThread(on_completed, on_interrupted, on_progress, userData, *args): + thread = CliThread(*args) + thread.connect("completed" , on_completed , userData) + thread.connect("interrupted", on_interrupted , userData) + thread.connect("progress" , on_progress , userData) + return thread + cliargs = ['kirmah-cli.py', 'split', '-df', '/media/Hermes/webbakup/The Raven.avi', '-z', '-r', '-m', '-o', '/home/dev/git_repos/kirmah2.15/The Raven.avi.kmh'] + cliargs = self.app.getCall() + self.thkmh = getKmhThread(self.thread_finished, self.thread_interrupted, self.thread_progress, None, cliargs, Sys.g.MPEVENT) + self.thkmh.start() + + + @Log(Const.LOG_UI) + def on_mixdata_changed(self, checkbox): + """""" + self.app.setMixMode(not checkbox.get_active()) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_randomdata_changed(self, checkbox): + """""" + self.app.setRandomMode(not checkbox.get_active()) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_multiproc_changed(self, checkbox, data = None): + """""" + disabled = checkbox.get_active() + self.disable('spinbutton2',disabled) + self.app.setMultiprocessing(int(self.get('spinbutton2').get_value()) if not disabled else 0) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_logging_changed(self, combo): + """""" + tree_iter = combo.get_active_iter() + if tree_iter != None: + v = combo.get_model()[tree_iter][:2][0] + if v =='DISABLED' : + Sys.g.DEBUG = False + elif hasattr(Const, v): + Sys.g.DEBUG = True + exec('Sys.g.LOG_LEVEL = Const.'+v) + else : + Sys.g.LOG_LEVEL = Const.LOG_DEFAULT + if self.start: self.refreshProceed() + + + @Log(Const.LOG_DEFAULT) + def on_compression_changed(self, combo): + tree_iter = combo.get_active_iter() + if tree_iter != None: + model = combo.get_model() + comp = KirmahHeader.COMP_END if model[tree_iter][:2][0]=='yes' else (KirmahHeader.COMP_NONE if model[tree_iter][:2][0]=='no' else KirmahHeader.COMP_ALL) + print(comp) + self.app.setCompression(comp) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_nproc_changed(self, spin): + """""" + self.app.setMultiprocessing(int(spin.get_value())) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_keylen_changed(self, spin): + """""" + filename = self.get('filechooserbutton1').get_filename() + if Io.file_exists(filename): + self.app.createNewKey(filename, int(self.get('spinbutton1').get_value())) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_new_file_key(self, fc): + """""" + filename = fc.get_filename() + if self.curKey == self.NEW_KEY: + self.app.createNewKey(filename, int(self.get('spinbutton1').get_value())) + self.app.selectKey(filename) + k, s, m = self.app.getKeyInfos(filename) + self.get('spinbutton1').set_value(s) + self.get('entry1').set_text(m) + self.get('filechooserbutton1').set_filename(filename) + if self.curKey == self.NEW_KEY: + self.get('radiobutton2').set_active(True) + self.disable('spinbutton1', True) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_NEVER) + def on_switch_mode(self, s, data): + """""" + self.app.switchEncMode(not s.get_active()) + if not self.app.splitmode : + for n in ['checkbutton2','checkbutton4','comboboxtext1','label12']: + self.disable(n, not self.app.encmode) + #~ self.on_new_file_dest(self.get('filechooserbutton3')) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_NEVER) + def on_switch_format(self, s, data): + """""" + self.app.switchFormatMode(not s.get_active()) + if self.app.encmode : + for n in ['checkbutton1', 'spinbutton2', 'checkbutton2','checkbutton4','comboboxtext1','label12']: + self.disable(n, self.app.splitmode) + if not s.get_active() : + self.get('label8').set_text('encrypt') + self.get('label9').set_text('decrypt') + self.get('checkbutton1').set_sensitive(True) + self.get('spinbutton2').set_sensitive(True) + else : + self.get('label8').set_text('split') + self.get('label9').set_text('merge') + self.get('checkbutton1').set_sensitive(False) + self.get('spinbutton2').set_sensitive(False) + if self.start: self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_new_file_source(self, fc, data=None): + """""" + try: + self.app.setSourceFile(fc.get_filename()) + self.IS_SOURCE_DEF = True + except FileNotFoundException as e: + Sys.eprint('FileNotFoundException :' + str(fc.get_filename()), Const.ERROR) + self.IS_SOURCE_DEF = False + self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_new_file_dest(self, fc, data=None): + """""" + try : + self.app.setDestFile(fc.get_filename()) + print(self.app.dst) + except Exception as e : + print(e) + pass + if self.start: + self.IS_DEST_DEF = True + self.refreshProceed() + + + @Log(Const.LOG_UI) + def on_existing_key(self, button): + """""" + self.curKey = self.EXISTING_KEY + self.disable('spinbutton1',True) + self.disable('filechooserbutton1',False) + self.get('filechooserbutton1').set_filename(self.app.kpath) + fc = self.get('filechooserbutton1') + self.on_new_file_key(fc) + + + @Log(Const.LOG_UI) + def on_new_key(self, button): + """""" + self.curKey = self.NEW_KEY + self.disable('spinbutton1',False) + self.disable('filechooserbutton1',False) + self.get('filechooserbutton1').set_current_folder(conf.DEFVAL_UKEY_PATH) + self.get('filechooserbutton1').set_filename(conf.DEFVAL_UKEY_PATH+'.rename.key') + + + @Log(Const.LOG_UI) + def on_default_key(self, button): + """""" + self.curKey = self.DEFAULT_KEY + self.disable('spinbutton1',True) + self.disable('filechooserbutton1',True) + fc = self.get('filechooserbutton1') + fc.set_filename(self.app.getDefaultKeyPath()) + self.on_new_file_key(fc) + + + @Log(Const.LOG_UI) + def on_autoscroll_changed(self, btn): + """""" + Sys.g.UI_AUTO_SCROLL = not btn.get_active() + + + @Log(Const.LOG_NEVER) + def clear_log(self, btn): + """""" + self.textbuffer.set_text('') + + + @Log(Const.LOG_UI) + def show_log(self): + """""" + btn = self.get('button1') + if not self.PROCEED : + self.get('frame3').hide() + self.get('frame1').show() + self.get('frame2').show() + self.get('checkbutton3').hide() + self.repack('frame4', True) + btn.set_sensitive(self.IS_DEST_DEF and self.IS_SOURCE_DEF) + + else : + self.repack('frame4', False) + self.get('frame1').hide() + self.get('frame2').hide() + self.get('frame3').show() + self.get('checkbutton3').show() + if btn.get_label() == conf.GUI_LABEL_PROCEED : + btn.set_sensitive(False) + + + @Log(Const.LOG_UI) + def refreshProceed(self): + """""" + #~ if self.start : + self.get('button1').set_sensitive(self.IS_DEST_DEF and self.IS_SOURCE_DEF) + + + @Log(Const.LOG_UI) + def on_proceed(self, btn): + """""" + if btn.get_label() == conf.GUI_LABEL_OK : + btn.set_label(conf.GUI_LABEL_PROCEED) + self.PROCEED = False + self.pb.hide() + self.show_log() + + else : + if not self.PROCEED : + self.PROCEED = True + self.STOPPED = False + btn.set_sensitive(False) + self.app.setDestFile(self.get('filechooserbutton3').get_filename()) + if not Io.file_exists(self.app.dst) or self.warnDialog('file '+self.app.dst+' already exists', 'Overwrite file ?'): + self.pb = self.get('progressbar1') + self.pb.set_fraction(0) + self.pb.show() + self.pb.pulse() + btn.set_sensitive(True) + btn.set_label(conf.GUI_LABEL_CANCEL) + self.clear_log(self.get('checkbutton3')) + self.show_log() + self.launch_thread() + else : + self.on_proceed_end(True) + else : + self.halt_thread() + + + @Log(Const.LOG_UI) + def halt_thread(self, *args): + Sys.wlog(Sys.dprint()) + Sys.pwarn(('thread interrupt',), False) + self.get('button1').set_sensitive(False) + if self.thkmh is not None and self.thkmh.isAlive(): + self.thkmh.cancel() + else : + self.textbuffer.insert_at_cursor('Kmh Thread is not Alive\n') + self.on_proceed_end(True) + self.pb.hide() + self.show_log() + + + @Log(Const.LOG_UI) + def on_proceed_end(self, abort=False): + """""" + try : + btn = self.get('button1') + btn.set_label('Proceed') + btn.set_sensitive(True) + self.PROCEED = False + if not abort : btn.set_label(conf.GUI_LABEL_OK) + self.get('checkbutton3').hide() + + except Exception as e: + Sys.pwarn((('on_proceed_end : ',(str(e),Sys.CLZ_WARN_PARAM), ' !'),), False) + pass + return False diff --git a/kirmah/ui.py b/kirmah/ui.py new file mode 100755 index 0000000..3f236a1 --- /dev/null +++ b/kirmah/ui.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah/ui.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module ui ~~ + +from gi.repository import Pango +from gi.repository.Gdk import threads_enter, threads_leave +from gi.repository.Gtk import AboutDialog, Builder, main as main_enter, main_quit, MessageDialog, MessageType, ButtonsType, ResponseType, PackType +from gi.repository.GdkPixbuf import Pixbuf +from gi.repository.GObject import threads_init, GObject, idle_add, SIGNAL_RUN_LAST, TYPE_NONE, TYPE_STRING, TYPE_FLOAT, TYPE_BOOLEAN +from threading import Thread, current_thread, enumerate as thread_enum +from multiprocessing import Event +from psr.sys import Sys, Io, Const +from psr.log import Log +from kirmah import conf +from kirmah.cli import Cli + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Gui ~~ + +class Gui(): + + + @Log(Const.LOG_BUILD) + def __init__(self, wname): + """""" + threads_init() + self.wname = wname + self.builder = Builder() + self.builder.add_from_file(conf.PRG_GLADE_PATH) + self.builder.connect_signals(self) + self.win = self.get(wname) + self.win.connect('destroy', self.onDeleteWindow) + self.win.connect('delete-event', self.onDeleteWindow) + self.win.set_title(conf.PRG_NAME+' v'+conf.PRG_VERS) + self.win.show_all() + self.on_start() + main_enter() + + + @Log(Const.LOG_DEBUG) + def buildTxtTags(self, textbuffer): + tags = {} + tags[Const.CLZ_TIME] = textbuffer.create_tag(Const.CLZ_TIME , foreground="#208420", weight=Pango.Weight.BOLD) + tags[Const.CLZ_SEC] = textbuffer.create_tag(Const.CLZ_SEC , foreground="#61B661", weight=Pango.Weight.BOLD) + tags[Const.CLZ_DEFAULT] = textbuffer.create_tag(Const.CLZ_DEFAULT , foreground="#FFEDD0") + tags[Const.CLZ_IO] = textbuffer.create_tag(Const.CLZ_IO , foreground="#EB3A3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_FUNC] = textbuffer.create_tag(Const.CLZ_FUNC , foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_CFUNC] = textbuffer.create_tag(Const.CLZ_CFUNC , foreground="#EBB33A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_DELTA] = textbuffer.create_tag(Const.CLZ_DELTA , foreground="#397BE8", weight=Pango.Weight.BOLD) + tags[Const.CLZ_ARGS] = textbuffer.create_tag(Const.CLZ_ARGS , foreground="#A1A1A1") + tags[Const.CLZ_ERROR] = textbuffer.create_tag(Const.CLZ_ERROR , background="#830005", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_ERROR_PARAM] = textbuffer.create_tag(Const.CLZ_ERROR_PARAM , background="#830005", foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_WARN] = textbuffer.create_tag(Const.CLZ_WARN , background="#A81459", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_WARN_PARAM] = textbuffer.create_tag(Const.CLZ_WARN_PARAM , background="#A81459", foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_PID] = textbuffer.create_tag(Const.CLZ_PID , background="#5B0997", foreground="#E4C0FF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_CPID] = textbuffer.create_tag(Const.CLZ_CPID , background="#770997", foreground="#F4CDFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_SYMBOL] = textbuffer.create_tag(Const.CLZ_SYMBOL , background="#61B661", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_OK] = textbuffer.create_tag(Const.CLZ_OK , background="#167B3B", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_KO] = textbuffer.create_tag(Const.CLZ_KO , background="#7B1716", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_TITLE] = textbuffer.create_tag(Const.CLZ_TITLE , foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_TASK] = textbuffer.create_tag(Const.CLZ_TASK , foreground="#61B661", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_APP] = textbuffer.create_tag(Const.CLZ_HEAD_APP , background="#2B5BAB", foreground="#FFFFFF", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_SEP] = textbuffer.create_tag(Const.CLZ_HEAD_SEP , foreground="#A1A1A1") + tags[Const.CLZ_HEAD_KEY] = textbuffer.create_tag(Const.CLZ_HEAD_KEY , foreground="#EBEB3A", weight=Pango.Weight.BOLD) + tags[Const.CLZ_HEAD_VAL] = textbuffer.create_tag(Const.CLZ_HEAD_VAL , foreground="#397BE8", weight=Pango.Weight.BOLD) + return tags + + + @Log(Const.LOG_UI) + def onDeleteWindow(self, *args): + """""" + mthread = current_thread() + try: + self.join_threads(True) + self.cleanResources() + + except Exception as e: + pass + + finally: + main_quit(*args) + + @Log() + def beforeDelete(self): + """""" + + + @Log(Const.LOG_UI) + def list_threads(self): + """""" + print('thread list : ') + for th in thread_enum(): + print(th) + + + @Log(Const.LOG_UI) + def join_threads(self, join_main=False): + """""" + mthread = current_thread() + try: + for th in thread_enum(): + if th is not mthread : + th.join() + if join_main: mthread.join() + + except Exception as e: + pass + + + @Log(Const.LOG_UI) + def on_about(self, btn): + """""" + about = AboutDialog() + about.set_program_name(conf.PRG_NAME) + about.set_version('v '+conf.PRG_VERS) + about.set_copyright(conf.PRG_ABOUT_COPYRIGHT) + about.set_comments(conf.PRG_ABOUT_COMMENTS) + about.set_website(conf.PRG_WEBSITE) + about.set_website_label(conf.PRG_WEBSITE) + about.set_license(Io.get_data(conf.PRG_LICENSE_PATH)) + pixbuf = Pixbuf.new_from_file_at_size(conf.PRG_LOGO_PATH, conf.PRG_ABOUT_LOGO_SIZE, conf.PRG_ABOUT_LOGO_SIZE) + about.set_logo(pixbuf) + pixbuf = Pixbuf.new_from_file_at_size(conf.PRG_LOGO_PATH, conf.PRG_ABOUT_LOGO_SIZE, conf.PRG_ABOUT_LOGO_SIZE) + about.set_icon(pixbuf) + about.run() + about.destroy() + + + @Log(Const.LOG_DEBUG) + def get(self, name): + """""" + return self.builder.get_object(name) + + + @Log(Const.LOG_DEBUG) + def disable(self, name, disabled): + """""" + self.get(name).set_sensitive(not disabled) + + + @Log(Const.LOG_DEBUG) + def repack(self, name, expandfill=False, packStart=True): + w = self.get(name) + w.get_parent().set_child_packing(w, expandfill, expandfill, 0, PackType.START if packStart else PackType.END ) + return w + + + @Log(Const.LOG_DEBUG) + def detachWidget(self, name, hideParent=True): + w = self.get(name) + wp = w.get_parent() + if wp is not None : + wp.remove(w) + w.unparent() + if hideParent : wp.hide() + + + @Log(Const.LOG_DEBUG) + def attachWidget(self, widget, parentName, expandfill=None, showParent=True): + if widget is not None : + wp = self.get(parentName) + if wp is not None : + if expandfill is None : wp.add(widget) + else : + wp.pack_start(widget,expandfill,expandfill,0) + if showParent : wp.show() + + + @Log(Const.LOG_UI) + def thread_finished(self, thread, ref): + thread = None + self.on_proceed_end(False) + + + @Log(Const.LOG_UI) + def on_proceed_end(self, abort=False): + """""" + + + @Log(Const.LOG_UI) + def thread_interrupted(self, thread, ref): + thread = None + self.end_progress() + self.on_proceed_end(False) + + + @Log(Const.LOG_NEVER) + def thread_progress(self, thread, progress, ref): + while not Sys.g.LOG_QUEUE.empty(): + data = Sys.g.LOG_QUEUE.get() + if data is not None : + if data is not Sys.g.SIGNAL_STOP : + cth, data = data + for item in data : + ei = self.textbuffer.get_end_iter() + offs = ei.get_offset() + self.textbuffer.insert_at_cursor(item[0]) + ei = self.textbuffer.get_end_iter() + oi = self.textbuffer.get_iter_at_offset(offs) + tagName = item[1] + self.textbuffer.apply_tag(self.tags[tagName], oi, ei) + self.textbuffer.insert_at_cursor('\n') + self.scroll_end() + else : + Sys.dprint('STOP') + thread.cancel() + self.update_progress(progress) + + + @Log(Const.LOG_NEVER) + def update_progress(self, progress, lvl=50): + if progress > 0 : + self.progressbar.set_text(str(progress)) + lp = self.progressbar.get_fraction() + diff = (progress/100.0 - lp) + for i in range(lvl): + nf = lp+(i*diff/lvl) + if nf < progress/100.0 : + self.progressbar.set_fraction(nf) + self.progressbar.set_fraction(progress/100.0) + else : + self.progressbar.set_fraction(self.progressbar.get_fraction()+0.01) + + + @Log(Const.LOG_NEVER) + def end_progress(self): + self.update_progress(100, 10) + + + @Log(Const.LOG_NEVER) + def scroll_end(self): + if Sys.g.UI_AUTO_SCROLL : + if self.textbuffer is not None : + insert_mark = self.textbuffer.get_insert() + ei = self.textbuffer.get_end_iter() + if ei is not None and insert_mark is not None: + self.textbuffer.place_cursor(ei) + self.textview.scroll_to_mark(insert_mark , 0.0, True, 0.0, 1.0) + + + @Log(Const.LOG_UI) + def cleanResources(self): + """""" + + + @Log(Const.LOG_UI) + def on_start(self): + """""" + + + @Log(Const.LOG_UI) + def warnDialog(self, intro, ask): + """""" + dialog = MessageDialog(self.get(self.wname), 0, MessageType.WARNING, ButtonsType.OK_CANCEL, intro) + dialog.format_secondary_text(ask) + response = dialog.run() + dialog.destroy() + return response == ResponseType.OK; + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class IdleObject ~~ + +class IdleObject(GObject): + """ + Override gi.repository.GObject to always emit signals in the main thread + by emmitting on an idle handler + """ + + @Log(Const.LOG_UI) + def __init__(self): + """""" + GObject.__init__(self) + + + @Log(Const.LOG_NEVER) + def emit(self, *args): + """""" + idle_add(GObject.emit, self, *args) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class CliThread ~~ + +class CliThread(Thread, IdleObject): + """ + Cancellable thread which uses gobject signals to return information + to the GUI. + """ + __gsignals__ = { # signal type signal return signal args + "completed" : ( SIGNAL_RUN_LAST, TYPE_NONE, ()), + "interrupted" : ( SIGNAL_RUN_LAST, TYPE_NONE, ()), + "progress" : ( SIGNAL_RUN_LAST, TYPE_NONE, (TYPE_FLOAT,)) + } + + + @Log(Const.LOG_DEBUG) + def __init__(self, rwargs, event): + Thread.__init__(self) + IdleObject.__init__(self) + self.setName('CliThread') + self.cliargs = rwargs + self.event = event + + @Log(Const.LOG_DEBUG) + def run(self): + """""" + self.cancelled = False + Sys.g.MPEVENT.clear() + print(Sys.g.LOG_LEVEL) + Cli('./', Sys.getpid(), self.cliargs, self, Sys.g.LOG_LEVEL) + self.emit("completed") + + + @Log(Const.LOG_NEVER) + def progress(self, value): + """""" + self.emit("progress", value) + + + @Log(Const.LOG_NEVER) + def cancel(self): + """ + Threads in python are not cancellable, so we implement our own + cancellation logic + """ + self.cancelled = True + self.event.set() + + + @Log(Const.LOG_NEVER) + def stop(self): + """""" + if self.isAlive(): + self.cancel() + if current_thread().getName()==self.getName(): + try: + self.emit("interrupted") + Sys.thread_exit() + except RuntimeError as e : + print(str(self.getName()) + ' COULD NOT BE TERMINATED') + raise e diff --git a/launcher.bat b/launcher.bat index efead5f..2c57bbb 100644 --- a/launcher.bat +++ b/launcher.bat @@ -1,28 +1,29 @@ -:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -:: # -:: software : ImpraStorage # -:: version : 0.8 # -:: date : 2012 # -:: licence : GPLv3.0 # -:: author : a-Sansara # -:: copyright : pluie.org # -:: # -:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # :: -:: This file is part of ImpraStorage. +:: software : ImpraStorage +:: version : 1.01 +:: date : 2014 +:: licence : GPLv3.0 +:: author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +:: copyright : pluie.org :: -:: ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # :: -:: ImpraStorage 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. +:: This file is part of ImpraStorage. +:: +:: ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +:: +:: ImpraStorage 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 ImpraStorage. If not, see . :: -:: You should have received a copy of the GNU General Public License -:: along with ImpraStorage. If not, see . @Echo Off Set _Title=ImpraStorage Set _Height=60 diff --git a/psr/__init__.py b/psr/__init__.py new file mode 100755 index 0000000..b28b04f --- /dev/null +++ b/psr/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/psr/cli.py b/psr/cli.py new file mode 100755 index 0000000..40e7192 --- /dev/null +++ b/psr/cli.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module cli ~~ + +from optparse import OptionParser, OptionGroup +from psr.sys import Sys, Io, Const, init +from psr.log import Log + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class TinyParser ~~ + +class TinyParser(OptionParser): + + + def format_description(self, formatter): + """""" + return self.description + + + def format_epilog(self, formatter): + """""" + return self.epilog + + + def error(self, errMsg, errData=None): + """""" + self.print_usage('') + self.error_cmd((errMsg,)) + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class AbstractConf ~~ + +class YourConf(): + + PRG_NAME = 'your-program' + PRG_VERS = '1.0' + PRG_AUTHOR = 'you' + PRG_LICENSE = 'GNU GPL v3' + PRG_COPY = 'company' + PRG_CLI_NAME = 'your-cli-program' + PRG_DESC = 'your desc' + +# you must provide a global conf object + +prgconf = YourConf() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class AbstractCli ~~ + +class AbstractCli(): + + conf = YourConf() + + def __init__(self, prgconf=None, *args, **kwargs): + """""" + if not Sys.isUnix : Const.LINE_SEP_CHAR = '-' + AbstractCli.conf = prgconf + self.CHQ = "'" + self.parser = TinyParser() + self.parser.print_help = self.print_help + self.parser.print_usage = self.print_usage + self.parser.error_cmd = self.error_cmd + + self.parser.add_option('-v', '--version' , action='store_true', default=False) + self.parser.add_option('-d', '--debug' , action='store_true', default=False) + self.parser.add_option('-f', '--force' , action='store_true', default=False) + self.parser.add_option('-q', '--quiet' , action='store_true', default=False) + + self.parser.add_option('--no-color' , action='store_true' , default=False) + + + def error_cmd(self, data, pusage=False): + """""" + if pusage : self.print_usage('') + Sys.dprint() + Sys.pwarn(data, True) + AbstractCli.exit(1) + + @staticmethod + def exit(code): + """""" + if Sys.isUnix() : Sys.exit(code) + + + @staticmethod + def print_header(): + """""" + a = AbstractCli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + b = AbstractCli.printHeaderTitle(AbstractCli.conf.PRG_CLI_NAME) + c = AbstractCli.printHeaderPart('version' ,AbstractCli.conf.PRG_VERS) + d = AbstractCli.printHeaderPart('author' ,AbstractCli.conf.PRG_AUTHOR) + e = AbstractCli.printHeaderPart('license' ,AbstractCli.conf.PRG_LICENSE) + f = AbstractCli.printHeaderPart('copyright',AbstractCli.conf.PRG_COPY) + Sys.echo(' ', Sys.Clz.OFF) + AbstractCli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + Sys.wlog(a) + Sys.wlog(b + c + d + e + f ) + Sys.wlog(a) + #~ Sys.wlog(Sys.dprint()) + + + @staticmethod + def printLineSep(sep,lenSep): + """""" + s = sep*lenSep + Sys.echo(s, Sys.CLZ_HEAD_LINE) + return [(s, Const.CLZ_HEAD_SEP)] + + + @staticmethod + def printHeaderTitle(title): + """""" + s = ' == '+title+' == ' + Sys.echo(s, Sys.CLZ_HEAD_APP, False, True) + return [(s, Const.CLZ_HEAD_APP)] + + + @staticmethod + def printHeaderPart(label,value): + """""" + a, b, c = ' [',':' ,'] ' + Sys.echo(a , Sys.CLZ_HEAD_SEP, False) + Sys.echo(label, Sys.CLZ_HEAD_KEY, False) + Sys.echo(b , Sys.CLZ_HEAD_SEP, False) + Sys.echo(value, Sys.CLZ_HEAD_VAL, False) + Sys.echo(c , Sys.CLZ_HEAD_SEP, False) + return [(a,Const.CLZ_HEAD_SEP),(label,Const.CLZ_HEAD_KEY),(b,Const.CLZ_HEAD_SEP),(value,Const.CLZ_HEAD_VAL),(c,Const.CLZ_HEAD_SEP)] + + + @staticmethod + def print_version(data): + """""" + AbstractCli.print_header() + + + @staticmethod + def print_options(): + """""" + Sys.dprint('\n') + AbstractCli.printLineSep(Const.LINE_SEP_CHAR,Const.LINE_SEP_LEN) + + Sys.echo(' MAIN OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-v'.ljust(13,' ')+', --version' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display programm version' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-d'.ljust(13,' ')+', --debug' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'enable debug mode' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-f'.ljust(13,' ')+', --force' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'force rewriting existing files without alert' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-q'.ljust(13,' ')+', --quiet' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'don\'t print status messages to stdout' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-h'.ljust(13,' ')+', --help' , Sys.CLZ_HELP_ARG) + Sys.echo(' '*50+'display help' , Sys.CLZ_HELP_ARG_INFO) + + Sys.dprint('\n') + Sys.echo(' KEY OPTIONS :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '*4+'-a ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('VALUE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --bind_opt_a'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('VALUE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'description option a' , Sys.CLZ_HELP_ARG_INFO) + Sys.echo(' '*4+'-b ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('VALUE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM, False) + Sys.echo(', --bind_opt_b'.ljust(18,' ') , Sys.CLZ_HELP_ARG, False) + Sys.echo('VALUE'.ljust(10,' ') , Sys.CLZ_HELP_PARAM) + Sys.echo(' '*50+'description option b' , Sys.CLZ_HELP_ARG_INFO) + + + + def print_usage(self, data, withoutHeader=False): + """""" + if not withoutHeader : AbstractCli.print_header() + + Sys.echo(' USAGE :\n' , Sys.CLZ_HELP_CMD) + Sys.echo(' '+AbstractCli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('help ' , Sys.CLZ_HELP_CMD) + + Sys.echo(' '+AbstractCli.conf.PRG_CLI_NAME+' ' , Sys.CLZ_HELP_PRG, False) + Sys.echo('cmd ' , Sys.CLZ_HELP_CMD, False) + Sys.echo('[ -a ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('param_a' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(' -b ' , Sys.CLZ_HELP_ARG, False) + Sys.echo('{' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('param_b' , Sys.CLZ_HELP_PARAM, False) + Sys.echo('}' , Sys.CLZ_HELP_PARAM, False) + Sys.echo(']' , Sys.CLZ_HELP_ARG) + + + @staticmethod + def print_help(): + """""" diff --git a/psr/const.py b/psr/const.py new file mode 100755 index 0000000..414de76 --- /dev/null +++ b/psr/const.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/const.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module const ~~ + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Const ~~ + +class Const: + + LOG_NEVER = -1 + LOG_ALL = 0 + LOG_BUILD = 1 + LOG_PRIVATE = 2 + LOG_DEBUG = 3 + LOG_WARN = 4 + LOG_UI = 5 + LOG_DEFAULT = 6 + LOG_APP = 7 + + CLZ_TIME = 'time' + CLZ_SEC = 'sec' + CLZ_CPID = 'cpid' + CLZ_PID = 'pid' + CLZ_IO = 'io' + CLZ_FUNC = 'func' + CLZ_CFUNC = 'cfunc' + CLZ_ARGS = 'args' + CLZ_DELTA = 'delta' + CLZ_ERROR = 'error' + CLZ_ERROR_PARAM = 'errorp' + CLZ_WARN = 'warn' + CLZ_WARN_PARAM = 'warnp' + CLZ_DEFAULT = 'default' + CLZ_TITLE = 'title' + CLZ_OK = 'ok' + CLZ_KO = 'ko' + CLZ_TASK = 'task' + CLZ_SYMBOL = 'symbol' + CLZ_ACTION = 'action' + CLZ_INIT = 'init' + + CLZ_0 = 'color0' + CLZ_1 = 'color1' + CLZ_2 = 'color2' + CLZ_3 = 'color3' + CLZ_4 = 'color4' + CLZ_5 = 'color5' + CLZ_6 = 'color6' + CLZ_7 = 'color7' + + CLZ_HEAD_APP = 'headapp' + CLZ_HEAD_SEP = 'headsep' + CLZ_HEAD_KEY = 'headkey' + CLZ_HEAD_VAL = 'headval' + + ERROR = 'ERROR' + WARN = 'WARNING' + OK = 'OK' + KO = 'KO' + + LOG_LIM_ARG_LENGTH = 20 + + LF = """ +""" + LF_STR = '\n' + + UNIT_SHORT_B = 'B' + UNIT_SHORT_KIB = 'KiB' + UNIT_SHORT_MIB = 'MiB' + UNIT_SHORT_GIB = 'GiB' + UNIT_SHORT_TIB = 'TiB' + + LINE_SEP_LEN = 100 + LINE_SEP_CHAR = '-' + +const = Const() diff --git a/psr/imap.py b/psr/imap.py new file mode 100755 index 0000000..0d81248 --- /dev/null +++ b/psr/imap.py @@ -0,0 +1,553 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/imap.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module imap ~~ + +from imaplib import Commands, IMAP4_SSL, Time2Internaldate +from binascii import b2a_base64, a2b_base64 +from codecs import register, StreamReader, StreamWriter +from email import message_from_bytes +from email.header import decode_header +from email.message import Message +from re import search as research, split as resplit +from multiprocessing import Process +from psr.sys import Io, Sys, Const +from psr.log import Log + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ ImapUtf7 decoding/encoding ~~ + +def _seq_encode(seq,l): + """""" + if len(seq) > 0 : + l.append('&%s-' % str(b2a_base64(bytes(''.join(seq),'utf-16be')),'utf-8').rstrip('\n=').replace('/', ',')) + elif l: + l.append('-') + + +def _seq_decode(seq,l): + """""" + d = ''.join(seq[1:]) + pad = 4-(len(d)%4) + l.append(str(a2b_base64(bytes(d.replace(',', '/')+pad*'=','utf-16be')),'utf-16be')) + + +def encode(s): + """""" + l, e, = [], [] + for c in s : + if ord(c) in range(0x20,0x7e): + if e : _seq_encode(e,l) + e = [] + l.append(c) + if c == '&' : l.append('-') + else : + e.append(c) + if e : _seq_encode(e,l) + return ''.join(l) + + +def decode(s): + """""" + l, d = [], [] + for c in s: + if c == '&' and not d : d.append('&') + elif c == '-' and d: + if len(d) == 1: l.append('&') + else : _seq_decode(d,l) + d = [] + elif d: d.append(c) + else: l.append(c) + if d: _seq_decode(d,l) + return ''.join(l) + + +def _encoder(s): + """""" + e = bytes(encode(s),'utf-8') + return e, len(e) + + +def _decoder(s): + """""" + d = decode(str(s,'utf-8')) + return d, len(d) + + +def _codec_imap4utf7(name): + """""" + if name == 'imap4-utf-7': + return (_encoder, _decoder, Imap4Utf7StreamReader, Imap4Utf7StreamWriter) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ StreamReader & StreamWriter ~~ + +class Imap4Utf7StreamReader(StreamReader): + def decode(self, s, errors='strict'): return _decoder(s) + +class Imap4Utf7StreamWriter(StreamWriter): + def decode(self, s, errors='strict'): return _encoder(s) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ registering codec ~~ + +register(_codec_imap4utf7) + + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Imap utilities ~~ + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImapConfig ~~ + +class ImapConfig: + """""" + + def __init__(self, host, user, pwd, port='993'): + """""" + self.host = host + self.user = user + self.pwd = pwd + self.port = str(port) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImapClient ~~ + +class ImapClient(IMAP4_SSL): + """""" + + Commands['XLIST'] = ('AUTH', 'SELECTED') + + @Log(Const.LOG_DEBUG) + def xlist(self, directory='""', pattern='*'): + """(X)List mailbox names in directory matching pattern. Using Google's XLIST extension + + (status, [data]) = .xlist(directory='""', pattern='*') + + 'data' is list of XLIST responses. + + thks to barduck : http://stackoverflow.com/users/602242/barduck + """ + try : + name = 'XLIST' + status, data = self._simple_command(name, directory, pattern) + return self._untagged_response(status, data, name) + except : + return 'NO', '' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImapHelper ~~ + +class ImapHelper: + """""" + + K_HEAD, K_DATA = 0, 1 + """""" + OK = 'OK' + """""" + KO = 'NO' + """""" + ENCODING = 'utf-8' + """""" + REG_SATUS = r'^"?(.*)"? \(([^\(]*)\)' + """""" + NO_SELECT = '\\Noselect' + """""" + CHILDREN = '\\HasChildren' + """""" + NO_CHILDREN = '\\HasNoChildren' + """""" + INBOX = '\\Inbox' + """""" + DRAFTS = '\\Drafts' + """""" + TRASH = '\\Trash' + """""" + SENT = '\\Sent' + """""" + DELETED = '\\Deleted' + """""" + FLAGS = '+FLAGS' + """""" + + @Log(Const.LOG_BUILD) + def __init__(self, conf, box='INBOX', noBoxCreat=False): + """""" + if conf.host != None and research('yahoo.com', conf.host) is not None : + self.DRAFTS = self.DRAFTS[:-1] + self.conf = conf + self.rootBox = box + self.BOXS = {} + self.cnx = None + self.cnxusr = None + self.noBoxCreat = noBoxCreat + self.switchAccount(self.conf, self.rootBox, True) + + @Log() + def reconnect(self): + """""" + Sys.pwlog([(' Reconnecting... ', Const.CLZ_7, True)]) + self.switchAccount(self.conf, self.rootBox, True) + + + @Log() + def switchAccount(self, conf, box='INBOX', force=False): + """""" + if force or self.cnx is None or self.cnxusr is not conf.user : + try : + Sys.pwlog([(' Attempt to login... ' , Const.CLZ_7), + ('(' , Const.CLZ_0), + (conf.user , Const.CLZ_2), + ('@' , Const.CLZ_0), + (conf.host , Const.CLZ_3), + (':' , Const.CLZ_0), + (conf.port , Const.CLZ_4), + (')' , Const.CLZ_0, True)]) + + self.cnx = ImapClient(conf.host,conf.port) + except Exception as e : + raise BadHostException() + + try : + status, resp = self.cnx.login(conf.user,conf.pwd) + + except Exception as e : + status = self.KO + pass + finally : + if status == self.KO : + self.cnxusr = None + raise BadLoginException(' Cannot login with '+conf.user+':'+conf.pwd) + else : + Sys.pwlog([(' Connected ', Const.CLZ_2, True), + (Const.LINE_SEP_CHAR*Const.LINE_SEP_LEN , Const.CLZ_0, True)]) + self.cnxusr = conf.user + try : + status, resp = self.cnx.select(self.rootBox) + if status == self.KO and not self.noBoxCreat: + self.createBox(self.rootBox) + status, resp = self.cnx.select(self.rootBox) + self.initBoxNames() + except Exception as e : + print(e) + + + @Log() + def createBox(self, box): + """""" + status, resp = self.cnx.create(encode(box)) + return status==self.OK + + + @Log() + def deleteBox(self, box): + """""" + status, resp = self.cnx.delete(encode(box)) + return status==self.OK + + + @Log(Const.LOG_DEBUG) + def initBoxNames(self): + """""" + status, resp = self.cnx.xlist() + if status == self.OK : + bdef, bname, c = None, None, None + for c in resp : + bdef, bname = c[1:-1].split(b') "/" "') + if bdef == Io.bytes(self.NO_SELECT+' '+self.CHILDREN) : + self.BOXS['/'] = Io.str(bname) + elif bdef == Io.bytes(self.NO_CHILDREN+' '+self.INBOX) : + self.BOXS[self.INBOX] = self.INBOX[1:].upper() + elif bdef == Io.bytes(self.NO_CHILDREN+' '+self.DRAFTS) : + self.BOXS[self.DRAFTS] = Io.str(bname) + elif bdef == Io.bytes(self.NO_CHILDREN+' '+self.TRASH) : + self.BOXS[self.TRASH] = Io.str(bname) + elif bdef == Io.bytes(self.NO_CHILDREN+' '+self.SENT) : + self.BOXS[self.SENT] = Io.str(bname) + else : + self.BOXS = { '/' : '/', self.INBOX : self.INBOX[1:].upper(), self.DRAFTS : self.DRAFTS[1:], self.TRASH : self.TRASH[1:], self.SENT : self.SENT[1:] } + + + @Log(Const.LOG_DEBUG) + def listBox(self, box='INBOX', pattern='*'): + """""" + status, resp = self.cnx.list(box,pattern) + l = [] + for r in resp : + if r is not None : + name = Io.str(r).split(' "/" ') + l.append((name[0][1:-1].split(' '),name[1][1:-1])) + return l + + + @Log(Const.LOG_DEBUG) + def status(self, box='INBOX'): + """""" + status, resp = self.cnx.status(box, '(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)') + if status == self.OK : + data = research(self.REG_SATUS, Io.str(resp[self.K_HEAD])) + l = resplit(' ',data.group(2)) + dic = {'BOX' : box} + for i in range(len(l)): + if i%2 == 0 : dic[l[i]] = int(l[i+1]) + else : dic = {} + return dic + + + @Log() + def countSeen(self, box='INBOX'): + """""" + s = self.status(box) + return s['MESSAGES']-s['UNSEEN'] + + + @Log() + def countUnseen(self, box='INBOX'): + """""" + return self.status(box)['UNSEEN'] + + + @Log() + def countMsg(self, box='INBOX'): + """""" + return self.status(box)['MESSAGES'] + + + @Log(Const.LOG_DEBUG) + def _ids(self, box='INBOX', search='ALL', charset=None, byUid=False): + """""" + status, resp = self.cnx.select(box) + if status == self.KO : + self.createBox(box) + self.cnx.select(box) + status, resp = self.cnx.search(charset, '(%s)' % search) + return resplit(' ',Io.str(resp[self.K_HEAD])) + + + @Log() + def idsUnseen(self, box='INBOX', charset=None): + """""" + return self._ids(box,'UNSEEN', charset) + + + @Log() + def idsMsg(self, box='INBOX', charset=None): + """""" + return self._ids(box,'ALL', charset) + + + @Log() + def idsSeen(self, box='INBOX', charset=None): + """""" + return self._ids(box,'NOT UNSEEN', charset) + + + @Log(Const.LOG_DEBUG) + def search(self, query, byUid=False): + """""" + if byUid : + status, resp = self.cnx.uid('search', None, query) + else : + status, resp = self.cnx.search(None, query) + ids = [m for m in resp[0].split()] + return ids + + + @Log() + def searchBySubject(self, subject, byUid=False): + """""" + return self.search('(SUBJECT "%s")' % subject, byUid) + + + @Log() + def getUid(self, mid): + """""" + value = '' + status, resp = self.cnx.fetch(mid, '(UID)') + if status==self.OK : + # '$mid (UID $uid)' + value = resp[0][len(str(mid))+6:-1] + return value + + + @Log(Const.LOG_DEBUG) + def fetch(self, mid, query, byUid=False): + """""" + if not byUid : + status, resp = self.cnx.fetch(mid, query) + else: + status, resp = self.cnx.uid('fetch', mid, query) + return status, resp + + + @Log() + def headerField(self, mid, field, byUid=False): + """""" + value = '' + field = field.upper() + query = '(UID BODY[HEADER' + ('])' if field=='*' or field=='ALL' else '.FIELDS (%s)])' % field) + status, resp = self.fetch(mid, query, byUid) + if status==self.OK and resp[0]!=None: + value = Io.str(resp[0][1][len(field)+2:-4]) + return value + + + @Log() + def getSubject(self, mid, byUid=False): + """""" + status, resp = self.fetch(mid, '(UID BODY[HEADER.FIELDS (SUBJECT)])', byUid) + subject = decode_header(str(resp[self.K_HEAD][1][9:-4], 'utf-8'))[0] + s = subject[0] + if subject[1] : + s = str(s,subject[1]) + return s + + + @staticmethod + def _getIdsList(ids): + idslist = None + if isinstance(ids,list): + if len(ids) > 0 and ids[0]!='' and ids[0]!=None: + li = len(ids)-1 + if int(ids[0])+li == int(ids[li]): + idslist = Io.str(ids[0]+b':'+ids[li]) if isinstance(ids[0],bytes) else str(ids[0])+':'+str(ids[li]) + else : + idslist = Io.str(b','.join(ids)) if isinstance(ids[0],bytes) else ','.join(ids) + elif isinstance(ids, int) and ids > 0: + idslist = Io.str(ids) + return idslist + + + @Log() + def delete(self, ids, byUid=False, expunge=True): + """""" + status, delids = None, ImapHelper._getIdsList(ids) + #~ print(delids) + if delids is not None : + if byUid: + status, resp = self.cnx.uid( 'store', delids, self.FLAGS, self.DELETED ) + else : + status, resp = self.cnx.store(delids, self.FLAGS, self.DELETED) + if expunge : + self.cnx.expunge() + return status == self.OK + + + @Log() + def clearTrash(self): + """""" + self.cnx.select(self.BOXS[self.TRASH]) + ids = self.search('ALL',True) + if len(ids) > 0 and ids[0]!='' and ids[0]!=None: + delids = ImapHelper._getIdsList(ids) + status, resp = self.cnx.uid('store', delids, self.FLAGS, self.DELETED ) + + Sys.pwlog([(' Deleting msg ', Const.CLZ_0), + (delids , Const.CLZ_1), + (' '+status , Const.CLZ_7, True)]) + self.cnx.expunge() + self.cnx.select(self.rootBox) + + + @Log() + def getEmail(self, mid, byUid=False): + """""" + status, resp = self.fetch(mid,'(UID RFC822)', byUid) + if status == self.OK and resp[0]!=None: + msg = message_from_bytes(resp[0][1]) + else : + msg = None + return msg + + + @Log(Const.LOG_APP) + def getAttachment(self, msg, toDir='./', byUid=False): + """""" + # self.download(msg, toDir, byUid, False) + # p = Process(target=self.download, args=(msg, toDir, byUid)) + # p.start() + # p.join() + if not isinstance(msg, Message) : + msg = self.getEmail(msg, byUid) + for part in msg.walk(): + filename = part.get_filename() + if part.get_content_maintype() == 'multipart' or not filename : continue + with Io.wfile(Sys.join(toDir, filename)) as fo : + fo.write(part.get_payload(decode=True)) + + + @Log(Const.LOG_APP) + def download(self, msg, toDir, byUid=False, reconError=True): + """""" + try: + if not isinstance(msg, Message) : + msg = self.getEmail(msg, byUid) + for part in msg.walk(): + filename = part.get_filename() + if part.get_content_maintype() == 'multipart' or not filename : continue + with Io.wfile(Sys.join(toDir, filename)) as fo : + fo.write(part.get_payload(decode=True)) + except Exception as e : + print(e) + self.reconnect() + self.download(msg, toDir, byUid, False) + + + @Log() + def send(self, msg, box='INBOX'): + """""" + mid = None + date = Time2Internaldate(Sys.time()) + status, resp = self.cnx.append(box, '\Draft', date, bytes(msg,'utf-8')) + if status==self.OK: + mid = str(resp[0],'utf-8')[11:-11].split(' ') + return mid + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class BadLoginException ~~ + +class BadLoginException(BaseException): + pass + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class BadLoginException ~~ + +class BadHostException(BaseException): + pass diff --git a/psr/ini.py b/psr/ini.py new file mode 100755 index 0000000..ca32bc3 --- /dev/null +++ b/psr/ini.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/ini.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module ini ~~ + +from re import split as regsplit +from psr.sys import Sys, Io, Const +from psr.log import Log + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class IniFile ~~ + +class IniFile: + """Read and write inifile""" + + @Log(Const.LOG_BUILD) + def __init__(self, path): + """""" + self.path = path + self.dic = {'main':{}} + self.read() + if not 'main' in self.dic : + self.dic['main'] = {} + + + @Log(Const.LOG_DEBUG) + def isEmpty(self): + """""" + return len(self.dic)==0 + + + @Log(Const.LOG_DEBUG) + def has(self, key, section='main'): + """""" + d = self.hasSection(section) and (key in self.dic[section]) + return d + + + @Log(Const.LOG_DEBUG) + def hasSection(self, section): + """""" + d = (section in self.dic) + return d + + + @Log(Const.LOG_DEBUG) + def get(self, key, section='main'): + """""" + return self.dic[section][key] + #if section in self.dic : + # return self.dic[section][key] + #else : + # return '' + + + @Log(Const.LOG_DEBUG) + def set(self, key, val, section='main'): + """""" + v = None + if not section in self.dic: + self.dic[section] = {} + if key in self.dic[section]: + v = self.dic[section].pop(key) + self.dic[section][key] = str(val) + return v + + + @Log() + def rem(self, key, section): + """""" + v = None + if section in self.dic : + if key == '*' : + v = self.dic.pop(section) + elif key in self.dic[section]: + v = self.dic[section].pop(key) + return v + + + @Log() + def save(self,path=None): + """""" + Io.set_data(path if path is not None else self.path, '# last updated : '+str(Sys.datetime.now())+Const.LF+self.toString()) + + + @Log(Const.LOG_DEBUG) + def getSection(self, section): + """""" + data = {} + for s in self.dic : + if s.startswith(section, 0) : data[s[len(section)+1:]] = self.dic[s].copy() + return data + + + @Log(Const.LOG_DEBUG) + def getSections(self): + """""" + l = {} + for s in self.dic: + section = s.split('.') + if len(section)> 1 and not section[0] in l : + l[section[0]] = 1 + return [k for i,k in enumerate(l)] + + + @Log(Const.LOG_DEBUG) + def toString(self, section='*'): + """""" + content = '' + main = '' + for s in self.dic: + if section=='*' or section+'.'==s[:len(section)+1]: + if s!='main': + content += Const.LF+'['+s+']'+Const.LF + for k in sorted(self.dic[s]): + k = k.rstrip(' ') + v = self.dic[s][k] if self.dic[s][k] is not None else '' + if s!='main' : + content += k+' = '+str(v)+Const.LF + else : main += k+' = '+str(v)+Const.LF + return main + content + + + @Log(Const.LOG_DEBUG) + def print(self, section='*', withoutSectionName=False): + """""" + if section=='main' or section=='*' : + self.printSection('main', withoutSectionName) + + for s in self.dic: + if section=='*' or section+'.'==s[:len(section)+1]: + if s!='main': + self.printSection(s, withoutSectionName) + + + @Log(Const.LOG_DEBUG) + def printSection(self, sectionName, withoutSectionName=False): + """""" + if sectionName!='main': + Sys.dprint() + if not withoutSectionName : + Sys.echo('['+sectionName+']', Sys.Clz.fgB3) + else: + Sys.echo('['+sectionName.split('.')[1]+']', Sys.Clz.fgB3) + if sectionName in self.dic : + for k in sorted(self.dic[sectionName]): + k = k.rstrip(' ') + a = '' + Sys.echo(k.ljust(10,' ')+' = ' , Sys.Clz.fgn7, False) + if self.dic[sectionName][k] is not None : + if len(self.dic[sectionName][k]) > 98: a = '…' + if Sys.isUnix() or k is not 'key' : + Sys.echo(self.dic[sectionName][k][:98]+a, Sys.Clz.fgN2) + else: + Sys.echo('key is masked', Sys.Clz.fgb1) + + @Log() + def read(self): + """""" + try: + with Io.rfile(self.path, False) as fi: + csection = 'main' + self.dic[csection] = {} + for l in fi: + l = l.rstrip().lstrip() + if len(l) > 0 and not l[0]=='#' : + d = regsplit(' *= *', l , 1) + if len(d)> 1: + self.dic[csection][d[0]] = d[1] if d[1] is not None else '' + elif len(l)>0 and l[0]=='[': + csection = l.strip('[]') + self.dic[csection] = {} + except IOError : + pass + + + @Log() + def delete(self): + Io.removeFile(self.path) + self.dic = {} diff --git a/psr/io.py b/psr/io.py new file mode 100755 index 0000000..1a105be --- /dev/null +++ b/psr/io.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/io.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module io ~~ + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Io ~~ + +class Io: + + from io import SEEK_CUR + from gzip import compress as gzcompress, decompress as gzdecompress + from bz2 import compress as bzcompress, decompress as bzdecompress + from errno import EEXIST + from os import remove as removeFile, utime, rename + from platform import system + if system() == 'Windows' : + from mmap import mmap, ACCESS_READ as PROT_READ + else : + from mmap import mmap, PROT_READ + + def __init__(self): + """""" + + @staticmethod + def read_in_chunks(f, chsize=1024, utf8=False): + """Lazy function (generator) to read a file piece by piece in + Utf-8 bytes + Default chunk size: 1k.""" + c = 0 + while True: + data = f.read(chsize) + if utf8 : + p = f.tell() + t = f.read(1) + f.seek(p) + if t!=b'' and not Io.is_utf8_start_sequence(ord(t)) : + delta = 0 + for i in range(3) : + t = f.read(1) + if Io.is_utf8_start_sequence(ord(t)) : + delta += i + break + f.seek(p) + data += f.read(delta) + if not data : break + yield data, c + c += 1 + + + @staticmethod + def read_utf8_chr(f, chsize=0, p=0): + """""" + with Io.mmap(f.fileno(), chsize*p) as mmp: + s, b, c = mmp.size(), b'', 0 + while mmp.tell() < s : + b = mmp.read(1) + c = Io.count_utf8_continuation_bytes(b) + if c > 0 : b += mmp.read(c) + yield str(b,'utf-8') + + + @staticmethod + def is_utf8_continuation_byte(b): + """""" + return b >= 0x80 and b <= 0xbf + + + @staticmethod + def is_utf8_start_sequence(b): + """""" + return (b >= 0x00 and b <= 0x7f) or (b>= 0xc2 and b <= 0xf4) + + + @staticmethod + def count_utf8_continuation_bytes(b): + """""" + c = 0 + try : d = ord(b) + except : d = int(b) + if d >= 0xc2 : + if d < 0xe0 : c = 1 + elif d < 0xf0 : c = 2 + else : c = 3 + return c + + + @staticmethod + def get_data(path, binary=False, remove=False): + """Get file content from `path` + :Returns: `str` + """ + d = '' + with Io.rfile(path, binary) as f : + d = f.read() + if remove : + Io.removeFile(path) + return d + + + @staticmethod + def get_file_obj(path, binary=False, writing=False, update=False, truncate=False): + """""" + if not writing : + if not binary : + f = open(path, encoding='utf-8', mode='rt') + else : + f = open(path, mode='rb') + else : + if update and not Io.file_exists(path): + if binary : + f = open(path, mode='wb') + else : + f = open(path, mode='wt', encoding='utf-8') + return f + m = ('w' if truncate else 'r')+('+' if update else '')+('b' if binary else 't') + if not binary : + f = open(path, mode=m, encoding='utf-8') + else : + f = open(path, mode=m) + return f + + + @staticmethod + def wfile(path, binary=True): + """""" + return Io.get_file_obj(path, binary, True, True, True) + + + @staticmethod + def ufile(path, binary=True): + """""" + return Io.get_file_obj(path, binary, True, True, False) + + + @staticmethod + def rfile(path, binary=True): + """""" + return Io.get_file_obj(path, binary) + + + @staticmethod + def set_data(path, content, binary=False): + """""" + with Io.wfile(path, binary) as f: + f.write(content) + + + @staticmethod + def readmmline(f, pos=0): + """""" + f.flush() + with Io.mmap(f.fileno(), 0, prot=Io.PROT_READ) as mmp: + mmp.seek(pos) + for line in iter(mmp.readline, b''): + pos = mmp.tell() + yield pos, Io.str(line[:-1]) + + + @staticmethod + def copy(fromPath, toPath): + """""" + if not fromPath == toPath : + with Io.rfile(fromPath) as fi : + with Io.wfile(toPath) as fo : + fo.write(fi.read()) + else : raise Exception('can\t copy to myself') + + + @staticmethod + def bytes(sdata, encoding='utf-8'): + """""" + return bytes(sdata,encoding) + + + @staticmethod + def str(bdata, encoding='utf-8'): + """""" + return str(bdata,encoding) if isinstance(bdata, bytes) else str(bdata) + + + @staticmethod + def printableBytes(bdata): + """""" + data = '' + if isinstance(bdata,bytes) : + try: + data = Io.str(bdata) + except Exception as e: + hexv = [] + for i in bdata[1:] : + hexv.append(hex(i)[2:].rjust(2,'0')) + data = ' '.join(hexv) + pass + else : + data = bdata + return bdata + + + @staticmethod + def is_binary(filename): + """Check if given filename is binary.""" + done = False + f = Io.get_file_obj(filename, True) + try: + CHUNKSIZE = 1024 + while 1: + chunk = f.read(CHUNKSIZE) + if b'\0' in chunk: done = True # found null byte + if done or len(chunk) < CHUNKSIZE: break + finally: + f.close() + return done + + + @staticmethod + def file_exists(path): + """""" + exist = False + try: + if path is not None : + with open(path) as f: exist = True + f.close() + except IOError as e: pass + return exist + + + @staticmethod + def touch(fname, times=None): + """ only existing files """ + if Io.file_exists(fname): + Io.utime(fname, times) diff --git a/psr/log.py b/psr/log.py new file mode 100755 index 0000000..fde19ea --- /dev/null +++ b/psr/log.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/log.py +# # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module log ~~ + +try : + from inspect import signature +except : + # < python 3.3 + signature = None + pass + +from psr.sys import Sys, Const + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ Class Log ~~ + +class Log: + + def __init__(self, level=Const.LOG_DEFAULT, debug=True, wtime=True): + self.debug = debug + self.level = level + self.wtime = wtime + + def __call__(self, func, *args): + def wrapped_func(*args, **kwargs): + debug, wtime = self.debug and Sys.g.DEBUG and self.level >= Sys.g.LOG_LEVEL, self.wtime and Sys.g.LOG_TIME + self.debug_start_time = None if not wtime else Sys.datetime.now() + if debug : + # >= python 3.3 + if signature is not None : + l = [p.name for p in signature(func).parameters.values()] + # < python 3.3 + # !! BAD FIX !! + else : + l = ['self' if args[0].__class__ is not None else ''] + + n = args+tuple(kwargs.items()) + if len(n)>0 and l[0] == 'self': + n = n[1:] + s = args[0].__class__.__name__ +'.'+func.__name__ + else: + s = func.__name__ + Log._write(s, self.debug_start_time, True, n) + f = func(*args, **kwargs) + if debug : + Log._write(s, self.debug_start_time, False) + return f + return wrapped_func + + + @staticmethod + def _formatArgs(args): + """""" + args = list(args) + for i,a in enumerate(args) : + if not (isinstance(a, str) or isinstance(a, bytes)): + a = str(a) + if len(a) > Sys.g.LOG_LIM_ARG_LENGTH : + args[i] = a[:Sys.g.LOG_LIM_ARG_LENGTH]+'...' if isinstance(a, str) else bytes('...','utf-8') + args = str(args)[1:-1] + if args[-1:] == ',' : args = args[:-1] + return args + + + @staticmethod + def _write(sign, t=None, enter=True, args=''): + """""" + if Sys.g.DEBUG : + #~ DONT USE Sys.g.RLOCK + isChildProc = not Sys.g_is_main_proc() + bind_data = [] + if t is not None : + bind_data += Sys.pdate(t.timetuple() if enter else Sys.datetime.now().timetuple(), isChildProc) + + a, b, c, d, e = ('=> ' if enter else '<= '), '['+str(Sys.getpid()).rjust(5,' ')+']', ' '+sign+'(', Log._formatArgs(args), ') ' + if not isChildProc : + Sys.echo(a , Sys.CLZ_IO , False) + Sys.echo(b , Sys.CLZ_PID if not isChildProc else Sys.CLZ_CPID, False) + Sys.echo(c , Sys.CLZ_FUNC, False) + try: + Sys.echo(d , Sys.CLZ_ARGS, False) + except : + Sys.echo('?nr_arg?' , Sys.CLZ_ARGS, False) + pass + Sys.echo(e , Sys.CLZ_FUNC, False) + + bind_data += [(a, Const.CLZ_IO),(b, Const.CLZ_CPID if isChildProc else Const.CLZ_PID),(c , Const.CLZ_CFUNC if isChildProc else Const.CLZ_FUNC),(d , Const.CLZ_ARGS),(e , Const.CLZ_CFUNC if isChildProc else Const.CLZ_FUNC)] + + if not enter and t is not None : + bind_data += Sys.pdelta(t, '', isChildProc) + else : + bind_data += Sys.dprint(dbcall=isChildProc) + + if isChildProc : + Sys.sendMainProcMsg(1, bind_data) + else : + Sys.wlog(bind_data) diff --git a/psr/mproc.py b/psr/mproc.py new file mode 100755 index 0000000..b357e9a --- /dev/null +++ b/psr/mproc.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/mproc.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module mproc ~~ + +from multiprocessing import Process, current_process, Pipe +from multiprocessing.connection import wait +from threading import current_thread +from psr.sys import Sys, Const, init +from psr.log import Log + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Worker ~~ + +class Worker: + + @Log(Const.LOG_BUILD) + def __init__(self, appname, debug, gui, color, loglvl, ppid, event, id, wp, delay, task, *args, **kwargs): + def mptask(id, *args, **kwargs): + Sys.sendMainProcMsg(Manager.MSG_INIT, None) + otask = task(id=id, event=event, *args, **kwargs) + Sys.sendMainProcMsg(Manager.MSG_END, None) + return otask + + init(appname, debug, ppid, color, loglvl) + Sys.g.WPIPE = wp + Sys.g.CPID = id + Sys.g.GUI = gui + # initialize child process event with parent process event + Sys.g.MPEVENT = event + if delay : Sys.sleep(delay) + mptask(id, *args, **kwargs) + # don't directly close pipe 'cause of eventual logging + # pipe will auto close on terminating child process + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Manager ~~ + +class Manager: + + MSG_INIT = 0 + MSG_PRINT = 1 + MSG_DATA = 2 + MSG_END = 3 + TYPE_MSG = list(range(4)) + K_ID = 0 + K_TYPE = 1 + K_DATA = 2 + K_PROC = 0 + K_PIPE = 1 + + checktime = None + + @Log(Const.LOG_UI) + def __init__(self, task, nproc=2, delay=None, event=None, *args, **kwargs): + """""" + self.readers = [] + self.plist = [] + self.onstart_bind = None + self.onrun_bind = None + self.onend_bind = None + for id in range(nproc): + r, w = Pipe(duplex=False) + self.readers.append(r) + # (process, wpipe) + p = Process(target=Worker, args=tuple([Sys.g.PRJ_NAME, Sys.g.DEBUG, Sys.g.GUI, Sys.g.COLOR_MODE, Sys.g.LOG_LEVEL, Sys.getpid(), event, id, w, delay, task])+tuple(args), kwargs=kwargs) + self.plist.append((p, w)) + + @Log(Const.LOG_APP) + def run(self, checktime=None, onstart_bind=None, onrun_bind=None, onend_bind=None): + self.checktime = checktime + self.onstart_bind = onstart_bind + self.onrun_bind = onrun_bind + self.onend_bind = onend_bind + for p, w in self.plist: + p.start() + w.close() + self.wait() + + + @Log(Const.LOG_DEBUG) + def wait(self): + """""" + while self.readers: + self.wait_childs() + if self.checktime is not None : Sys.sleep(self.checktime) + + + def getcpid(self, id): + """""" + return self.plist[id][self.K_PROC].pid + + + @Log(Const.LOG_ALL) + def wait_childs(self): + """""" + for r in wait(self.readers): + try: + msg = r.recv() + except EOFError: + self.readers.remove(r) + else: + if len(msg)==3 and msg[self.K_TYPE] in self.TYPE_MSG : + + cpid = self.getcpid(msg[self.K_ID]) + + if msg[self.K_TYPE] == self.MSG_INIT : + if hasattr(self.onstart_bind, '__call__'): + self.onstart_bind(msg[self.K_ID], cpid, msg[self.K_DATA]) + + elif msg[self.K_TYPE] == self.MSG_PRINT : + if Sys.g.DEBUG : + if not Sys.g.GUI : + for item in msg[self.K_DATA] : + Sys.echo(item[0], Sys.clzdic[item[1]], False, True) + Sys.dprint('') + #~ else : + Sys.wlog(msg[self.K_DATA]) + + elif msg[self.K_TYPE] == self.MSG_DATA : + if hasattr(self.onrun_bind, '__call__'): + self.onrun_bind(msg[self.K_ID], cpid, msg[self.K_DATA]) + + elif msg[self.K_TYPE] == self.MSG_END : + if hasattr(self.onend_bind, '__call__'): + self.onend_bind(msg[self.K_ID], cpid, msg[self.K_DATA]) diff --git a/psr/sys.py b/psr/sys.py new file mode 100755 index 0000000..abdddcf --- /dev/null +++ b/psr/sys.py @@ -0,0 +1,610 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/sys.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ module sys ~~ + +from psr.io import Io +from psr.const import Const +from threading import RLock, current_thread +from multiprocessing import Event +from queue import Queue + +def init(name, debug, remote=False, color=True, loglvl=Const.LOG_NEVER): + Sys.g_init(name, debug, remote, color, loglvl) + Sys.g_set_main_proc(remote) + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Sys ~~ + +class Sys: + """""" + + from platform import system as getSysName + from os import system as sysCall, remove as removeFile, makedirs, sep, getpid, listdir + from getpass import getuser as getUserLogin + from time import strftime, mktime, time, localtime, sleep + from datetime import datetime, timedelta + from sys import exit, stdout, executable + from os.path import abspath, dirname, join, realpath, basename, getsize, isdir, splitext + from math import log, floor, ceil + from _thread import exit as thread_exit + + import builtins as g + + g.DEBUG = False + g.LOG_LEVEL = Const.LOG_DEFAULT + g.LOG_TIME = False + g.LOG_LIM_ARG_LENGTH = Const.LOG_LIM_ARG_LENGTH + g.QUIET = False + g.COLOR_MODE = True + g.RLOCK = None + g.MPRLOCK = None + g.WPIPE = None + g.THREAD_CLI = None + g.UI_AUTO_SCROLL = True + g.CPID = None + g.SIGNAL_STOP = 0 + g.SIGNAL_START = 1 + g.SIGNAL_RUN = 2 + g.SIGNAL_CLEAR = 3 + g.GUI = False + g.GUI_PRINT_STDOUT = True + g.MPEVENT = Event() + g.LOG_QUEUE = None + + + @staticmethod + def g_init(prjName, debug=True, remote=False, color=True, loglvl=Const.LOG_DEFAULT): + """""" + Sys.g.PRJ_NAME = prjName + Sys.g.DEBUG = debug + Sys.g.COLOR_MODE = color + Sys.g.LOG_LEVEL = loglvl + Sys.g.LOG_TIME = True + Sys.g.MAIN_PROC = None + Sys.g.RLOCK = RLock() + Sys.g.LOG_QUEUE = Queue() if Sys.g.GUI else None + + + @staticmethod + def sendMainProcMsg(type, data): + """""" + if not Sys.g_is_main_proc() and Sys.g.WPIPE is not None and Sys.g.CPID is not None and type in range(4) : + Sys.g.WPIPE.send((Sys.g.CPID, type, data)) + + + @staticmethod + def g_set_main_proc(ppid=None): + """""" + Sys.g.MAIN_PROC = Sys.getpid() if ppid is None or ppid is False else ppid + + + @staticmethod + def g_is_main_proc(): + """""" + try : + return Sys.g.MAIN_PROC == Sys.getpid() + except : + return False + + + @staticmethod + def g_has_ui_trace(): + """""" + try: + return Sys.g.GUI and Sys.g.DEBUG + except Exception as e: + return False + + + @staticmethod + def cli_emit_progress(value=0): + """""" + if Sys.g.THREAD_CLI is not None : Sys.g.THREAD_CLI.progress(value) + + + @staticmethod + def is_cli_cancel(event=None): + """""" + c = Sys.g.THREAD_CLI is not None and Sys.g.THREAD_CLI.cancelled + return c or (event is not None and event.is_set()) + + + @staticmethod + def isUnix(): + """""" + return not Sys.getSysName() == 'Windows' + + + @staticmethod + def clear(): + return Sys.sysCall('cls' if not Sys.isUnix() else 'clear') + + + @staticmethod + def mkdir_p(path): + """""" + try: + Sys.makedirs(path) + except OSError as e: + if e.errno == Io.EEXIST: + pass + else: raise + + + @staticmethod + def readableBytes(b, p=2): + """Give a human representation of bytes size `b` + :Returns: `str` + """ + if b is None or b=='': return '0' + else :b = int(b) + units = [Const.UNIT_SHORT_B, Const.UNIT_SHORT_KIB, Const.UNIT_SHORT_MIB, Const.UNIT_SHORT_GIB, Const.UNIT_SHORT_TIB]; + b = max(b,0); + if b == 0 : lb= 0 + else : lb = Sys.log(b) + p = Sys.floor(lb/Sys.log(1024)) + p = min(p, len(units)- 1) + #Uncomment one of the following alternatives + b /= pow(1024,p) + #b /= (1 << (10 * p)) + return str(round(b, 1))+' '+units[p] + + + @staticmethod + def getFileExt(fromPath): + """""" + return Sys.splitext(fromPath) + + + @staticmethod + def getFileSize(path): + """""" + return Sys.readableBytes(Sys.getsize(path)) + + + @staticmethod + def getPrintableBytes(bdata): + """""" + data = '' + if isinstance(bdata,bytes) : + try: + data = str(bdata, 'utf-8') + except Exception as e: + hexv = [] + for i in bdata[1:] : + hexv.append(hex(i)[2:].rjust(2,'0')) + data = ' '.join(hexv) + pass + else : + data = bdata + return data + + + @staticmethod + def getHexaBytes(bdata): + """""" + data = '' + if isinstance(bdata,bytes) : + hexv = [] + for i in bdata[1:] : + hexv.append(hex(i)[2:].rjust(2,'0')) + data = ' '.join(hexv) + else : + data = bdata + return data + + + @staticmethod + # never log this func -> maximum recursion + def wlog(data=[('','default')]): + """""" + if not Sys.is_cli_cancel(): + if Sys.g.LOG_QUEUE is not None : + try : + Sys.g.LOG_QUEUE.put((current_thread().name,data)) + Sys.cli_emit_progress() + except Exception as e: + Sys.pwarn((('wlog exception ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + + else : + Sys.g.THREAD_CLI.stop() + + + @staticmethod + def pwlog(data, guiClear=False): + """ data=[('text', keycolor, newline)]""" + if guiClear : Sys.wlog(Sys.g.SIGNAL_CLEAR) + wd = [] + for item in data : + nl = False if len(item)< 3 else (item[2]==1 or item[2]==True) + c = Const.CLZ_0 if (len(item)< 2 or item[1] not in Sys.clzdic) else item[1] + Sys.echo(item[0], Sys.clzdic[c], nl) + wd += [(item[0], c)] + if nl and Sys.g.GUI : + Sys.wlog(wd) + wd = [] + if len(wd) > 0 and Sys.g.GUI : + Sys.wlog(wd) + + + @staticmethod + def echo(data, colors, endLF=True, endClz=True): + """""" + if isinstance(data,bytes) : + data = Sys.getPrintableBytes(data) + + ev = '' if not endLF else Sys.Clz._LF + tokens = [c.lstrip(Sys.Clz._MARKER[0]).rstrip(Sys.Clz._SEP) for c in colors.split(Sys.Clz._MARKER) if c is not ''] + if Sys.isUnix() : + if data is None: data = '' + if endClz : data += Sys.Clz._uOFF + if Sys.g.COLOR_MODE : + Sys.dprint(eval('Sys.Clz._u'+'+Sys.Clz._u'.join(tokens))+data,end=ev, dbcall=False) + else : + Sys.dprint(data,end=ev, dbcall=False) + else : + if Sys.g.COLOR_MODE : Sys.Clz.setColor(eval('Sys.Clz._w'+'|Sys.Clz._w'.join(tokens))) + Sys.dprint(data,end=ev, dbcall=False) + Sys.stdout.flush() + if endClz and Sys.g.COLOR_MODE : Sys.Clz.setColor(Sys.Clz._wOFF) + + + @staticmethod + def dprint(d='',end=Const.LF, dbcall=False): + """""" + dbcall = Sys.g.QUIET + if not dbcall : + if not Sys.g.GUI or Sys.g.GUI_PRINT_STDOUT : + if Sys.g.RLOCK is not None : + with Sys.g.RLOCK : + if not Sys.g.QUIET : + print(d,end=end) + else : + if not Sys.g.QUIET : + print(d,end=end) + + bdata = [(d,Const.CLZ_DEFAULT)] + return bdata + + + @staticmethod + def eprint(d='', label=Const.WARN, dbcall=False): + """""" + c = Sys.CLZ_ERROR if label is Const.ERROR else Sys.CLZ_WARN + Sys.echo(' '+label+' : ', c, False, False) + Sys.echo(str(d)+' ', c, True, True) + + bdata = [(label+' : ' , label),(str(d)+' ', label)] + return bdata + + + @staticmethod + def pdate(t, dbcall = False): + """""" + t, s = Sys.strftime('%H:%M',t), Sys.strftime(':%S ',t) + if not dbcall : + Sys.echo(t , Sys.CLZ_TIME, False) + Sys.echo(s , Sys.CLZ_SEC , False) + + bdata = [(t , Const.CLZ_TIME),(s , Const.CLZ_SEC)] + return bdata + + + @staticmethod + def pkval(label, value, pad=40, dbcall= False): + """""" + l, v = label.rjust(pad,' '), ' '+str(value) + if not dbcall : + Sys.echo(l, Sys.CLZ_SEC , False) + Sys.echo(v, Sys.CLZ_TIME , True) + + bdata = [(l, Const.CLZ_SEC),(v, Const.CLZ_TIME)] + return bdata + + + @staticmethod + def getDelta(t): + v = ''.join(['{:.5f}'.format(Sys.time()-(Sys.mktime(t.timetuple())+1e-6*t.microsecond)),' s']) + return v + + @staticmethod + def pdelta(t, label='', dbcall= False): + """""" + if len(label)>0 and not dbcall : Sys.echo(label+' ', Sys.CLZ_IO, False) + v = Sys.getDelta(t) + if not dbcall : + Sys.echo(v, Sys.CLZ_DELTA) + + bdata = [] + if len(label)>0 : + bdata.append((label+' ', Const.CLZ_IO)) + bdata.append((v, Const.CLZ_DELTA)) + return bdata + + + @staticmethod + def pcontent(content, color=None, bcolor=Const.CLZ_DEFAULT, dbcall= False): + """""" + if not dbcall : Sys.echo(content, Sys.CLZ_SEC if color is None else color) + + bdata = [(content, bcolor)] + return bdata + + + @staticmethod + def pwarn(data, isError=False, length=Const.LINE_SEP_LEN, dbcall=False): + """ data struct : + ( # line0 + 'simple line', # LF + # line1 + # p0 p1 p2 + ('complex line with ',('paramValue',fgcolor), ' suit complex line'), # LF + # line2 + 'other simple line ' + ) + """ + w = ' '+(Const.WARN if not isError else Const.ERROR)+' : ' + clz = Sys.CLZ_WARN if not isError else Sys.CLZ_ERROR + clzp = Sys.CLZ_WARN_PARAM if not isError else Sys.CLZ_ERROR_PARAM + uiclz = Const.CLZ_WARN if not isError else Const.CLZ_ERROR + uiclzp = Const.CLZ_WARN_PARAM if not isError else Const.CLZ_ERROR_PARAM + + if not dbcall : Sys.echo(w, clzp, False, False) + bdata = [] + if not Sys.g.QUIET : + bdata.append((w, uiclzp)) + for i, line in enumerate(data) : + if i > 0 : + if not dbcall : Sys.echo(' '*len(w), clz, False, False) + if not Sys.g.QUIET : + bdata.append((' '*len(w), uiclz)) + if isinstance(line,str) : + s = line.ljust(length-len(w),' ') + if not dbcall : Sys.echo(s, clz, True, True) + + if not Sys.g.QUIET : + bdata.append((s, uiclz)) + Sys.wlog(bdata) + bdata = [] + else : + sl = 0 + for p in line : + if isinstance(p,str) : + Sys.echo(p, clz, False, False) + bdata.append((p, uiclz)) + sl += len(p) + else : + Sys.echo(p[0], clzp+p[1], False, False) + bdata.append((p[0], uiclzp)) + sl += len(p[0]) + s = ' '.ljust(length-sl-len(w),' ') + if not dbcall : Sys.echo(s, clz, True, True) + if not Sys.g.QUIET : + bdata.append((s, uiclz)) + Sys.wlog(bdata) + bdata = [] + + if not dbcall : Sys.dprint() + if Sys.g.DEBUG : Sys.wlog([('',Const.CLZ_DEFAULT)]) + + + @staticmethod + def _psymbol(ch, done=True): + """""" + Sys.echo(' ', Sys.CLZ_DEFAULT, False, False) + Sys.echo(' '+ch+' ', Sys.CLZ_HEAD_APP if done else Sys.CLZ_SYMBOL, False, True) + Sys.echo(' ', Sys.CLZ_DEFAULT, False, True) + bdata = [(' ', Const.CLZ_DEFAULT),(' '+ch+' ', Const.CLZ_HEAD_APP if done else Sys.CLZ_SYMBOL),(' ', Const.CLZ_DEFAULT)] + return bdata + + + @staticmethod + def pask(ask, yesValue='yes', noValue='no'): + """""" + Sys._psymbol('?') + Sys.echo('', Sys.Clz.fgb3, False, False) + ask = ask + ' ('+yesValue+'/'+noValue+') ? ' + answer = input(ask) + while answer.lower()!=yesValue.lower() and answer.lower()!=noValue.lower() : + s = 'Please enter either '+yesValue+' or '+noValue+' : ' + answer = input(' '.ljust(5,' ')+s.ljust(len(ask),' ')) + Sys.dprint() + return answer.lower()==yesValue.lower() + + + @staticmethod + def pstep(title, stime, done, noelf=False, exitOnFailed=True, length=100): + """""" + if stime is not None : + v = ' ('+''.join(['{:.5f}'.format(Sys.time()-(Sys.mktime(stime.timetuple())+1e-6*stime.microsecond)),' s'])+')' + else : v = '' + bdata = Sys._psymbol('*') + Sys.echo(title, Sys.CLZ_TITLE, False, False) + Sys.echo(v+' '.ljust(length-len(title)-20-len(v), ' '),Sys.CLZ_DELTA, False, True) + if done : + Sys.echo(' == '+Const.OK+' == ', Sys.CLZ_OK) + else : + Sys.echo(' == '+Const.KO+' == ', Sys.CLZ_KO) + + bdata = bdata + [(title, Const.CLZ_TITLE),(v+' '.ljust(length-len(title)-20-len(v)), Const.CLZ_DELTA),(' == '+(Const.OK if done else Const.KO)+' == ', (Const.CLZ_OK if done else Const.CLZ_KO))] + + Sys.wlog(bdata) + if not noelf : + Sys.wlog(Sys.dprint()) + + if exitOnFailed and not done: + Sys.exit(1) + + + @staticmethod + def ptask(title='Processing, please wait'): + if not Sys.g.QUIET : + s = ' '+title+'...' + Sys.echo(s, Sys.CLZ_TASK ) + Sys.wlog([(s, Const.CLZ_TASK)]) + Sys.wlog(Sys.dprint()) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class Coloriz ~~ + +class Coloriz: + + _MARKER = '!§' + """""" + _SEP = ';' + """""" + _PATTERN_COLOR = '^'+_MARKER[0]+'[nfNFbB][0-7]'+_SEP+'$' + """""" + _wFH = 0x0008 + """""" + _wBH = 0x0080 + """""" + _uOFF = '\033[1;m' + """""" + _wOFF = None + """""" + _LF = Const.LF + """""" + OFF = _MARKER+_MARKER[0]+'OFF'+_SEP+_MARKER + """""" + def __init__(self): + """Colors for both plateform are : 0: black - 1: red - 2:green - 3: yellow - 4: blue - 5: purple - 6: cyan - 7: white + available class members : + foreground normal (same as bold for w32): + self.fgn0 -> self.fgn7 + foreground bold : + self.fgb0 -> self.fgb7 + foreground high intensity (same as bold high intensity for w35): + self.fgN0 -> self.fgN7 + foreground bold high intensity : + self.fgB0 -> self.fgB7 + background + self.bg0 -> self.bg7 + background high intensity + self.BG0 -> self.BG7 + default colors : + self.OFF + """ + if not Sys.isUnix(): + j = 0 + for i in (0,4,2,6,1,5,3,7): + exec('self._wf%i = 0x000%i' % (i,j) + Const.LF+'self._wb%i = 0x00%i0' % (i,j) + Const.LF+'self._wF%i = 0x000%i | self._wFH' % (i,j) + Const.LF+'self._wB%i = 0x00%i0 | self._wBH' % (i,j)) + # normal eq bold + exec('self._wn%i = self._wf%i' % (i,i)) + # normal high intensity eq bold high intensity + exec('self._wN%i = self._wF%i' % (i,i)) + j += 1 + import psr.w32color as w32cons + self._wOFF = w32cons.get_text_attr() + self._wOFFbg = self._wOFF & 0x0070 + self._wOFFfg = self._wOFF & 0x0007 + self.setColor = w32cons.set_text_attr + + for i in range(0,8): + # foreground normal + exec('self.fgn%i = self._MARKER + self._MARKER[0] + "n%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._un%i = "\\033[0;3%im"' % (i,i)) + # foreground bold + exec('self.fgb%i = self._MARKER + self._MARKER[0] + "f%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._uf%i = "\\033[1;3%im"' % (i,i)) + # foreground high intensity + exec('self.fgN%i = self._MARKER + self._MARKER[0] + "N%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._uN%i = "\\033[0;9%im"' % (i,i)) + # foreground bold high intensity + exec('self.fgB%i = self._MARKER + self._MARKER[0] + "F%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._uF%i = "\\033[1;9%im"' % (i,i)) + # background + exec('self.bg%i = self._MARKER + self._MARKER[0] + "b%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._ub%i = "\\033[4%im"' % (i,i)) + # background high intensity + exec('self.BG%i = self._MARKER + self._MARKER[0] + "B%i" + self._SEP + self._MARKER' % (i,i)) + if Sys.isUnix() : exec('self._uB%i = "\\033[0;10%im"' % (i,i)) + +Sys.Clz = Coloriz() +Sys.CLZ_TIME = Sys.Clz.fgN2+Sys.Clz.bg0 +Sys.CLZ_SEC = Sys.Clz.fgb7+Sys.Clz.bg0 +Sys.CLZ_PID = Sys.Clz.fgb1+Sys.Clz.bg0 +Sys.CLZ_PPID = Sys.Clz.fgb1+Sys.Clz.bg0 +Sys.CLZ_CPID = Sys.Clz.fgb7+Sys.Clz.bg0 +Sys.CLZ_IO = Sys.Clz.fgB1+Sys.Clz.bg0 +Sys.CLZ_FUNC = Sys.Clz.fgb3+Sys.Clz.bg0 +Sys.CLZ_CFUNC = Sys.Clz.fgb3+Sys.Clz.bg0 +Sys.CLZ_ARGS = Sys.Clz.fgn7+Sys.Clz.bg0 +Sys.CLZ_DELTA = Sys.Clz.fgN4+Sys.Clz.bg0 +Sys.CLZ_TASK = Sys.Clz.fgB2+Sys.Clz.bg0 +Sys.CLZ_ERROR = Sys.Clz.fgb7+Sys.Clz.bg1 +Sys.CLZ_ERROR_PARAM = Sys.Clz.fgb3+Sys.Clz.bg1 +Sys.CLZ_WARN = Sys.Clz.fgb7+Sys.Clz.bg5 +Sys.CLZ_WARN_PARAM = Sys.Clz.fgb3+Sys.Clz.bg5 +Sys.CLZ_DEFAULT = Sys.Clz.fgb7+Sys.Clz.bg0 +Sys.CLZ_TITLE = Sys.Clz.fgB7+Sys.Clz.bg0 +Sys.CLZ_SYMBOL = Sys.Clz.BG4+Sys.Clz.fgB7 +Sys.CLZ_OK = Sys.Clz.bg2+Sys.Clz.fgb7 +Sys.CLZ_KO = Sys.Clz.bg1+Sys.Clz.fgb7 +Sys.CLZ_ACTION = Sys.Clz.BG4+Sys.Clz.fgB7 +Sys.CLZ_INIT = Sys.Clz.BG4+Sys.Clz.fgB7 +Sys.CLZ_HELP_PRG = Sys.Clz.fgb7 +Sys.CLZ_HELP_CMD = Sys.Clz.fgB3 +Sys.CLZ_HELP_PARAM = Sys.Clz.fgB1 +Sys.CLZ_HELP_ARG = Sys.Clz.fgB3 +Sys.CLZ_HELP_COMMENT = Sys.Clz.fgn7 +Sys.CLZ_HELP_ARG_INFO = Sys.Clz.fgb7 +Sys.CLZ_HELP_DESC = Sys.Clz.fgN1 +Sys.CLZ_HEAD_APP = Sys.Clz.BG4+Sys.Clz.fgB7 +Sys.CLZ_HEAD_KEY = Sys.Clz.fgB3 +Sys.CLZ_HEAD_VAL = Sys.Clz.fgB4 +Sys.CLZ_HEAD_SEP = Sys.Clz.fgB0 +Sys.CLZ_HEAD_LINE = Sys.Clz.fgN0 + +Sys.CLZ_0 = Sys.Clz.fgn7 +Sys.CLZ_1 = Sys.Clz.fgB1 +Sys.CLZ_2 = Sys.Clz.fgB2 +Sys.CLZ_3 = Sys.Clz.fgB3 +Sys.CLZ_4 = Sys.Clz.fgB4 +Sys.CLZ_5 = Sys.Clz.fgB5 +Sys.CLZ_6 = Sys.Clz.fgB6 +Sys.CLZ_7 = Sys.Clz.fgB7 + +Sys.clzdic = { Const.CLZ_0 : Sys.CLZ_0 , Const.CLZ_1 : Sys.CLZ_1 , Const.CLZ_2 : Sys.CLZ_2, + Const.CLZ_3 : Sys.CLZ_3 , Const.CLZ_4 : Sys.CLZ_4 , Const.CLZ_5 : Sys.CLZ_5, + Const.CLZ_6 : Sys.CLZ_6 , Const.CLZ_7 : Sys.CLZ_7 , + Const.CLZ_TASK : Sys.CLZ_TASK , Const.CLZ_SYMBOL: Sys.CLZ_SYMBOL, + Const.CLZ_TIME : Sys.CLZ_TIME , Const.CLZ_SEC : Sys.CLZ_SEC , + Const.CLZ_IO : Sys.CLZ_IO , Const.CLZ_CPID : Sys.CLZ_CPID , Const.CLZ_PID : Sys.CLZ_PID, + Const.CLZ_CFUNC : Sys.CLZ_CFUNC , Const.CLZ_FUNC : Sys.CLZ_FUNC , Const.CLZ_ARGS : Sys.CLZ_ARGS, + Const.CLZ_DELTA : Sys.CLZ_DELTA , + Const.CLZ_ERROR : Sys.CLZ_ERROR , Const.CLZ_WARN : Sys.CLZ_WARN , Const.CLZ_ERROR_PARAM : Sys.CLZ_ERROR_PARAM, Const.CLZ_WARN_PARAM : Sys.CLZ_WARN_PARAM, + Const.CLZ_DEFAULT : Sys.CLZ_DEFAULT, + Const.CLZ_ACTION : Sys.CLZ_ACTION , + Const.CLZ_INIT : Sys.CLZ_INIT + } diff --git a/psr/w32color.py b/psr/w32color.py new file mode 100755 index 0000000..e524ea9 --- /dev/null +++ b/psr/w32color.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# psr/w32color.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +""" +Colors text in console mode application (win32). +Uses ctypes and Win32 methods SetConsoleTextAttribute and +GetConsoleScreenBufferInfo. +""" + +from ctypes import windll, Structure as Struct, c_short as SHORT, c_ushort as WORD, byref + +class Coord(Struct): + """struct in wincon.h.""" + _fields_ = [("X", SHORT),("Y", SHORT)] + +class SmallRect(Struct): + """struct in wincon.h.""" + _fields_ = [("Left", SHORT),("Top", SHORT),("Right", SHORT),("Bottom", SHORT)] + +class ConsoleScreenBufferInfo(Struct): + """struct in wincon.h.""" + _fields_ = [("dwSize", Coord),("dwCursorPosition", Coord),("wAttributes", WORD),("srWindow", SmallRect),("dwMaximumWindowSize", Coord)] + +# winbase.h +STD_INPUT_HANDLE = -10 +STD_OUTPUT_HANDLE = -11 +STD_ERROR_HANDLE = -12 + +stdout_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) +SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute +GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + +def get_text_attr(): + """Returns the character attributes (colors) of the console screen + buffer.""" + csbi = ConsoleScreenBufferInfo() + GetConsoleScreenBufferInfo(stdout_handle, byref(csbi)) + return csbi.wAttributes + +def set_text_attr(color): + """Sets the character attributes (colors) of the console screen + buffer. Color is a combination of foreground and background color, + foreground and background intensity.""" + SetConsoleTextAttribute(stdout_handle, color) diff --git a/resources/impra/LICENSE b/resources/impra/LICENSE new file mode 100755 index 0000000..94a9ed0 --- /dev/null +++ b/resources/impra/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 + (at your option) any later version. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/resources/impra/glade/impra.glade b/resources/impra/glade/impra.glade new file mode 100755 index 0000000..aec8494 --- /dev/null +++ b/resources/impra/glade/impra.glade @@ -0,0 +1,2398 @@ + + + + + 128 + 8096 + 1024 + 1 + 10 + + + 64 + 2048 + 128 + 1 + 10 + + + False + 5 + ImpraStorage + False + True + center + True + ../../pixmaps/impra/impra.png + dialog + False + False + + + False + vertical + 2 + + + 5 + False + False + True + start + end + + + + + + + + + False + False + end + 0 + + + + + True + False + center + 5 + 5 + 5 + 5 + 0 + in + + + True + False + 12 + + + True + False + + + 200 + True + False + 1 + Source path : + + + 0 + 0 + 1 + 1 + + + + + True + False + vertical + + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + Rewrite label : + + + 0 + 1 + 1 + 1 + + + + + 200 + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + Category : + + + 0 + 2 + 1 + 1 + + + + + True + True + + True + + + 1 + 2 + 1 + 1 + + + + + True + False + 20 + end + + + Cancel + True + True + True + image21 + + + + False + True + 0 + + + + + Add + True + True + True + image22 + + + + False + True + 1 + + + + + 0 + 4 + 2 + 1 + + + + + auto category + True + True + False + end + 0 + True + True + + + 0 + 3 + 1 + 1 + + + + + + + + + + + + True + False + <b> Select a file </b> + True + + + + + True + True + 1 + + + + + + + False + 5 + ImpraStorage + False + True + center + True + ../../pixmaps/impra/impra.png + dialog + False + False + + + False + vertical + 2 + + + False + False + True + start + end + + + + + + + + + False + False + end + 0 + + + + + True + False + center + 5 + 5 + 5 + 5 + 0 + in + + + True + False + 12 + + + True + False + + + True + False + 1 + Rewrite label : + + + 0 + 0 + 1 + 1 + + + + + 200 + True + True + + True + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + Category : + + + 0 + 1 + 1 + 1 + + + + + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 20 + end + + + Cancel + True + True + True + image19 + + + + False + True + 0 + + + + + Save + True + True + True + image20 + + + + False + True + 1 + + + + + 0 + 2 + 2 + 1 + + + + + + + + + True + False + <b> Edit File </b> + True + + + + + True + True + 1 + + + + + + + True + False + gtk-save + + + True + False + gtk-connect + 2 + + + True + False + gtk-connect + + + True + False + gtk-save + + + True + False + gtk-remove + + + True + False + gtk-execute + + + True + False + gtk-add + + + True + False + gtk-remove + + + True + False + gtk-new + + + True + False + gtk-properties + + + True + False + gtk-find + + + True + False + gtk-close + + + True + False + gtk-save + + + True + False + gtk-close + + + True + False + gtk-new + + + True + False + gtk-new + + + True + False + gtk-remove + + + True + False + gtk-refresh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + center + 1248 + 750 + ../../pixmaps/impra/impra.png + + + + True + False + vertical + + + True + False + + + True + False + _Fichier + True + + + True + False + + + gtk-new + True + False + True + True + + + + + gtk-open + True + False + True + True + + + + + gtk-save + True + False + True + True + + + + + gtk-save-as + True + False + True + True + + + + + True + False + + + + + gtk-quit + True + False + True + True + + + + + + + + + True + False + É_dition + True + + + True + False + + + gtk-cut + True + False + True + True + + + + + gtk-copy + True + False + True + True + + + + + gtk-paste + True + False + True + True + + + + + gtk-delete + True + False + True + True + + + + + + + + + True + False + _Affichage + True + + + + + True + False + Aid_e + True + + + True + False + + + gtk-about + True + False + True + True + + + + + + + + + + False + True + 0 + + + + + True + True + + + True + True + vertical + 400 + True + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + + + True + False + + + True + False + 1 + Account : + + + + + + + + False + True + 0 + + + + + True + False + 22 + 0.15000000596046448 + -- + + + False + True + 1 + + + + + True + False + 1 + Index uid : + + + + + + + + False + True + 2 + + + + + True + False + 22 + 0.15000000596046448 + -- + + + False + True + 3 + + + + + True + False + 1 + Box : + + + + + + + + False + True + 4 + + + + + True + False + 22 + 0.15000000596046448 + -- + + + False + True + 5 + + + + + True + False + Total size : + + + + + + + + False + True + 6 + + + + + True + False + 22 + -- + + + False + True + 7 + + + + + Refresh + True + False + True + True + 20 + image8 + + + + False + True + 8 + + + + + True + False + 1 + date + + + + + + + + + True + True + 9 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + True + 2 + treestore1 + True + 0 + horizontal + True + 1 + + + + + + + + True + True + 1 + + + + + + + False + True + + + + + True + False + vertical + + + True + False + 5 + 5 + 5 + 5 + 0 + + + True + False + 5 + 5 + 5 + 5 + + + True + True + 5000 + True + + + True + False + vertical + + + True + True + in + + + True + True + False + False + + + + + True + True + 0 + + + + + True + False + end + 5 + 5 + + + True + False + + + + + False + True + 1 + + + + + False + True + + + + + False + vertical + + + True + True + in + + + True + False + none + + + True + False + vertical + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + 0 + + + + + True + False + end + 5 + 5 + + + True + False + False + + + + + False + True + 1 + + + + + True + True + + + + + + + + + True + False + <b>Imap log</b> + True + + + + + True + True + 0 + + + + + True + False + 5 + 5 + 5 + 5 + 0 + in + + + True + False + + + True + False + + + Search : + True + False + True + False + 0 + True + + + + False + True + 0 + + + + + True + False + True + + True + + + False + True + 1 + + + + + True + False + False + 0 + 1 + + + False + True + 2 + + + + + True + False + False + 0 + 1 + + + False + True + 3 + + + + + True + False + False + 0 + 1 + + + False + True + 4 + + + + + Find + True + False + True + True + image5 + + + + False + True + 5 + + + + + False + True + 0 + + + + + Download + True + False + True + True + 60 + image1 + + + + False + True + 1 + + + + + File Infos + True + False + True + True + 10 + image2 + + + + False + True + 2 + + + + + Add File + True + False + True + True + 10 + image3 + + + + False + True + 3 + + + + + Delete + True + False + True + True + 10 + image4 + + + + False + True + 4 + + + + + + + True + False + <b>Actions</b> + True + + + + + False + True + end + 1 + + + + + True + True + + + + + + + True + False + + + True + False + gtk-home + + + False + True + 0 + + + + + True + False + 15 + 32 + Index + + + False + True + 1 + + + + + False + + + + + True + False + center + center + 0 + in + + + True + False + start + 10 + 12 + + + True + False + center + vertical + + + True + False + + + True + False + 15 + Choose a profile : + + + False + True + 0 + + + + + True + False + 0 + 1 + + + + False + True + 1 + + + + + new profile + True + True + True + image15 + + + + False + True + 2 + + + + + hide password + True + True + False + end + 78 + 0 + True + True + + + + True + True + 4 + + + + + False + True + 0 + + + + + True + False + + + True + False + 5 + 5 + 5 + 5 + 0 + out + + + True + False + + + 120 + True + False + 20 + 1 + profle name : + + + 0 + 0 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + imap host : + False + + + 0 + 3 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + imap port : + + + 0 + 4 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + imap user : + + + 0 + 5 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 4 + 1 + 1 + + + + + True + False + 1 + imap password : + + + 0 + 6 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 5 + 1 + 1 + + + + + 10 + True + False + vertical + + + 0 + 2 + 2 + 1 + + + + + True + False + 1 + user name : + + + 0 + 1 + 1 + 1 + + + + + True + True + start + False + + True + + + 1 + 6 + 1 + 1 + + + + + test account + True + True + True + start + center + 22 + image11 + + + + 1 + 7 + 1 + 2 + + + + + True + False + end + 12 + testing + + + + + + 0 + 7 + 1 + 2 + + + + + True + False + 10 + 10 + + + True + False + center + 10 + 10 + 10 + 0 + in + + + True + False + + + True + False + 80 + 10 + 1 + account name : + + + 0 + 1 + 1 + 1 + + + + + True + True + start + 10 + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + imap host : + False + + + 0 + 2 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 2 + 1 + 1 + + + + + True + False + 1 + imap port : + + + 0 + 3 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + imap user : + + + 0 + 4 + 1 + 1 + + + + + True + True + start + + True + + + 1 + 4 + 1 + 1 + + + + + True + False + 1 + imap password : + + + 0 + 5 + 1 + 1 + + + + + True + True + start + False + + True + + + 1 + 5 + 1 + 1 + + + + + test account + True + False + True + True + start + center + 22 + image12 + + + + 1 + 6 + 1 + 1 + + + + + True + False + 3 + + + True + False + 22 + 1 + bind account : + + + False + True + 0 + + + + + True + False + 0 + 1 + + + + False + True + 1 + + + + + add + True + True + True + start + 5 + image17 + + + + False + True + 2 + + + + + remove + True + True + True + start + image18 + + + + False + True + 3 + + + + + 0 + 0 + 2 + 1 + + + + + True + False + end + 13 + label + + + + + + 0 + 6 + 1 + 1 + + + + + + + True + False + <b> Multi-Account </b> + True + + + + + False + True + 0 + + + + + 2 + 0 + 1 + 10 + + + + + True + False + 10 + 10 + 10 + vertical + + + True + False + 10 + 10 + 10 + 10 + 0 + in + + + True + False + + + 120 + True + False + 100 + 1 + index key length : + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + index key : + False + + + 0 + 1 + 1 + 1 + + + + + 560 + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + mark : + + + 0 + 2 + 1 + 1 + + + + + True + True + + True + + + 1 + 2 + 1 + 1 + + + + + True + False + 1 + files key length : + + + 0 + 3 + 1 + 1 + + + + + True + True + start + 4 + + True + 128 + adjustment2 + + + 1 + 3 + 1 + 1 + + + + + True + False + + + True + True + start + + True + True + adjustment1 + True + + + False + True + 0 + + + + + Generate Key + True + True + True + 5 + image16 + + + + False + True + 1 + + + + + 1 + 0 + 1 + 1 + + + + + + + True + False + <b> Encryption </b> + True + + + + + False + False + 0 + + + + + True + False + center + label + + + True + True + 1 + + + + + 0 + 9 + 3 + 6 + + + + + + + True + False + <b> Main Account </b> + True + + + + + False + False + 0 + + + + + False + True + 1 + + + + + True + False + start + True + + + save profile + True + True + True + end + image13 + + + + False + True + 3 + 0 + + + + + delete profile + True + True + True + start + image14 + + + + False + True + 3 + 1 + + + + + True + True + 3 + + + + + + + + + True + False + <b>Profile Edition</b> + True + + + + + 1 + False + + + + + True + False + + + True + False + gtk-preferences + + + False + True + 0 + + + + + True + False + 9 + 39 + Config + + + False + True + 1 + + + + + 1 + False + + + + + + + + + + + True + True + 1 + + + + + True + False + vertical + 2 + + + False + True + 4 + + + + + + diff --git a/resources/imprastorage.desktop b/resources/imprastorage.desktop new file mode 100644 index 0000000..f5154bb --- /dev/null +++ b/resources/imprastorage.desktop @@ -0,0 +1,12 @@ + +[Desktop Entry] +Name=ImpraStorage +Comment=ImpraStorage provided a private imap access to store large files +Version=2.18 +Icon=/usr/share/pixmaps/impra/impra.png +Exec=impra +Terminal=false +Type=Application +StartupNotify=false +MimeType= +Categories=System; diff --git a/resources/pixmaps/impra/impra.png b/resources/pixmaps/impra/impra.png new file mode 100755 index 0000000000000000000000000000000000000000..e49b190ae8c6841d4744d7aa088a693ed6a48aff GIT binary patch literal 17155 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 T4b|fRy|^eNp(tJ@Y8do?+Oe_D literal 0 HcmV?d00001 diff --git a/scripts/imprastorage b/scripts/imprastorage new file mode 100755 index 0000000..3d20cfc --- /dev/null +++ b/scripts/imprastorage @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +from psr.sys import Sys, Const +from impra.gui import AppGui + +def main(): + try: + c = 0 + AppGui() + except Exception as e: + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/scripts/imprastorage-cli b/scripts/imprastorage-cli new file mode 100755 index 0000000..b427feb --- /dev/null +++ b/scripts/imprastorage-cli @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah-cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +from psr.sys import Sys, Const +from impra.cli import Cli + +def main(): + try: + c = 0 + Cli('.'+Sys.sep) + except Exception as e : + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/scripts/kirmah-cli b/scripts/kirmah-cli new file mode 100755 index 0000000..48d4336 --- /dev/null +++ b/scripts/kirmah-cli @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# kirmah-cli.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : Kirmah +# version : 2.18 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of Kirmah. +# +# Kirmah is free software (free as in speech) : 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 (at your option) any later version. +# +# Kirmah 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 Kirmah. If not, see . +# + +from psr.sys import Sys, Const +from kirmah.cli import Cli + +def main(): + try: + c = 0 + Cli('.'+Sys.sep) + except Exception as e : + Sys.pwarn((('main : ',(str(e),Sys.CLZ_ERROR_PARAM), ' !'),), True) + #~ raise e + c = 1 + return c + +if __name__ == '__main__': + Sys.exit(main()) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9287ba6 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- +# setup.py +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . +# + +from impra import conf +from distutils.core import setup +import glob +import os + +# I18N +I18NFILES = [] + +for filepath in glob.glob('resources/locale/*/LC_MESSAGES/*.mo'): + lang = filepath[len('resources/locale/'):] + targetpath = os.path.dirname(os.path.join('share/locale',lang)) + I18NFILES.append((targetpath, [filepath])) + +setup(name = conf.PRG_NAME, + version = conf.PRG_VERS, + packages = [conf.PRG_PACKAGE, 'psr'], + scripts = ['scripts/'+conf.PRG_SCRIPT, 'scripts/'+conf.PRG_CLI_NAME], + data_files= [('/usr/share/pixmaps/'+conf.PRG_PACKAGE , glob.glob('resources/pixmaps/'+conf.PRG_PACKAGE+'/*.png')), + ('/usr/share/applications' , ['resources/'+conf.PRG_PACKAGE+'.desktop']), + ('/usr/share/'+conf.PRG_PACKAGE , glob.glob('resources/'+conf.PRG_PACKAGE+'/LICENSE')), + ('/usr/share/'+conf.PRG_PACKAGE+'/glade' , glob.glob('resources/'+conf.PRG_PACKAGE+'/glade/*.glade'))] + + I18NFILES + ) diff --git a/setup_build.py b/setup_build.py index 64d60ed..d8d51b9 100644 --- a/setup_build.py +++ b/setup_build.py @@ -1,30 +1,31 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# # -# software : ImpraStorage # -# version : 0.8 # -# date : 2012 # -# licence : GPLv3.0 # -# author : a-Sansara # -# copyright : pluie.org # -# # -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#-*- coding: utf-8 -*- +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# This file is part of ImpraStorage. +# software : ImpraStorage +# version : 1.01 +# date : 2014 +# licence : GPLv3.0 +# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]> +# copyright : pluie.org # -# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# ImpraStorage 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. +# This file is part of ImpraStorage. +# +# ImpraStorage is free software (free as in speech) : 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 (at your option) any later version. +# +# ImpraStorage 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 ImpraStorage. If not, see . # -# You should have received a copy of the GNU General Public License -# along with ImpraStorage. If not, see . import sys from cx_Freeze import setup, Executable @@ -59,9 +60,9 @@ if 'bdist_msi' in sys.argv: ) setup( name="ImpraStorage.exe", - version="0.7", + version="1.01", author="a-Sansara", - description="ImpraStorage provided a private imap access to store large files. License GNU GPLv3 Copyright 2012 pluie.org", + description="ImpraStorage provided a private imap access to store large files. License GNU GPLv3 Copyright 2014 pluie.org", executables=[exe], options = {"build_exe": build_exe_options}, scripts=[ @@ -71,7 +72,7 @@ if 'bdist_msi' in sys.argv: else : setup( name = "ImpraStorage", - version = "0.7", + version = "1.01", description = "ImpraStorage provided a private imap access to store large files", options = {"build_exe": build_exe_options}, executables = [Executable("imprastorage.py", base=base)])