commit 30a4a685c1d034cca0c905c174c5d6f7838dd842
parent b49ab2ceac92fe74c6063ee330c582a45599e226
Author: lash <dev@holbrook.no>
Date: Fri, 2 Dec 2022 09:10:58 +0000
Introduce wrapper parent class to hold message content buffer
Diffstat:
7 files changed, 197 insertions(+), 106 deletions(-)
diff --git a/piknik/crypto.py b/piknik/crypto.py
@@ -10,14 +10,16 @@ import gnupg
# local imports
from piknik.error import VerifyError
from piknik.msg import MessageEnvelope
+from piknik.wrap import Wrapper
-logg = logging.getLogger()
+logg = logging.getLogger(__name__)
logging.getLogger('gnupg').setLevel(logging.ERROR)
-class PGPSigner:
+class PGPSigner(Wrapper):
def __init__(self, home_dir=None, default_key=None, passphrase=None, use_agent=False, skip_verify=False):
+ super(PGPSigner, self).__init__()
self.gpg = gnupg.GPG(gnupghome=home_dir)
self.default_key = default_key
self.passphrase = passphrase
@@ -69,16 +71,18 @@ class PGPSigner:
return MessageEnvelope(msg)
- def message_callback(self, envelope, msg, message_id):
+ def message_callback(self, envelope, msg, message_id, message_date):
if msg.get('From') != None:
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 msg.get('Content-Type') != 'application/pgp-signature':
+ self.add(message_id, msg)
return (envelope, msg,)
v = self.__envelope.as_string()
diff --git a/piknik/msg.py b/piknik/msg.py
@@ -4,12 +4,11 @@ import uuid
import mimetypes
from base64 import b64encode
import time
-
-#from email.message import EmailMessage as Message
from email.message import Message
from email import message_from_string
from email.policy import Compat32
from email.utils import formatdate
+from email.utils import parsedate_to_datetime
logg = logging.getLogger(__name__)
@@ -45,6 +44,7 @@ class IssueMessage:
def __unwrap(self, msg, envelope_callback=rubber_stamp_envelope, message_callback=None, post_callback=None):
message_ids = []
message_id = None
+ message_date = None
envelope = None
initial = False
@@ -66,8 +66,10 @@ class IssueMessage:
if new_message_id != None:
message_id = new_message_id
message_ids.append(message_id)
+ d = m.get('Date')
+ message_date = parsedate_to_datetime(d)
else:
- message_callback(envelope, m, message_id)
+ message_callback(envelope, 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
@@ -1,21 +1,33 @@
-#import sys
+# standard imports
import logging
+import sys
+
+# local imports
+from piknik.msg import MessageEnvelope
logg = logging.getLogger(__name__)
+def stream_accumulator(v, w=sys.stdout):
+ w.write(v)
+
+
class Renderer:
- def __init__(self, basket, accumulator=None):
+ def __init__(self, basket, accumulator=None, envelope_callback=None, message_callback=None):
self.b = basket
#self.e = None
self.a = accumulator
+ self.message_callback = message_callback
+ self.envelope_callback = envelope_callback
- def __add(self, v):
- if self.a != None:
+ def add(self, v, accumulator=None):
+ if accumulator == None:
+ accumulator = self.a
+ if accumulator != None:
if v != None:
- self.a(v)
+ accumulator(v)
def apply_envelope_pre(self, state, issue, tags, envelope, accumulator=None):
@@ -30,15 +42,15 @@ class Renderer:
pass
- def apply_message_pre(self, state, issue, tags, envelope, message, message_id, accumulator=None):
+ def apply_message_pre(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, accumulator=None):
+ 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, accumulator=None):
+ def apply_message(self, state, issue, tags, envelope, message, message_id, message_date, accumulator=None):
pass
@@ -53,22 +65,31 @@ 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)
+ else:
+ envelope = MessageEnvelope(envelope)
r = self.apply_envelope_pre(state, issue, tags, envelope, accumulator=accumulator)
- self.__add(r)
+ self.add(r)
r = self.apply_envelope(state, issue, tags, envelope, accumulator=accumulator)
- self.__add(r)
+ self.add(r)
r = self.apply_envelope_post(state, issue, tags, envelope, accumulator=accumulator)
- self.__add(r)
-
- def message_callback(envelope, message, message_id):
- r = self.apply_message_pre(state, issue, tags, envelope, message, message_id, accumulator=accumulator)
- self.__add(r)
- r = self.apply_message(state, issue, tags, envelope, message, message_id, accumulator=accumulator)
- self.__add(r)
- r = self.apply_message_post(state, issue, tags, envelope, message, message_id, accumulator=accumulator)
- self.__add(r)
+ 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)
+ 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)
@@ -85,11 +106,11 @@ class Renderer:
issue = self.b.get(issue_id)
tags = self.b.tags(issue_id=issue_id)
r = self.apply_issue_pre(state, issue, tags)
- self.__add(r)
+ self.add(r)
r = self.apply_issue(state, issue, tags)
- self.__add(r)
+ self.add(r)
r = self.apply_issue_post(state, issue, tags)
- self.__add(r)
+ self.add(r)
def apply_begin(self, accumulator=None):
@@ -102,15 +123,15 @@ class Renderer:
def apply(self, accumulator=None):
r = self.apply_begin()
- self.__add(r)
+ self.add(r)
for state in self.b.states():
r = self.apply_state_pre(state)
- self.__add(r)
+ self.add(r)
r = self.apply_state(state)
- self.__add(r)
+ self.add(r)
r = self.apply_state_post(state)
- self.__add(r)
+ self.add(r)
r = self.apply_end()
- self.__add(r)
+ self.add(r)
diff --git a/piknik/render/ini.py b/piknik/render/ini.py
@@ -1,25 +1,28 @@
-import sys
-
+# local imports
from .base import Renderer as BaseRenderer
+from .base import stream_accumulator
class Renderer(BaseRenderer):
- def apply_state(self, state, w=sys.stdout):
- w.write('[' + state + ']\n')
-
-
- def apply_issue(self, state, issue, tags, w=sys.stdout):
- w.write('{}\t{}\t{}\n'.format(issue.title, ','.join(tags), issue.id))
+ def __init__(self, basket, accumulator=stream_accumulator):
+ super(Renderer, self).__init__(basket, accumulator=accumulator)
- def apply_state_post(self, state, w=sys.stdout):
- w.write('\n')
+ def apply_state(self, state, accumulator=None):
+ s = '[' + state + ']\n'
+ self.add(s, accumulator=accumulator)
+ super(Renderer, self).apply_state(state, accumulator=accumulator)
- def apply_message_part(self, state, issue, envelope, message, message_from, message_date, message_id, message_valid, dump_dir=None, w=sys.stdout):
- pass
+ def apply_issue(self, state, issue, tags, accumulator=None):
+ s = '{}\t{}\t{}\n'.format(
+ issue.title,
+ ','.join(tags),
+ issue.id,
+ )
+ self.add(s, accumulator=accumulator)
- def apply_message_post(self, state, issue, tags, message, message_from, message_date, message_id, message_valid, w=sys.stdout):
- pass
+ def apply_state_post(self, state, accumulator=None):
+ self.add('\n', accumulator=accumulator)
diff --git a/piknik/render/plain.py b/piknik/render/plain.py
@@ -1,22 +1,25 @@
# standard imports
-import sys
+import logging
# external imports
from mimeparse import parse_mime_type
# local imports
from .base import Renderer as BaseRenderer
+from .base import stream_accumulator
+
+logg = logging.getLogger(__name__)
class Renderer(BaseRenderer):
- def __init__(self):
- super(Renderer, self).__init__()
- self.msg_buf = ''
+ def __init__(self, basket, dump_dir=None, accumulator=stream_accumulator, **kwargs):
+ super(Renderer, self).__init__(basket, accumulator=accumulator, **kwargs)
+ self.dump_dir = dump_dir
- def apply_issue(self, state, issue, tags, w=sys.stdout):
- w.write("""title: {}
+ def apply_issue(self, state, issue, tags, accumulator=None):
+ s = """title: {}
tags: {}
id: {}
@@ -25,40 +28,38 @@ id: {}
issue.title,
', '.join(tags),
)
- )
+ self.add(s, accumulator=accumulator)
assigned = issue.get_assigned()
if len(assigned) == 0:
- w.write('(not assigned)\n')
- return
-
- w.write('assigned to:\n')
- owner = issue.owner()
- for v in assigned:
- o = v[0]
- s = o.id()
- if o == owner:
- s += ' (owner)'
- w.write('\t' + str(s))
-
-
- def apply_message_post(self, state, issue, tags, message, message_from, message_date, message_id, message_valid, w=sys.stdout):
- r = self.msg_buf
- self.msg_buf = ''
- w.write('\nmessage {} from {} {} - {}\n\t{}\n'.format(message_date, message_from, message_valid, message_id, r))
- #return r
-
+ self.add('(not assigned)\n', accumulator=accumulator)
- #def apply_message(self, state, issue, tags, message, dump_dir=None, w=sys.stdout):
- # return
-
-
- #ww.seek(0)
- #self.msg_buf += '\n\t' + ww.read() + '\n'
-
-
- def apply_message_part(self, state, issue, envelope, message, message_from, message_date, message_id, message_valid, dump_dir=None, w=sys.stdout):
+ else:
+ self.add('assigned to:\n', accumulator=accumulator)
+ owner = issue.owner()
+ for v in assigned:
+ o = v[0]
+ s = o.id()
+ if o == owner:
+ s += ' (owner)'
+ s = '\t' + str(s) + '\n'
+ self.add(s, accumulator=accumulator)
+
+ super(Renderer, self).apply_issue(state, issue, tags, accumulator=accumulator)
+
+
+ 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)
+
+
+ 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()
@@ -72,14 +73,15 @@ id: {}
else:
v = '[rich text]'
else:
- if dump_dir != 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(dump_dir, filename, v)
+ filename = to_suffixed_file(self.dump_dir, filename, v)
sz = message.get('Content-Length')
if sz == None:
sz = 'unknown'
v = '[file: {}, type {}/{}, size: {}]'.format(filename, m[0], m[1], sz)
- w.write('\n\t' + v + '\n')
+ s = '\n\t' + v + '\n'
+ self.add(s, accumulator=accumulator)
diff --git a/piknik/runnable/show.py b/piknik/runnable/show.py
@@ -50,7 +50,8 @@ def to_suffixed_file(d, s, data):
# TODO can implement as email parser api instead?
class PGPWrapper(PGPSigner):
- def __init__(self, renderer, state, issue, home_dir=None):
+ #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
@@ -59,18 +60,18 @@ class PGPWrapper(PGPSigner):
self.message_id = None
self.sender = None
self.valid = False
- self.renderer = renderer
+ #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 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):
@@ -79,8 +80,8 @@ class PGPWrapper(PGPSigner):
return envelope
- def message_callback(self, envelope, message, message_id):
- (envelope, message) = super(PGPWrapper, self).message_callback(envelope, message, message_id)
+ def message_callback(self, envelope, message, message_id, message_date):
+ (envelope, message) = super(PGPWrapper, self).message_callback(envelope, message, message_id, message_date)
if envelope != None and not envelope.resolved:
logg.debug('have sender {}'.format(self.sender))
@@ -89,7 +90,7 @@ class PGPWrapper(PGPSigner):
self.resolved = True
if message_id == None:
- return
+ return (envelope, message,)
messages = []
if message.get('X-Piknik-Msg-Id') == None:
@@ -107,6 +108,8 @@ class PGPWrapper(PGPSigner):
self.message_date = parsedate_to_datetime(d)
self.message_id = message_id
+ return (envelope, message,)
+
def post_callback(self, messages_id):
dump_dir = None
@@ -194,19 +197,20 @@ def main():
#import piknik.render.html
#renderer = piknik.render.html.Renderer()
import piknik.render.ini
- renderer = piknik.render.ini.Renderer()
- return process_states(renderer, basket)
-
- import piknik.render.html
- renderer = piknik.render.html.Renderer()
- #import piknik.render.ini
- #renderer = piknik.render.ini.Renderer()
+ renderer = piknik.render.ini.Renderer(basket)
+ #return process_states(renderer, basket)
+ renderer.apply()
+ return
issue = basket.get(arg.issue_id)
tags = basket.tags(arg.issue_id)
+ state = basket.get_state(arg.issue_id)
+ verifier = PGPWrapper(state, issue, home_dir=gpg_home)
- #globals()['render_' + arg.renderer](basket, state, issue, tags)
- render(renderer, basket, issue, tags)
+ import piknik.render.plain
+ renderer = piknik.render.plain.Renderer(basket, envelope_callback=verifier.envelope_callback, message_callback=verifier.message_callback)
+
+ renderer.apply_issue(state, issue, tags)
if __name__ == '__main__':
diff --git a/piknik/wrap.py b/piknik/wrap.py
@@ -0,0 +1,55 @@
+# standard imports
+import logging
+
+# external imports
+from mimeparse import parse_mime_type
+
+logg = logging.getLogger(__name__)
+
+
+class Wrapper:
+
+ def __init__(self):
+ self.content_buffer = {}
+
+
+ def add(self, message_id, message):
+ 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:
+ 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:
+ sz = -1
+ else:
+ sz = int(sz)
+
+ self.content_buffer[message_id] = {
+ 'type': m,
+ 'filename': filename,
+ 'size': sz,
+ 'contents': v,
+ }
+
+ logg.debug('buffered content {}'.format(self.content_buffer[message_id]))
+
+
+ def pop(self, message_id):
+ r = self.content_buffer[message_id]
+ del self.content_buffer[message_id]
+ return r