1.1 --- a/MoinMessage.py Sun Mar 10 01:19:48 2013 +0100
1.2 +++ b/MoinMessage.py Fri Apr 19 15:15:40 2013 +0200
1.3 @@ -11,6 +11,7 @@
1.4 from email.mime.multipart import MIMEMultipart
1.5 from email.mime.application import MIMEApplication
1.6 from email.mime.base import MIMEBase
1.7 +from email.utils import formatdate, parsedate
1.8 from subprocess import Popen, PIPE
1.9 from tempfile import mkstemp
1.10 from urlparse import urlsplit
1.11 @@ -25,10 +26,20 @@
1.12 "An update message."
1.13
1.14 def __init__(self, text=None):
1.15 + self.date = None
1.16 self.updates = []
1.17 if text:
1.18 self.parse_text(text)
1.19
1.20 + def init_date(self, message):
1.21 +
1.22 + "Obtain the date of the given 'message'."
1.23 +
1.24 + if message.has_key("Date"):
1.25 + self.date = parsedate(message["Date"])
1.26 + else:
1.27 + self.date = None
1.28 +
1.29 def parse_text(self, text):
1.30
1.31 "Parse the given 'text' as a message."
1.32 @@ -39,6 +50,8 @@
1.33
1.34 "Handle the given 'message', recording the separate updates."
1.35
1.36 + self.init_date(message)
1.37 +
1.38 # The message either consists of a collection of updates.
1.39
1.40 if message.is_multipart() and is_collection(message):
1.41 @@ -79,9 +92,15 @@
1.42 part.attach(alternative)
1.43 return part
1.44
1.45 - def get_payload(self):
1.46 + def get_payload(self, timestamped=True):
1.47
1.48 - "Get the multipart payload for the message."
1.49 + """
1.50 + Get the multipart payload for the message. If the 'timestamped'
1.51 + parameter is omitted or set to a true value, the payload will be given a
1.52 + date header set to the current date and time that can be used to assess
1.53 + the validity of a message and to determine whether it has already been
1.54 + received by a recipient.
1.55 + """
1.56
1.57 if len(self.updates) == 1:
1.58 message = self.updates[0]
1.59 @@ -91,6 +110,10 @@
1.60 for update in self.updates:
1.61 message.attach(update)
1.62
1.63 + if timestamped:
1.64 + timestamp(message)
1.65 + self.init_date(message)
1.66 +
1.67 return message
1.68
1.69 class Mailbox:
1.70 @@ -289,6 +312,20 @@
1.71
1.72 # Communications functions.
1.73
1.74 +def timestamp(message):
1.75 +
1.76 + """
1.77 + Timestamp the given 'message' so that its validity can be assessed by the
1.78 + recipient.
1.79 + """
1.80 +
1.81 + datestr = formatdate()
1.82 +
1.83 + if not message.has_key("Date"):
1.84 + message.add_header("Date", datestr)
1.85 + else:
1.86 + message["Date"] = datestr
1.87 +
1.88 def sendMessage(message, url):
1.89
1.90 "Send 'message' to the given 'url."
2.1 --- a/README.txt Sun Mar 10 01:19:48 2013 +0100
2.2 +++ b/README.txt Fri Apr 19 15:15:40 2013 +0200
2.3 @@ -73,6 +73,10 @@
2.4 moinmessage_gpg_recipients_page (optional, default is MoinMessageRecipientsDict)
2.5 This provides a mapping from recipients to remote URLs and key fingerprints.
2.6
2.7 + moinmessage_reject_messages_without_dates (optional, default is True)
2.8 + This causes messages sent to a Wiki using the PostMessage action to be
2.9 + rejected if date information is missing.
2.10 +
2.11 Fingerprints and Keys
2.12 ---------------------
2.13
3.1 --- a/actions/PostMessage.py Sun Mar 10 01:19:48 2013 +0100
3.2 +++ b/actions/PostMessage.py Fri Apr 19 15:15:40 2013 +0200
3.3 @@ -10,9 +10,11 @@
3.4 from MoinMoin.PageEditor import PageEditor
3.5 from MoinMoin.log import getLogger
3.6 from MoinMoin.user import User
3.7 -from MoinSupport import ItemStore
3.8 +from MoinMoin import wikiutil
3.9 +from MoinSupport import ItemStore, getHeader, getMetadata, getWikiDict, writeHeaders
3.10 from MoinMessage import GPG, Message, MoinMessageError
3.11 from email.parser import Parser
3.12 +import time
3.13
3.14 try:
3.15 from cStringIO import StringIO
3.16 @@ -35,6 +37,7 @@
3.17 self.pagename = pagename
3.18 self.request = request
3.19 self.page = Page(request, pagename)
3.20 + self.store = ItemStore(self.page, "messages", "message-locks")
3.21
3.22 def do_action(self):
3.23 request = self.request
3.24 @@ -211,6 +214,29 @@
3.25 message = Message()
3.26 message.handle_message(content)
3.27
3.28 + # Test any date against the page or message store.
3.29 +
3.30 + if message.date:
3.31 + store_date = time.gmtime(self.store.mtime())
3.32 + page_date = time.gmtime(wikiutil.version2timestamp(self.page.mtime_usecs()))
3.33 + last_date = max(store_date, page_date)
3.34 +
3.35 + # Reject messages older than the page date.
3.36 +
3.37 + if message.date < last_date:
3.38 + writeHeaders(request, "text/plain", getMetadata(self.page), "403 Forbidden")
3.39 + request.write("The message is too old: %s versus %s." % (message.date, last_date))
3.40 + return
3.41 +
3.42 + # Reject messages without dates if so configured.
3.43 +
3.44 + elif getattr(request.cfg, "moinmessage_reject_messages_without_dates", True):
3.45 + writeHeaders(request, "text/plain", getMetadata(self.page), "403 Forbidden")
3.46 + request.write("The message has no date information.")
3.47 + return
3.48 +
3.49 + # Handle each update.
3.50 +
3.51 for update in message.updates:
3.52
3.53 # Handle a single part.
3.54 @@ -240,8 +266,7 @@
3.55 # Update a message store for the page.
3.56
3.57 if to_store(update):
3.58 - store = ItemStore(self.page, "messages", "message-locks")
3.59 - store.append(update.as_string())
3.60 + self.store.append(update.as_string())
3.61
3.62 # Update the page.
3.63
4.1 --- a/macros/ShowMessages.py Sun Mar 10 01:19:48 2013 +0100
4.2 +++ b/macros/ShowMessages.py Fri Apr 19 15:15:40 2013 +0200
4.3 @@ -44,7 +44,7 @@
4.4 # Show the messages.
4.5 # NOTE: Support additional access control.
4.6
4.7 - store = ItemStore(page, ("messages",), ("message-locks",))
4.8 + store = ItemStore(page, "messages", "message-locks")
4.9
4.10 output = []
4.11 output.append(fmt.div(on=1, css_class="showmessages-messages"))