version 1.01 - compat kirmah 2.18
This commit is contained in:
parent
b9d35d7e9b
commit
acd09ff551
12
CHANGELOG
Executable file
12
CHANGELOG
Executable file
|
@ -0,0 +1,12 @@
|
|||
ImpraStorage 1.01 (2014-07-18)
|
||||
=========================
|
||||
|
||||
New General Features
|
||||
-------------------------
|
||||
|
||||
*
|
||||
|
||||
Bugfixes
|
||||
-------------------------
|
||||
|
||||
* [#] --
|
24
README
Executable file
24
README
Executable file
|
@ -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
|
43
cmd.bat
43
cmd.bat
|
@ -1,28 +1,29 @@
|
|||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
:: #
|
||||
:: software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
:: version : 0.8 #
|
||||
:: date : 2012 #
|
||||
:: licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
:: author : a-Sansara <http://www.a-sansara.net/> #
|
||||
:: copyright : pluie.org <http://www.pluie.org/> #
|
||||
:: #
|
||||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
::
|
||||
:: This file is part of ImpraStorage.
|
||||
:: software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
:: version : 1.01
|
||||
:: date : 2014
|
||||
:: licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
:: author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
:: copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
::
|
||||
:: You should have received a copy of the GNU General Public License
|
||||
:: along with ImpraStorage. If not, see <http://www.gnu.org/licenses/>.
|
||||
@echo off
|
||||
TITLE ImpraStorage (GNU GPLv3)
|
||||
set cmd=''
|
||||
|
|
45
impra-cli.py
Executable file
45
impra-cli.py
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra-cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
45
impra.py
Executable file
45
impra.py
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
|
@ -0,0 +1 @@
|
|||
|
264
impra/app.py
Executable file
264
impra/app.py
Executable file
|
@ -0,0 +1,264 @@
|
|||
#!/usr/bin/env python
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/app.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
||||
|
1852
impra/cli.py
Normal file → Executable file
1852
impra/cli.py
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
573
impra/cliapp.py
Executable file
573
impra/cliapp.py
Executable file
|
@ -0,0 +1,573 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/cliapp.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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()
|
88
impra/conf.py
Executable file
88
impra/conf.py
Executable file
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/conf.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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'
|
1514
impra/core.py
Normal file → Executable file
1514
impra/core.py
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
|
@ -1,33 +1,35 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.pluie.org/> #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/cryot.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# This file is part of ImpraStorage.
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with ImpraStorage. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ package crypt ~~
|
||||
# ~~ module crypt ~~
|
||||
|
||||
from base64 import urlsafe_b64encode, b64decode
|
||||
from binascii import b2a_base64, a2b_base64
|
||||
|
|
858
impra/gui.py
Executable file
858
impra/gui.py
Executable file
|
@ -0,0 +1,858 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/gui.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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)
|
453
impra/imap.py
453
impra/imap.py
|
@ -1,453 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
730
impra/index.py
Executable file
730
impra/index.py
Executable file
|
@ -0,0 +1,730 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/index.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
101
impra/ini.py
Executable file
101
impra/ini.py
Executable file
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/ini.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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 = {}
|
95
impra/mail.py
Executable file
95
impra/mail.py
Executable file
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/mail.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
415
impra/ui.py
Executable file
415
impra/ui.py
Executable file
|
@ -0,0 +1,415 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# impra/ui.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
515
impra/util.py
515
impra/util.py
|
@ -1,515 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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()
|
|
@ -1,71 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
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)
|
|
@ -1,30 +1,31 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.pluie.org/> #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# This file is part of ImpraStorage.
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with ImpraStorage. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from impra.core import ImpraConf, ImpraStorage, realpath, dirname, abspath, sep
|
||||
from impra.util import IniFile, RuTime, get_file_path, Clz, mprint
|
||||
|
|
45
kirmah-cli.py
Executable file
45
kirmah-cli.py
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah-cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
1
kirmah/__init__.py
Executable file
1
kirmah/__init__.py
Executable file
|
@ -0,0 +1 @@
|
|||
|
217
kirmah/app.py
Executable file
217
kirmah/app.py
Executable file
|
@ -0,0 +1,217 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah/app.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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):
|
||||
""""""
|
461
kirmah/cli.py
Executable file
461
kirmah/cli.py
Executable file
|
@ -0,0 +1,461 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah/cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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()
|
334
kirmah/cliapp.py
Executable file
334
kirmah/cliapp.py
Executable file
|
@ -0,0 +1,334 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah/cliapp.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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())
|
125
kirmah/conf.py
Executable file
125
kirmah/conf.py
Executable file
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah/conf.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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'
|
1218
kirmah/crypt.py
Executable file
1218
kirmah/crypt.py
Executable file
File diff suppressed because one or more lines are too long
418
kirmah/gui.py
Executable file
418
kirmah/gui.py
Executable file
|
@ -0,0 +1,418 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah.gui.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
372
kirmah/ui.py
Executable file
372
kirmah/ui.py
Executable file
|
@ -0,0 +1,372 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah/ui.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
43
launcher.bat
43
launcher.bat
|
@ -1,28 +1,29 @@
|
|||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
:: #
|
||||
:: software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
:: version : 0.8 #
|
||||
:: date : 2012 #
|
||||
:: licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
:: author : a-Sansara <http://www.a-sansara.net/> #
|
||||
:: copyright : pluie.org <http://www.pluie.org/> #
|
||||
:: #
|
||||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
:: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
::
|
||||
:: This file is part of ImpraStorage.
|
||||
:: software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
:: version : 1.01
|
||||
:: date : 2014
|
||||
:: licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
:: author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
:: copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
::
|
||||
:: You should have received a copy of the GNU General Public License
|
||||
:: along with ImpraStorage. If not, see <http://www.gnu.org/licenses/>.
|
||||
@Echo Off
|
||||
Set _Title=ImpraStorage
|
||||
Set _Height=60
|
||||
|
|
3
psr/__init__.py
Executable file
3
psr/__init__.py
Executable file
|
@ -0,0 +1,3 @@
|
|||
|
||||
|
||||
|
224
psr/cli.py
Executable file
224
psr/cli.py
Executable file
|
@ -0,0 +1,224 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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():
|
||||
""""""
|
105
psr/const.py
Executable file
105
psr/const.py
Executable file
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/const.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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()
|
553
psr/imap.py
Executable file
553
psr/imap.py
Executable file
|
@ -0,0 +1,553 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/imap.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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]) = <instance>.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
|
209
psr/ini.py
Executable file
209
psr/ini.py
Executable file
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/ini.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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 = {}
|
260
psr/io.py
Executable file
260
psr/io.py
Executable file
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/io.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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)
|
127
psr/log.py
Executable file
127
psr/log.py
Executable file
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/log.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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)
|
155
psr/mproc.py
Executable file
155
psr/mproc.py
Executable file
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/mproc.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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])
|
610
psr/sys.py
Executable file
610
psr/sys.py
Executable file
|
@ -0,0 +1,610 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/sys.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# ~~ 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
|
||||
}
|
71
psr/w32color.py
Executable file
71
psr/w32color.py
Executable file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# psr/w32color.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
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)
|
674
resources/impra/LICENSE
Executable file
674
resources/impra/LICENSE
Executable file
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
2398
resources/impra/glade/impra.glade
Executable file
2398
resources/impra/glade/impra.glade
Executable file
File diff suppressed because it is too large
Load Diff
12
resources/imprastorage.desktop
Normal file
12
resources/imprastorage.desktop
Normal file
|
@ -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;
|
BIN
resources/pixmaps/impra/impra.png
Executable file
BIN
resources/pixmaps/impra/impra.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
resources/pixmaps/impra/impra_ico.png
Executable file
BIN
resources/pixmaps/impra/impra_ico.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
45
scripts/imprastorage
Executable file
45
scripts/imprastorage
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
45
scripts/imprastorage-cli
Executable file
45
scripts/imprastorage-cli
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah-cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
45
scripts/kirmah-cli
Executable file
45
scripts/kirmah-cli
Executable file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# kirmah-cli.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : Kirmah <http://kirmah.sourceforge.net/>
|
||||
# version : 2.18
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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())
|
53
setup.py
Normal file
53
setup.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
# setup.py
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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
|
||||
)
|
|
@ -1,30 +1,31 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# software : ImpraStorage <http://imprastorage.sourceforge.net/> #
|
||||
# version : 0.8 #
|
||||
# date : 2012 #
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/> #
|
||||
# author : a-Sansara <http://www.a-sansara.net/> #
|
||||
# copyright : pluie.org <http://www.pluie.org/> #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#-*- coding: utf-8 -*-
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# This file is part of ImpraStorage.
|
||||
# software : ImpraStorage <http://kirmah.sourceforge.net/>
|
||||
# version : 1.01
|
||||
# date : 2014
|
||||
# licence : GPLv3.0 <http://www.gnu.org/licenses/>
|
||||
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
|
||||
# copyright : pluie.org <http://www.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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with ImpraStorage. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)])
|
||||
|
|
Loading…
Reference in New Issue
Block a user