1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - FetchMessages Action 4 5 @copyright: 2012, 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from MoinSupport import getMetadata, writeHeaders, parseDictEntry 10 from MoinMessage import Message, GPG 11 from MoinMessageSupport import MoinMessageAction, \ 12 get_signing_users, get_recipient_details, \ 13 MoinMessageRecipientError 14 from email.mime.text import MIMEText 15 from email.parser import Parser 16 from itertools import islice 17 18 Dependencies = ['pages'] 19 20 class FetchMessages(MoinMessageAction): 21 22 "A handler for requests accessing messages." 23 24 def handle_message_content(self, content): 25 26 "Handle the given message 'content'." 27 28 request = self.request 29 30 # NOTE: Could employ a more accurate content type. 31 32 if not content.get_content_type() == "text/plain": 33 writeHeaders(request, "text/plain", getMetadata(self.page), "415 Unsupported Media Type") 34 request.write("The content does not appear to be a request for messages.") 35 return 36 37 homedir = self.get_homedir() 38 if not homedir: 39 writeHeaders(request, "text/plain", getMetadata(self.page), "403 Forbidden") 40 request.write("This site is not configured for this request.") 41 return 42 43 gpg = GPG(homedir) 44 45 # Get keys for signing and encrypting. 46 # The signing key will be this wiki's signing key for the user 47 # requesting the messages. 48 # The encryption key will be the key associated with the user requesting 49 # the messages, found in the recipients mapping. 50 51 recipient = request.user.name 52 53 signing_users = get_signing_users(request) 54 signer = signing_users and signing_users.get(recipient) 55 56 # Get the recipient details. 57 58 try: 59 parameters = get_recipient_details(request, recipient, fetching=True) 60 except MoinMessageRecipientError, exc: 61 writeHeaders(request, "text/plain", getMetadata(self.page), "403 Forbidden") 62 request.write(exc.message) 63 return 64 65 # Obtain commands from the payload, returning a collection of messages. 66 67 commands = content.get_payload(decode=True) 68 69 # Build a container for the responses. 70 71 message = Message() 72 73 # Process each command, using RFC 1939 (POP3) as inspiration. 74 75 for command in commands.split("\n"): 76 command = command.strip() 77 78 # Get the command and arguments. 79 80 command_parts = command.split(None, 1) 81 cmd = command_parts[0] 82 83 # A request to count the messages is returned in a part. 84 85 if cmd == "STAT": 86 result = str(len(self.store)) 87 part = MIMEText(result, "plain") 88 part["Request-Type"] = "STAT" 89 part["Request-Status"] = "OK" 90 message.add_update(part) 91 92 # A request for specific messages returns each message in its own 93 # part. 94 95 elif cmd in ("RETR", "DELE"): 96 97 try: 98 # Either select all. 99 100 if len(command_parts) == 1: 101 count = None 102 103 # Or select a particular number. 104 105 else: 106 count = int(parameters[1]) 107 108 except ValueError: 109 part = MIMEText(command, "plain") 110 part["Request-Type"] = cmd 111 part["Request-Status"] = "ERR" 112 message.add_update(part) 113 114 else: 115 # A request for specific messages returns each message 116 # in its own part within a collection part. 117 118 if cmd == "RETR": 119 container = Message() 120 121 for message_text in islice(iter(self.store), count): 122 message_item = Parser().parsestr(message_text) 123 container.add_update(message_item) 124 125 # Convert the container to a proper multipart section. 126 127 message.add_update(container.get_payload()) 128 129 # A request to delete messages is performed immediately. 130 131 elif cmd == "DELE": 132 keys = self.store.keys()[:count] 133 keys.sort() 134 135 for key in keys: 136 del self.store[key] 137 138 part = MIMEText(result, "plain") 139 part["Request-Type"] = cmd 140 part["Request-Status"] = "OK" 141 message.add_update(part) 142 143 # Handle invalid commands. 144 145 elif cmd: 146 part = MIMEText(result, "plain") 147 part["Request-Type"] = cmd 148 part["Request-Status"] = "ERR" 149 message.add_update(part) 150 151 # Sign and encrypt the message. 152 153 message = message.get_payload() 154 155 if signer: 156 message = gpg.signMessage(message, signer) 157 158 message = gpg.encryptMessage(message, parameters["fingerprint"]) 159 160 # Write the response. 161 162 writeHeaders(request, "text/plain", getMetadata(self.page)) 163 request.write(message.as_string()) 164 165 # Action function. 166 167 def execute(pagename, request): 168 FetchMessages(pagename, request).do_action() # instead of render 169 170 # vim: tabstop=4 expandtab shiftwidth=4