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 @@
+
+
+
+
+
+
+
+ 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
+
+
+
+ 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 0000000..e49b190
Binary files /dev/null and b/resources/pixmaps/impra/impra.png differ
diff --git a/resources/pixmaps/impra/impra_ico.png b/resources/pixmaps/impra/impra_ico.png
new file mode 100755
index 0000000..fd0d9d1
Binary files /dev/null and b/resources/pixmaps/impra/impra_ico.png differ
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)])