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 10 from MoinMessage import Message 11 from MoinMessageSupport import MoinMessageAction 12 from email.mime.text import MIMEText 13 from email.parser import Parser 14 from itertools import islice 15 16 try: 17 from cStringIO import StringIO 18 except ImportError: 19 from StringIO import StringIO 20 21 Dependencies = ['pages'] 22 23 class FetchMessages(MoinMessageAction): 24 25 "A handler for requests accessing messages." 26 27 def handle_message_content(self, content): 28 29 "Handle the given message 'content'." 30 31 request = self.request 32 33 # NOTE: Could employ a more accurate content type. 34 35 if not content.get_content_type() == "text/plain": 36 writeHeaders(request, "text/plain", getMetadata(self.page), "403 Forbidden") 37 request.write("The content does not appear to be a request for messages.") 38 return 39 40 # Obtain commands from the payload, returning a collection of messages. 41 42 commands = content.get_payload(decode=True) 43 44 # Build a container for the responses. 45 46 message = Message() 47 48 # Process each command, using RFC 1939 (POP3) as inspiration. 49 50 for command in commands.split("\n"): 51 command = command.strip() 52 53 # Get the command and arguments. 54 55 command_parts = command.split(None, 1) 56 cmd = command_parts[0] 57 58 # A request to count the messages is returned in a part. 59 60 if cmd == "STAT": 61 result = str(len(self.store)) 62 part = MIMEText(result, "plain") 63 part["Request-Type"] = "STAT" 64 part["Request-Status"] = "OK" 65 message.add_update(part) 66 67 # A request for specific messages returns each message in its own 68 # part. 69 70 elif cmd in ("RETR", "DELE"): 71 72 try: 73 # Either select all. 74 75 if len(command_parts) == 1: 76 count = None 77 78 # Or select a particular number. 79 80 else: 81 count = int(parameters[1]) 82 83 except ValueError: 84 part = MIMEText(command, "plain") 85 part["Request-Type"] = cmd 86 part["Request-Status"] = "ERR" 87 message.add_update(part) 88 89 else: 90 # A request for specific messages returns each message 91 # in its own part within a collection part. 92 93 if cmd == "RETR": 94 container = Message() 95 96 for message_text in islice(iter(self.store), count): 97 message_item = Parser().parse(StringIO(message_text)) 98 container.add_update(message_item) 99 100 # Convert the container to a proper multipart section. 101 102 message.add_update(container.get_payload()) 103 104 # A request to delete messages is performed immediately. 105 106 elif cmd == "DELE": 107 keys = self.store.keys()[:count] 108 keys.sort() 109 110 for key in keys: 111 del self.store[key] 112 113 part = MIMEText(result, "plain") 114 part["Request-Type"] = cmd 115 part["Request-Status"] = "OK" 116 message.add_update(part) 117 118 # Handle invalid commands. 119 120 elif cmd: 121 part = MIMEText(result, "plain") 122 part["Request-Type"] = cmd 123 part["Request-Status"] = "ERR" 124 message.add_update(part) 125 126 # Write the response. 127 128 request.write(message.get_payload().as_string()) 129 return 1, None 130 131 # Action function. 132 133 def execute(pagename, request): 134 FetchMessages(pagename, request).do_action() # instead of render 135 136 # vim: tabstop=4 expandtab shiftwidth=4