# HG changeset patch # User Paul Boddie # Date 1389546742 -3600 # Node ID 6bd75f8d2ab932c4a9a6ddd79e247d80667816ee # Parent b293d1ec0936c8ad044b24747b9175fcbba50203 Updated the action to use the MessageInterface abstraction. Made the getfiles script use the command language. Introduced specific content types for command sequences and responses. Reversed the order of operations in the getfiles script to be compatible with the action (so that signing occurs before encryption). diff -r b293d1ec0936 -r 6bd75f8d2ab9 actions/FetchMessages.py --- a/actions/FetchMessages.py Sun Jan 12 18:10:13 2014 +0100 +++ b/actions/FetchMessages.py Sun Jan 12 18:12:22 2014 +0100 @@ -2,18 +2,15 @@ """ MoinMoin - FetchMessages Action - @copyright: 2012, 2013 by Paul Boddie + @copyright: 2012, 2013, 2014 by Paul Boddie @license: GNU GPL (v2 or later), see COPYING.txt for details. """ from MoinSupport import getMetadata, writeHeaders, parseDictEntry -from MoinMessage import Message, GPG +from MoinMessage import GPG, MessageInterface from MoinMessageSupport import MoinMessageAction, \ get_signing_users, get_recipient_details, \ MoinMessageRecipientError -from email.mime.text import MIMEText -from email.parser import Parser -from itertools import islice Dependencies = ['pages'] @@ -27,9 +24,9 @@ request = self.request - # NOTE: Could employ a more accurate content type. + # Employing an unstandardised content type. - if not content.get_content_type() == "text/plain": + if not content.get_content_type() == "text/x-moinmessage-fetch": writeHeaders(request, "text/plain", getMetadata(self.page), "415 Unsupported Media Type") request.write("The content does not appear to be a request for messages.") return @@ -62,91 +59,11 @@ request.write(exc.message) return - # Obtain commands from the payload, returning a collection of messages. + # Obtain commands from the payload, returning a collection of messages + # and command results. commands = content.get_payload(decode=True) - - # Build a container for the responses. - - message = Message() - - # Process each command, using RFC 1939 (POP3) as inspiration. - - for command in commands.split("\n"): - command = command.strip() - - # Get the command and arguments. - - command_parts = command.split(None, 1) - cmd = command_parts[0] - - # A request to count the messages is returned in a part. - - if cmd == "STAT": - result = str(len(self.store)) - part = MIMEText(result, "plain") - part["Request-Type"] = "STAT" - part["Request-Status"] = "OK" - message.add_update(part) - - # A request for specific messages returns each message in its own - # part. - - elif cmd in ("RETR", "DELE"): - - try: - # Either select all. - - if len(command_parts) == 1: - count = None - - # Or select a particular number. - - else: - count = int(parameters[1]) - - except ValueError: - part = MIMEText(command, "plain") - part["Request-Type"] = cmd - part["Request-Status"] = "ERR" - message.add_update(part) - - else: - # A request for specific messages returns each message - # in its own part within a collection part. - - if cmd == "RETR": - container = Message() - - for message_text in islice(iter(self.store), count): - message_item = Parser().parsestr(message_text) - container.add_update(message_item) - - # Convert the container to a proper multipart section. - - message.add_update(container.get_payload()) - - # A request to delete messages is performed immediately. - - elif cmd == "DELE": - keys = self.store.keys()[:count] - keys.sort() - - for key in keys: - del self.store[key] - - part = MIMEText(result, "plain") - part["Request-Type"] = cmd - part["Request-Status"] = "OK" - message.add_update(part) - - # Handle invalid commands. - - elif cmd: - part = MIMEText(result, "plain") - part["Request-Type"] = cmd - part["Request-Status"] = "ERR" - message.add_update(part) + message = MessageInterface(self.store).execute(commands) # Sign and encrypt the message. @@ -159,7 +76,7 @@ # Write the response. - writeHeaders(request, "text/plain", getMetadata(self.page)) + writeHeaders(request, "text/x-moinmessage-fetch-response", getMetadata(self.page)) request.write(message.as_string()) # Action function. diff -r b293d1ec0936 -r 6bd75f8d2ab9 scripts/getfiles.py --- a/scripts/getfiles.py Sun Jan 12 18:10:13 2014 +0100 +++ b/scripts/getfiles.py Sun Jan 12 18:12:22 2014 +0100 @@ -11,6 +11,7 @@ from email.parser import Parser from os.path import join, exists from os import makedirs +from time import strftime import sys def writefile(filename, s): @@ -47,19 +48,20 @@ message = Message() - part = MIMEText("\n".join([sender, datetime]), "moinmessage-request") + commands = ["MBOX %s" % sender, "LAST %s" % datetime, "RETR"] + part = MIMEText("\n".join(commands), "x-moinmessage-fetch") message.add_update(part) # Get the e-mail message itself. email_message = message.get_payload() - # Encrypt, sign and send the request. + # Sign, encrypt and send the request. gpg = GPG() - encrypted_message = gpg.encryptMessage(email_message, service) - signed_message = gpg.signMessage(encrypted_message, signer) - resp = sendMessageOpener(signed_message, url) + signed_message = gpg.signMessage(email_message, signer) + encrypted_message = gpg.encryptMessage(signed_message, service) + resp = sendMessageOpener(encrypted_message, url) # Verify the response after possible transport encryption. @@ -80,12 +82,19 @@ message.handle_message(content) for part in message.updates: + if part.get_content_type() == "text/x-moinmessage-fetch-result": + if part["Request-Status"] != "OK": + print part + else: + print part["Request-Type"] + continue # Use the "outer" filename to determine a directory for the retrieved # file, even though the eventual filename in the directory may be # different. - directory = part["Content-Disposition"] + timestamp = strftime("%Y-%m-%d_%H:%M:%S") + directory = part.get("Content-Disposition", timestamp) # The retrieved content may be encrypted. diff -r b293d1ec0936 -r 6bd75f8d2ab9 tests/test_fetch.py --- a/tests/test_fetch.py Sun Jan 12 18:10:13 2014 +0100 +++ b/tests/test_fetch.py Sun Jan 12 18:12:22 2014 +0100 @@ -40,7 +40,7 @@ message = Message() parts = [] - part = MIMEText("\n".join(args), "plain", sys.stdin.encoding) + part = MIMEText("\n".join(args), "x-moinmessage-fetch", sys.stdin.encoding) message.add_update(part) # Sign and encrypt the request.