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:
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