diff --git a/kirmah/cli.py b/kirmah/cli.py index 478f9af..cfcc1da 100755 --- a/kirmah/cli.py +++ b/kirmah/cli.py @@ -126,19 +126,10 @@ class Cli(AbstractCli): Sys.g.LOG_QUEUE.put(Sys.g.SIGNAL_STOP) else : - self.parser.error_cmd((('unknow command ',(a[0],Sys.Clz.fgb3)),)) + self.parser.error_cmd((('unknow command ',(a[0],Sys.Clz.fgb3)),), True) if not o.quiet : Sys.dprint() -#~ - #~ @staticmethod - #~ def error_cmd(data): - #~ """""" - #~ Cli.print_usage('') - #~ Sys.dprint() - #~ Sys.pwarn(data, True) - #~ Cli.exit(1) - @staticmethod def print_usage(data, withoutHeader=False): diff --git a/kirmah/cliapp.py b/kirmah/cliapp.py index 87a12aa..3761578 100755 --- a/kirmah/cliapp.py +++ b/kirmah/cliapp.py @@ -87,9 +87,7 @@ class CliApp: if self.o.outputfile is None : self.o.outputfile = Sys.basename(self.a[1]) if self.o.outputfile[-len(Kirmah.EXT):] != Kirmah.EXT : - print(self.o.outputfile[-len(Kirmah.EXT):]) self.o.outputfile += Kirmah.EXT - print(self.o.outputfile) 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)) diff --git a/kirmah/conf.py b/kirmah/conf.py index 0881656..7462850 100755 --- a/kirmah/conf.py +++ b/kirmah/conf.py @@ -48,7 +48,7 @@ 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) +#~ 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') diff --git a/kirmah/crypt.py b/kirmah/crypt.py index 8b1a9c0..703174e 100755 --- a/kirmah/crypt.py +++ b/kirmah/crypt.py @@ -63,7 +63,7 @@ def hash_sha256_file(path): return sha256(open(path, mode='rb').read()).hexdigest() -@Log() +@Log(Const.LOG_ALL) def hash_md5_file(path): """Get a md5 hash of file from path :Returns: `str` @@ -104,7 +104,7 @@ class KeyGen : """""" @Log(Const.LOG_BUILD) - def __init__(self, length, salt=None): + def __init__(self, length=1024, salt=None): """""" self.new(length, salt) @@ -613,8 +613,7 @@ class Kirmah: @Log() def split(self, fromPath, hlst): """""" - if not Sys.is_cli_cancel(): - self.DIR_OUTBOX = '' + if not Sys.is_cli_cancel(): f = open(fromPath, 'rb+') m, p, rsz = mmap(f.fileno(), 0), 0, 0 fsize = Sys.getsize(fromPath) @@ -766,11 +765,11 @@ class Kirmah: d = Sys.datetime.now() if compstart : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Compressing data') + if not Sys.g.QUIET : Sys.ptask('Compressing data') self.compress_start(fp, tp, compstart, emit=emit) if compstart : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Compression mode', d, True) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 if emit : Sys.cli_emit_progress(5) return fp, tp, decHeader['rmode'], decHeader['mmode'], compend @@ -783,14 +782,14 @@ class Kirmah: if rmode : #~ self.mpRandomFileContent(fp, tp, 4) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Randomizing data') + if not Sys.g.QUIET : Sys.ptask('Randomizing data') self.randomFileContent(fp, tp, emit=emit) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 if emit : Sys.cli_emit_progress(75) if mmode : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Mixing data') + if not Sys.g.QUIET : Sys.ptask('Mixing data') self.mixdata(fp, tp, True, emit=emit) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 @@ -799,17 +798,17 @@ class Kirmah: if compend : d = Sys.datetime.now() if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Compressing data') + if not Sys.g.QUIET : Sys.ptask('Compressing data') self.compress_end(fp, toPath, compend, emit=emit) if emit : Sys.cli_emit_progress(95) if compend : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Compression mode', d, True) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) # clean tmp files if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Cleaning') + if not Sys.g.QUIET : Sys.ptask('Cleaning') try : Sys.removeFile(self.tmpPath1) @@ -850,21 +849,21 @@ class Kirmah: def encrypt_mproc(self, fp, tp, nproc=1, emit=True): """""" if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Encrypting data') + if not Sys.g.QUIET : Sys.ptask('Encrypting data') d = Sys.datetime.now() c = not Sys.is_cli_cancel() if c: if nproc == 1 : self.encryptToFile(fp, tp) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Encrypt data', d, c) + if not Sys.g.QUIET : Sys.pstep('Encrypt data', d, c) else : hlstPaths = self.prepare_mproc_encode(fp, nproc) mg = Manager(self.mproc_encode_part, nproc, None, Sys.g.MPEVENT) mg.run() self.mpMergeFiles(hlstPaths, tp, emit=emit) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Encrypt data (multiproc)', d, c) + if not Sys.g.QUIET : Sys.pstep('Encrypt data (multiproc)', d, c) if emit : Sys.cli_emit_progress(70) @@ -901,9 +900,9 @@ class Kirmah: if len(decHeader) > 0 : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) if decHeader['smode'] == self.mark[fsize%len(self.mark)] : - Sys.pstep('Reading Header', d, True) + if not Sys.g.QUIET : Sys.pstep('Reading Header', d, True) else : - Sys.pstep('Reading Header', d, False, False, False) + if not Sys.g.QUIET : Sys.pstep('Reading Header', d, False, False, False) raise BadKeyException('wrong key') compend, compstart = not decHeader['cmode']== KirmahHeader.COMP_NONE, decHeader['cmode']== KirmahHeader.COMP_ALL @@ -913,26 +912,26 @@ class Kirmah: if compend : d = Sys.datetime.now() if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Uncompressing data') + if not Sys.g.QUIET : Sys.ptask('Uncompressing data') self.uncompress_end(fp, tp, compend, emit=emit) if emit : Sys.cli_emit_progress(10) if compend : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Compression mode', d, True) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 if decHeader['mmode'] : d = Sys.datetime.now() if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Sorting data') + if not Sys.g.QUIET : Sys.ptask('Sorting data') self.unmixdata(fp, tp, emit=emit) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 if emit : Sys.cli_emit_progress(20) if decHeader['rmode'] : d = Sys.datetime.now() if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Reordering data') + if not Sys.g.QUIET : Sys.ptask('Reordering data') self.unRandomFileContent(fp, tp, emit=emit) fp, tp = tp, self.tmpPath2 if tp == self.tmpPath1 else self.tmpPath1 if emit : Sys.cli_emit_progress(25) @@ -947,15 +946,15 @@ class Kirmah: if emit : Sys.cli_emit_progress(80) if compstart : if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Uncompressing data') + if not Sys.g.QUIET : Sys.ptask('Uncompressing data') self.uncompress_start(fromPath, toPath, compstart, emit=emit) if emit : Sys.cli_emit_progress(90) if compstart: if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Compression mode', d, True) + if not Sys.g.QUIET : Sys.pstep('Compression mode', d, True) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Cleaning') + if not Sys.g.QUIET : Sys.ptask('Cleaning') if emit : Sys.cli_emit_progress(95) try : Sys.removeFile(self.tmpPath1) @@ -969,7 +968,7 @@ class Kirmah: def decrypt_mproc(self, fromPath, toPath, nproc=1, emit=True): """""" if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.ptask('Decrypting data') + if not Sys.g.QUIET : Sys.ptask('Decrypting data') d = Sys.datetime.now() c = not Sys.is_cli_cancel() if c: @@ -977,14 +976,14 @@ class Kirmah: if nproc == 1 : self.decryptToFile(fromPath, toPath) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Decrypt data', d, True) + if not Sys.g.QUIET : Sys.pstep('Decrypt data', d, True) else : hlstPaths = self.prepare_mproc_decode(fromPath, nproc) mg = Manager(self.mproc_decode_part, nproc, None, Sys.g.MPEVENT, emit=True) mg.run() self.mpMergeFiles(hlstPaths, toPath, emit=emit) if Sys.g.DEBUG : Sys.wlog(Sys.dprint()) - Sys.pstep('Decrypt data (multiproc)', d, True) + if not Sys.g.QUIET : Sys.pstep('Decrypt data (multiproc)', d, True) diff --git a/psr/cli.py b/psr/cli.py index f05057b..c49b487 100644 --- a/psr/cli.py +++ b/psr/cli.py @@ -87,6 +87,7 @@ class AbstractCli(): """""" 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 @@ -98,7 +99,7 @@ class AbstractCli(): 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): """""" @@ -127,7 +128,7 @@ class AbstractCli(): Sys.wlog(a) Sys.wlog(b + c + d + e + f ) Sys.wlog(a) - Sys.wlog(Sys.dprint()) + #~ Sys.wlog(Sys.dprint()) @staticmethod diff --git a/psr/imap.py b/psr/imap.py new file mode 100644 index 0000000..6e9f1dc --- /dev/null +++ b/psr/imap.py @@ -0,0 +1,488 @@ +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 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 = port + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImapClient ~~ + +class ImapClient(IMAP4_SSL): + """""" + + Commands['XLIST'] = ('AUTH', 'SELECTED') + + @Log(Const.LOG_DEBUG) + def xlist(self, directory='""', pattern='*'): + """(X)List mailbox names in directory matching pattern. Using Google's XLIST extension + + (status, [data]) = .xlist(directory='""', pattern='*') + + 'data' is list of XLIST responses. + + thks to barduck : http://stackoverflow.com/users/602242/barduck + """ + try : + name = 'XLIST' + status, data = self._simple_command(name, directory, pattern) + return self._untagged_response(status, data, name) + except : + return 'NO', '' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class ImapHelper ~~ + +class ImapHelper: + """""" + + K_HEAD, K_DATA = 0, 1 + """""" + OK = 'OK' + """""" + KO = 'NO' + """""" + ENCODING = 'utf-8' + """""" + REG_SATUS = r'^"?(.*)"? \(([^\(]*)\)' + """""" + NO_SELECT = '\\Noselect' + """""" + CHILDREN = '\\HasChildren' + """""" + NO_CHILDREN = '\\HasNoChildren' + """""" + INBOX = '\\Inbox' + """""" + DRAFTS = '\\Drafts' + """""" + TRASH = '\\Trash' + """""" + SENT = '\\Sent' + """""" + DELETED = '\\Deleted' + """""" + FLAGS = '+FLAGS' + """""" + + @Log(Const.LOG_BUILD) + def __init__(self, conf, box='INBOX'): + """""" + if 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.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.print(' Attempt to login... ' , Sys.Clz.fgB7, False) + Sys.print('(' , Sys.Clz.fgn7, False) + Sys.print(conf.user , Sys.Clz.fgB2, False) + Sys.print('@' , Sys.Clz.fgn7, False) + Sys.print(conf.host , Sys.Clz.fgB4, False) + Sys.print(':' , Sys.Clz.fgn7, False) + Sys.print(conf.port , Sys.Clz.fgB3, False) + Sys.print(')' , Sys.Clz.fgn7) + 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.print(' connected' , Sys.Clz.fgB2) + self.cnxusr = conf.user + try : + status, resp = self.cnx.select(self.rootBox) + if status == self.KO : + 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.print(' Deleting msg ', Sys.Clz.fgn7, False, False) + Sys.print(delids , Sys.Clz.fgB1, False, False) + Sys.print(' '+status , Sys.Clz.fgB7) + 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): + """""" + 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() + def send(self, msg, box='INBOX'): + """""" + mid = None + date = Time2Internaldate(Sys.time()) + status, resp = self.cnx.append(box, '\Draft', date, bytes(msg,'utf-8')) + if status==self.OK: + mid = str(resp[0],'utf-8')[11:-11].split(' ') + return mid + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class BadLoginException ~~ + +class BadLoginException(BaseException): + pass + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~ class BadLoginException ~~ + +class BadHostException(BaseException): + pass diff --git a/psr/ini.py b/psr/ini.py new file mode 100644 index 0000000..93e45d1 --- /dev/null +++ b/psr/ini.py @@ -0,0 +1,170 @@ +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 = {} + self.read() + + + @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] + + + @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] + 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.print('['+sectionName+']', Sys.Clz.fgB3) + else: + Sys.print('['+sectionName.split('.')[1]+']', Sys.Clz.fgB3) + if sectionName in self.dic : + for k in sorted(self.dic[sectionName]): + k = k.rstrip(' ') + a = '' + Sys.print(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.print(self.dic[sectionName][k][:98]+a, Sys.Clz.fgN2) + else: + Sys.print('key is masked', Sys.Clz.fgb1) + + @Log() + def read(self): + """""" + try: + with Io.rfile(self.path, False) as fi: + csection = 'main' + self.dic[csection] = {} + for l in fi: + l = l.rstrip().lstrip() + if len(l) > 0 and not l[0]=='#' : + d = regsplit(' *= *', l , 1) + if len(d)> 1: + self.dic[csection][d[0]] = d[1] if d[1] is not None else '' + elif len(l)>0 and l[0]=='[': + csection = l.strip('[]') + self.dic[csection] = {} + except IOError : + pass + + + @Log() + def delete(self): + Io.removeFile(self.path) + self.dic = {} diff --git a/psr/log.py b/psr/log.py index 4dd7393..65769c7 100644 --- a/psr/log.py +++ b/psr/log.py @@ -86,7 +86,7 @@ class Log: 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 b'...' + 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 diff --git a/psr/sys.py b/psr/sys.py index 6aa1f1b..c9956dd 100644 --- a/psr/sys.py +++ b/psr/sys.py @@ -48,10 +48,10 @@ class Sys: """""" from platform import system as getSysName - from os import system as sysCall, remove as removeFile, makedirs, sep, getpid + 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 + from datetime import datetime, timedelta from sys import exit from os.path import abspath, dirname, join, realpath, basename, getsize, isdir, splitext from math import log, floor, ceil @@ -164,6 +164,8 @@ class Sys: """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 @@ -261,6 +263,7 @@ class Sys: @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 : @@ -309,11 +312,16 @@ class Sys: 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.print(label+' ', Sys.CLZ_IO, False) - v = ''.join(['{:.5f}'.format(Sys.time()-(Sys.mktime(t.timetuple())+1e-6*t.microsecond)),' s']) + v = Sys.getDelta(t) if not dbcall : Sys.print(v, Sys.CLZ_DELTA)