#!/usr/bin/env python3
#-*- coding: utf-8 -*-
# impra/cliapp.py
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# software : ImpraStorage
# version : 1.01
# date : 2014
# licence : GPLv3.0
# author : a-Sansara <[a-sansara]at[clochardprod]dot[net]>
# copyright : pluie.org
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# This file is part of ImpraStorage.
#
# ImpraStorage is free software (free as in speech) : you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# ImpraStorage is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with ImpraStorage. If not, see .
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ module cliapp ~~
import impra.conf as conf
from impra.core import ImpraStorage, ImpraConf
from impra.ini import KiniFile
from kirmah.crypt import Kirmah, BadKeyException, KeyGen, represents_int, KeyGen, represents_int
from psr.sys import Sys, Const, Io
from psr.log import Log
from psr.imap import BadHostException, BadLoginException
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ class CliApp ~~
class CliApp:
@Log(Const.LOG_BUILD)
def __init__(self, home, path, parser, Cli, a, o):
""""""
self.parser = parser
self.Cli = Cli
self.a = a
self.o = o
self.home = home
self.stime = Sys.datetime.now()
self.account = 'default'
self.rootBox = '__impra__2'
self.uid = '0'
self.date = ''
if not Io.file_exists('impra2.ini'+Kirmah.EXT):
print('ini file not exist')
if len(self.a)>0 and self.a[0]=='conf' and self.o.save :
kg = KeyGen()
Io.set_data('impra2.ini.key', kg.key)
self.ini = KiniFile('impra2.ini')
self.ini.set('key' , kg.key , self.account+'.keys')
self.ini.set('mark', kg.mark , self.account+'.keys')
self.ini.set('salt', '-¤-ImpraStorage-¤-' , self.account+'.keys')
else :
self.needConfig()
else :
if not (len(self.a)>0 and self.a[0]=='conf') :
self.ini = KiniFile('impra2.ini')
self.impst = ImpraStorage(ImpraConf(self.ini),wkdir=path)
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()