1.1 --- a/actions/FetchMessages.py Sun Jan 12 18:10:13 2014 +0100
1.2 +++ b/actions/FetchMessages.py Sun Jan 12 18:12:22 2014 +0100
1.3 @@ -2,18 +2,15 @@
1.4 """
1.5 MoinMoin - FetchMessages Action
1.6
1.7 - @copyright: 2012, 2013 by Paul Boddie <paul@boddie.org.uk>
1.8 + @copyright: 2012, 2013, 2014 by Paul Boddie <paul@boddie.org.uk>
1.9 @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.10 """
1.11
1.12 from MoinSupport import getMetadata, writeHeaders, parseDictEntry
1.13 -from MoinMessage import Message, GPG
1.14 +from MoinMessage import GPG, MessageInterface
1.15 from MoinMessageSupport import MoinMessageAction, \
1.16 get_signing_users, get_recipient_details, \
1.17 MoinMessageRecipientError
1.18 -from email.mime.text import MIMEText
1.19 -from email.parser import Parser
1.20 -from itertools import islice
1.21
1.22 Dependencies = ['pages']
1.23
1.24 @@ -27,9 +24,9 @@
1.25
1.26 request = self.request
1.27
1.28 - # NOTE: Could employ a more accurate content type.
1.29 + # Employing an unstandardised content type.
1.30
1.31 - if not content.get_content_type() == "text/plain":
1.32 + if not content.get_content_type() == "text/x-moinmessage-fetch":
1.33 writeHeaders(request, "text/plain", getMetadata(self.page), "415 Unsupported Media Type")
1.34 request.write("The content does not appear to be a request for messages.")
1.35 return
1.36 @@ -62,91 +59,11 @@
1.37 request.write(exc.message)
1.38 return
1.39
1.40 - # Obtain commands from the payload, returning a collection of messages.
1.41 + # Obtain commands from the payload, returning a collection of messages
1.42 + # and command results.
1.43
1.44 commands = content.get_payload(decode=True)
1.45 -
1.46 - # Build a container for the responses.
1.47 -
1.48 - message = Message()
1.49 -
1.50 - # Process each command, using RFC 1939 (POP3) as inspiration.
1.51 -
1.52 - for command in commands.split("\n"):
1.53 - command = command.strip()
1.54 -
1.55 - # Get the command and arguments.
1.56 -
1.57 - command_parts = command.split(None, 1)
1.58 - cmd = command_parts[0]
1.59 -
1.60 - # A request to count the messages is returned in a part.
1.61 -
1.62 - if cmd == "STAT":
1.63 - result = str(len(self.store))
1.64 - part = MIMEText(result, "plain")
1.65 - part["Request-Type"] = "STAT"
1.66 - part["Request-Status"] = "OK"
1.67 - message.add_update(part)
1.68 -
1.69 - # A request for specific messages returns each message in its own
1.70 - # part.
1.71 -
1.72 - elif cmd in ("RETR", "DELE"):
1.73 -
1.74 - try:
1.75 - # Either select all.
1.76 -
1.77 - if len(command_parts) == 1:
1.78 - count = None
1.79 -
1.80 - # Or select a particular number.
1.81 -
1.82 - else:
1.83 - count = int(parameters[1])
1.84 -
1.85 - except ValueError:
1.86 - part = MIMEText(command, "plain")
1.87 - part["Request-Type"] = cmd
1.88 - part["Request-Status"] = "ERR"
1.89 - message.add_update(part)
1.90 -
1.91 - else:
1.92 - # A request for specific messages returns each message
1.93 - # in its own part within a collection part.
1.94 -
1.95 - if cmd == "RETR":
1.96 - container = Message()
1.97 -
1.98 - for message_text in islice(iter(self.store), count):
1.99 - message_item = Parser().parsestr(message_text)
1.100 - container.add_update(message_item)
1.101 -
1.102 - # Convert the container to a proper multipart section.
1.103 -
1.104 - message.add_update(container.get_payload())
1.105 -
1.106 - # A request to delete messages is performed immediately.
1.107 -
1.108 - elif cmd == "DELE":
1.109 - keys = self.store.keys()[:count]
1.110 - keys.sort()
1.111 -
1.112 - for key in keys:
1.113 - del self.store[key]
1.114 -
1.115 - part = MIMEText(result, "plain")
1.116 - part["Request-Type"] = cmd
1.117 - part["Request-Status"] = "OK"
1.118 - message.add_update(part)
1.119 -
1.120 - # Handle invalid commands.
1.121 -
1.122 - elif cmd:
1.123 - part = MIMEText(result, "plain")
1.124 - part["Request-Type"] = cmd
1.125 - part["Request-Status"] = "ERR"
1.126 - message.add_update(part)
1.127 + message = MessageInterface(self.store).execute(commands)
1.128
1.129 # Sign and encrypt the message.
1.130
1.131 @@ -159,7 +76,7 @@
1.132
1.133 # Write the response.
1.134
1.135 - writeHeaders(request, "text/plain", getMetadata(self.page))
1.136 + writeHeaders(request, "text/x-moinmessage-fetch-response", getMetadata(self.page))
1.137 request.write(message.as_string())
1.138
1.139 # Action function.
2.1 --- a/scripts/getfiles.py Sun Jan 12 18:10:13 2014 +0100
2.2 +++ b/scripts/getfiles.py Sun Jan 12 18:12:22 2014 +0100
2.3 @@ -11,6 +11,7 @@
2.4 from email.parser import Parser
2.5 from os.path import join, exists
2.6 from os import makedirs
2.7 +from time import strftime
2.8 import sys
2.9
2.10 def writefile(filename, s):
2.11 @@ -47,19 +48,20 @@
2.12
2.13 message = Message()
2.14
2.15 - part = MIMEText("\n".join([sender, datetime]), "moinmessage-request")
2.16 + commands = ["MBOX %s" % sender, "LAST %s" % datetime, "RETR"]
2.17 + part = MIMEText("\n".join(commands), "x-moinmessage-fetch")
2.18 message.add_update(part)
2.19
2.20 # Get the e-mail message itself.
2.21
2.22 email_message = message.get_payload()
2.23
2.24 - # Encrypt, sign and send the request.
2.25 + # Sign, encrypt and send the request.
2.26
2.27 gpg = GPG()
2.28 - encrypted_message = gpg.encryptMessage(email_message, service)
2.29 - signed_message = gpg.signMessage(encrypted_message, signer)
2.30 - resp = sendMessageOpener(signed_message, url)
2.31 + signed_message = gpg.signMessage(email_message, signer)
2.32 + encrypted_message = gpg.encryptMessage(signed_message, service)
2.33 + resp = sendMessageOpener(encrypted_message, url)
2.34
2.35 # Verify the response after possible transport encryption.
2.36
2.37 @@ -80,12 +82,19 @@
2.38 message.handle_message(content)
2.39
2.40 for part in message.updates:
2.41 + if part.get_content_type() == "text/x-moinmessage-fetch-result":
2.42 + if part["Request-Status"] != "OK":
2.43 + print part
2.44 + else:
2.45 + print part["Request-Type"]
2.46 + continue
2.47
2.48 # Use the "outer" filename to determine a directory for the retrieved
2.49 # file, even though the eventual filename in the directory may be
2.50 # different.
2.51
2.52 - directory = part["Content-Disposition"]
2.53 + timestamp = strftime("%Y-%m-%d_%H:%M:%S")
2.54 + directory = part.get("Content-Disposition", timestamp)
2.55
2.56 # The retrieved content may be encrypted.
2.57
3.1 --- a/tests/test_fetch.py Sun Jan 12 18:10:13 2014 +0100
3.2 +++ b/tests/test_fetch.py Sun Jan 12 18:12:22 2014 +0100
3.3 @@ -40,7 +40,7 @@
3.4 message = Message()
3.5 parts = []
3.6
3.7 - part = MIMEText("\n".join(args), "plain", sys.stdin.encoding)
3.8 + part = MIMEText("\n".join(args), "x-moinmessage-fetch", sys.stdin.encoding)
3.9 message.add_update(part)
3.10
3.11 # Sign and encrypt the request.