piknik

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

commit 9693b803ac4c9d01aeccab1f0b23e19f2b461aa3
parent 30a4a685c1d034cca0c905c174c5d6f7838dd842
Author: lash <dev@holbrook.no>
Date:   Fri,  2 Dec 2022 11:24:15 +0000

Retrieve correct signing material for verify

Diffstat:
Mpiknik/crypto.py | 48+++++++++++++++++++++++-------------------------
Mpiknik/msg.py | 3+--
Mpiknik/render/base.py | 51+++++++++++++++++++++++++++++++++------------------
Mpiknik/render/ini.py | 4++--
Mpiknik/render/plain.py | 63+++++++++++++++++++++++++++++++++++----------------------------
Mpiknik/runnable/show.py | 17++---------------
Mpiknik/wrap.py | 42++++++++++++++++++++++++++++++------------
7 files changed, 126 insertions(+), 102 deletions(-)

diff --git a/piknik/crypto.py b/piknik/crypto.py @@ -9,7 +9,6 @@ import gnupg # local imports from piknik.error import VerifyError -from piknik.msg import MessageEnvelope from piknik.wrap import Wrapper logg = logging.getLogger(__name__) @@ -24,8 +23,7 @@ class PGPSigner(Wrapper): self.default_key = default_key self.passphrase = passphrase self.use_agent = use_agent - self.__envelope_state = -1 # -1 not in envelope, 0 in outer envelope, 1 inner envelope, not (yet) valid, 2 envelope valid (with signature) - self.__envelope = None + self.sign_material = None self.__skip_verify = skip_verify @@ -42,7 +40,6 @@ class PGPSigner(Wrapper): def sign(self, msg, passphrase=None): # msg = IssueMessage object m = Message() - v = msg.as_string() m.set_type('multipart/relative') m.add_header('X-Piknik-Envelope', 'pgp') ms = Message() @@ -51,6 +48,7 @@ class PGPSigner(Wrapper): ms.add_header('Content-Disposition', 'attachment', filename=fn) self.set_from(msg, passphrase=passphrase) + v = msg.as_string() sig = self.gpg.sign(v, keyid=self.default_key, detach=True, passphrase=self.passphrase) ms.set_payload(str(sig)) @@ -60,32 +58,32 @@ class PGPSigner(Wrapper): return m - def envelope_callback(self, msg, env_header): - self.__envelope = None + def process_envelope(self, msg, env_header): + self.envelope = None if env_header != 'pgp': raise VerifyError('expected envelope type "pgp", but got {}'.format(env_header)) - if self.__envelope_state > -1 and self.__envelope_state < 2: - raise VerifyError('new envelope before previous was verified ({})'.format(self.__envelope_state)) - self.__envelope = msg - self.__envelope_state = 0 - return MessageEnvelope(msg) + if self.envelope_state > -1 and self.envelope_state < 2: + raise VerifyError('new envelope before previous was verified ({})'.format(self.envelope_state)) + self.sign_material = None + super(PGPSigner, self).process_envelope(msg, env_header) + return self.envelope - def message_callback(self, envelope, msg, message_id, message_date): + def process_message(self, envelope, msg, message_id, message_date): if msg.get('From') != None: - envelope.sender = msg.get('From') + self.envelope.sender = msg.get('From') - if self.__envelope_state == 0: - self.add(message_id, msg) - self.__envelope_state = 1 - self.__envelope = msg - return (envelope, msg,) + if self.envelope_state == 0: + self.envelope_state = 1 + self.sign_material = msg + return (self.envelope, msg,) if msg.get('Content-Type') != 'application/pgp-signature': - self.add(message_id, msg) - return (envelope, msg,) + self.add(self.envelope, message_id, msg) + return (self.envelope, msg,) + + v = self.sign_material.as_string() - v = self.__envelope.as_string() sig = msg.get_payload() (fd, fp) = tempfile.mkstemp() f = os.fdopen(fd, 'w') @@ -105,8 +103,8 @@ class PGPSigner(Wrapper): raise VerifyError('invalid signature for message {}'.format(message_id)) else: logg.debug('signature ok from {}'.format(r.fingerprint)) - envelope.valid = True - #envelope.sender = r.fingerprint - self.__envelope_state = 2 + self.envelope.valid = True + self.envelope.sender = r.fingerprint + self.envelope_state = 2 - return (envelope, msg,) + return (self.envelope, msg,) diff --git a/piknik/msg.py b/piknik/msg.py @@ -68,8 +68,7 @@ class IssueMessage: message_ids.append(message_id) d = m.get('Date') message_date = parsedate_to_datetime(d) - else: - message_callback(envelope, m, message_id, message_date) + message_callback(m, message_id, message_date) if post_callback != None: post_callback(message_ids) diff --git a/piknik/render/base.py b/piknik/render/base.py @@ -14,12 +14,10 @@ def stream_accumulator(v, w=sys.stdout): class Renderer: - def __init__(self, basket, accumulator=None, envelope_callback=None, message_callback=None): + def __init__(self, basket, accumulator=None, wrapper=None): self.b = basket - #self.e = None self.a = accumulator - self.message_callback = message_callback - self.envelope_callback = envelope_callback + self.w = wrapper def add(self, v, accumulator=None): @@ -49,7 +47,11 @@ class Renderer: def apply_message_post(self, state, issue, tags, envelope, message, message_id, message_date, accumulator=None): pass - + + def apply_message_post(self, state, issue, tags, envelope, message, message_id, message_date, accumulator=None): + pass + + def apply_message(self, state, issue, tags, envelope, message, message_id, message_date, accumulator=None): pass @@ -65,8 +67,8 @@ class Renderer: def apply_issue(self, state, issue, tags, accumulator=None): def envelope_callback(envelope, envelope_type): - if self.envelope_callback != None: - envelope = self.envelope_callback(envelope, envelope_type) + if self.w != None: + envelope = self.w.process_envelope(envelope, envelope_type) else: envelope = MessageEnvelope(envelope) r = self.apply_envelope_pre(state, issue, tags, envelope, accumulator=accumulator) @@ -77,19 +79,32 @@ class Renderer: self.add(r) return envelope - def message_callback(envelope, message, message_id, message_date): - if self.message_callback != None: - (envelope, message) = self.message_callback(envelope, message, message_id, message_date) - r = self.apply_message_pre(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) - self.add(r) - r = self.apply_message(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) - self.add(r) - r = self.apply_message_post(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) - self.add(r) + def message_callback(message, message_id, message_date): + envelope = None + if self.w != None: + (envelope, message) = self.w.process_message(envelope, message, message_id, message_date) + else: + logg.warning('no wrapper defined. no message parts will be output') + + initial = True + while True: + v = self.w.pop() + if v == None: + break + if initial: + r = self.apply_message_pre(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) + self.add(r) + r = self.apply_message(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) + self.add(r) + r = self.apply_message_post(state, issue, tags, envelope, message, message_id, message_date, accumulator=accumulator) + self.add(r) + initial = False + + r = self.apply_message_part(state, issue, tags, envelope, message, message_date, v) + self.add(r) + return (envelope, message,) - #for msg in self.b.get_msg(issue.id, envelope_callback=envelope_callback, message_callback=message_callback): - logg.debug('in msg') self.b.get_msg(issue.id, envelope_callback=envelope_callback, message_callback=message_callback) diff --git a/piknik/render/ini.py b/piknik/render/ini.py @@ -5,8 +5,8 @@ from .base import stream_accumulator class Renderer(BaseRenderer): - def __init__(self, basket, accumulator=stream_accumulator): - super(Renderer, self).__init__(basket, accumulator=accumulator) + def __init__(self, basket, accumulator=stream_accumulator, **kwargs): + super(Renderer, self).__init__(basket, accumulator=accumulator, **kwargs) def apply_state(self, state, accumulator=None): diff --git a/piknik/render/plain.py b/piknik/render/plain.py @@ -1,5 +1,7 @@ # standard imports import logging +import tempfile +import os # external imports from mimeparse import parse_mime_type @@ -10,6 +12,19 @@ from .base import stream_accumulator logg = logging.getLogger(__name__) +def to_suffixed_file(d, s, data): + (v, ext) = os.path.splitext(s) + r = tempfile.mkstemp(suffix=ext, dir=d) + + f = os.fdopen(r[0], 'wb') + try: + f.write(data) + except TypeError: + f.write(data.encode('utf-8')) + f.close() + + return r[1] + class Renderer(BaseRenderer): @@ -51,37 +66,29 @@ id: {} def apply_message(self, state, issue, tags, envelope, message, message_id, message_date, accumulator=None): s = '\nmessage {} from {} {} - {}\n\n'.format( - message_date, - envelope.sender, - envelope.valid, - message_id, - ) - self.add(s, accumulator=accumulator) - + message_date, + envelope.sender, + envelope.valid, + message_id, + ) + return s - def apply_message_part(self, state, issue, envelope, message, message_id, message_date, accumulator=None): - m = parse_mime_type(message.get_content_type()) - filename = message.get_filename() - v = '' - if filename == None: - if m[0] == 'text': - if m[1] == 'plain': - v = message.get_payload() - if message.get('Content-Transfer-Encoding') == 'BASE64': - v = b64decode(v).decode() - else: - v = '[rich text]' - else: + def apply_message_part(self, state, issue, tags, envelope, message, message_date, message_content, accumulator=None): + if message_content['filename'] != None: if self.dump_dir != None: - v = message.get_payload() - if message.get('Content-Transfer-Encoding') == 'BASE64': - v = b64decode(v).decode() - filename = to_suffixed_file(self.dump_dir, filename, v) - sz = message.get('Content-Length') - if sz == None: + filename = to_suffixed_file(self.dump_dir, message_content['filename'], message_content['contents']) + sz = message_content['size'] + if sz == -1: sz = 'unknown' - v = '[file: {}, type {}/{}, size: {}]'.format(filename, m[0], m[1], sz) + v = '[file: {}, type {}/{}, size: {}]'.format( + message_content['filename'], + message_content['type'][0], + message_content['type'][1], + sz, + ) + else: + v = message_content['contents'] s = '\n\t' + v + '\n' - self.add(s, accumulator=accumulator) + return s diff --git a/piknik/runnable/show.py b/piknik/runnable/show.py @@ -15,7 +15,7 @@ from piknik.store import FileStoreFactory from piknik.crypto import PGPSigner from piknik.render.plain import Renderer -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) logg = logging.getLogger() argp = argparse.ArgumentParser() @@ -47,12 +47,9 @@ def to_suffixed_file(d, s, data): return r[1] -# TODO can implement as email parser api instead? class PGPWrapper(PGPSigner): - #def __init__(self, renderer, state, issue, home_dir=None): def __init__(self, state, issue, home_dir=None): - #super(PGPWrapper, self).__init__(home_dir=home_dir) super(PGPWrapper, self).__init__(home_dir=home_dir, skip_verify=True) self.message_date = None self.messages = [] @@ -60,20 +57,10 @@ class PGPWrapper(PGPSigner): self.message_id = None self.sender = None self.valid = False - #self.renderer = renderer self.state = state self.issue = issue -# def render_message(self, envelope, messages, message_sender, message_date, message_id, dump_dir=None, w=sys.stdout): -# for message in messages: -# self.renderer.apply_message_part(self.state, self.issue, envelope, message, message_sender, message_date, message_id, self.valid, dump_dir=dump_dir, w=w) -# #valid = '[++]' -# #if not self.valid: -# # valid = '[!!]' -# self.renderer.apply_message_post(self.state, self.issue, envelope, message, self.sender, message_date, message_id, self.valid, w=w) - - def envelope_callback(self, envelope, envelope_type): envelope = super(PGPWrapper, self).envelope_callback(envelope, envelope_type) envelope.valid = False @@ -208,7 +195,7 @@ def main(): verifier = PGPWrapper(state, issue, home_dir=gpg_home) import piknik.render.plain - renderer = piknik.render.plain.Renderer(basket, envelope_callback=verifier.envelope_callback, message_callback=verifier.message_callback) + renderer = piknik.render.plain.Renderer(basket, wrapper=verifier) #envelope_callback=verifier.envelope_callback, message_callback=verifier.message_callback) renderer.apply_issue(state, issue, tags) diff --git a/piknik/wrap.py b/piknik/wrap.py @@ -4,28 +4,42 @@ import logging # external imports from mimeparse import parse_mime_type +# local imports +from piknik.msg import MessageEnvelope + logg = logging.getLogger(__name__) class Wrapper: - def __init__(self): - self.content_buffer = {} + def __init__(self, dump_dir=None): + self.content_buffer = [] + self.envelope_state = -1 # -1 not in envelope, 0 in outer envelope, 1 inner envelope, not (yet) valid, 2 envelope valid (with signature) + self.envelope = None + self.dump_dir = dump_dir + + + + def process_envelope(self, msg, env_header): + self.envelope = MessageEnvelope(msg) + self.envelope_state = 0 + + + def process_message(self, envelope, msg, message_id, message_data): + raise NotImplementedError() - def add(self, message_id, message): + def add(self, envelope, message_id, message): m = parse_mime_type(message.get_content_type()) filename = message.get_filename() - v = '' + v = None if filename == None: if m[0] == 'text': if m[1] == 'plain': v = message.get_payload() if message.get('Content-Transfer-Encoding') == 'BASE64': v = b64decode(v).decode() - else: - v = '[rich text]' else: if self.dump_dir != None: v = message.get_payload() @@ -39,17 +53,21 @@ class Wrapper: else: sz = int(sz) - self.content_buffer[message_id] = { + o = { + 'envelope': envelope, + 'id': message_id, 'type': m, 'filename': filename, 'size': sz, 'contents': v, } - logg.debug('buffered content {}'.format(self.content_buffer[message_id])) + logg.info('buffered content {}'.format(o)) + self.content_buffer.append(o) - def pop(self, message_id): - r = self.content_buffer[message_id] - del self.content_buffer[message_id] - return r + def pop(self): + try: + return self.content_buffer.pop() + except IndexError: + return None