diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 683b1728b784778a36f341281f72c5b55594d434..8346b0590cb605ab7a0ac6d4dcba5f1557273f0a 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
and given "file" class nodes that are linked to the "msg" node.
. In a multipart/alternative message or part, we look for a text/plain
subpart and ignore the other parts.
+ . A message/rfc822 is treated similar tomultipart/mixed (except for
+ special handling of the first text part) if unpack_rfc822 is set in
+ the mailgw config section.
Summary
-------
import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
import time, random, sys, logging
-import traceback, MimeWriter, rfc822
+import traceback, rfc822
from email.Header import decode_header
from roundup import configuration, hyperdb, date, password, rfc2822, exceptions
from roundup.mailer import Mailer, MessageSendError
from roundup.i18n import _
+from roundup.hyperdb import iter_roles
try:
import pyme, pyme.core, pyme.gpgme
yield sig
sig = sig.next
-
-def iter_roles(roles):
- ''' handle the text processing of turning the roles list
- into something python can use more easily
- '''
- for role in [x.lower().strip() for x in roles.split(',')]:
- yield role
-
-def user_has_role(db, userid, role_list):
- ''' see if the given user has any roles that appear
- in the role_list
- '''
- for role in iter_roles(db.user.get(userid, 'roles')):
- if role in iter_roles(role_list):
- return True
- return False
-
-
def check_pgp_sigs(sig, gpgctx, author):
''' Theoretically a PGP message can have several signatures. GPGME
returns status on all signatures in a linked list. Walk that
def getheader(self, name, default=None):
hdr = mimetools.Message.getheader(self, name, default)
+ # TODO are there any other False values possible?
+ # TODO if not hdr: return hdr
+ if hdr is None:
+ return None
if not hdr:
return ''
if hdr:
def getname(self):
"""Find an appropriate name for this message."""
+ name = None
if self.gettype() == 'message/rfc822':
# handle message/rfc822 specially - the name should be
# the subject of the actual e-mail embedded here
+ # we add a '.eml' extension like other email software does it
self.fp.seek(0)
- name = Message(self.fp).getheader('subject')
- else:
+ s = cStringIO.StringIO(self.getbody())
+ name = Message(s).getheader('subject')
+ if name:
+ name = name + '.eml'
+ if not name:
# try name on Content-Type
name = self.getparam('name')
if not name:
# flagging.
# multipart/form-data:
# For web forms only.
+ # message/rfc822:
+ # Only if configured in [mailgw] unpack_rfc822
- def extract_content(self, parent_type=None, ignore_alternatives = False):
+ def extract_content(self, parent_type=None, ignore_alternatives=False,
+ unpack_rfc822=False):
"""Extract the body and the attachments recursively.
If the content is hidden inside a multipart/alternative part,
ig = ignore_alternatives and not content_found
for part in self.getparts():
new_content, new_attach = part.extract_content(content_type,
- not content and ig)
+ not content and ig, unpack_rfc822)
# If we haven't found a text/plain part yet, take this one,
# otherwise make it an attachment.
attachments.extend(new_attach)
if ig and content_type == 'multipart/alternative' and content:
attachments = []
+ elif unpack_rfc822 and content_type == 'message/rfc822':
+ s = cStringIO.StringIO(self.getbody())
+ m = Message(s)
+ ig = ignore_alternatives and not content
+ new_content, attachments = m.extract_content(m.gettype(), ig,
+ unpack_rfc822)
+ attachments.insert(0, m.text_as_attachment())
elif (parent_type == 'multipart/signed' and
content_type == 'application/pgp-signature'):
# ignore it so it won't be saved as an attachment
self.default_class = value.strip()
self.mailer = Mailer(instance.config)
- self.logger = logging.getLogger('mailgw')
+ self.logger = logging.getLogger('roundup.mailgw')
# should we trap exceptions (normal usage) or pass them through
# (for testing)
fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
return 0
- def do_imap(self, server, user='', password='', mailbox='', ssl=0):
+ def do_imap(self, server, user='', password='', mailbox='', ssl=0,
+ cram=0):
''' Do an IMAP connection
'''
import getpass, imaplib, socket
return 1
try:
- server.login(user, password)
+ if cram:
+ server.login_cram_md5(user, password)
+ else:
+ server.login(user, password)
except imaplib.IMAP4.error, e:
self.logger.exception('IMAP login failure')
return 1
# if we've not found a valid classname prefix then force the
# scanning to handle there being a leading delimiter
- title_re = r'(?P<title>%s[^%s]+)'%(
+ title_re = r'(?P<title>%s[^%s]*)'%(
not matches['classname'] and '.' or '', delim_open)
m = re.match(title_re, tmpsubject.strip(), re.IGNORECASE)
if m:
# Don't create users if anonymous isn't allowed to register
create = 1
anonid = self.db.user.lookup('anonymous')
- if not (self.db.security.hasPermission('Create', anonid, 'user')
+ if not (self.db.security.hasPermission('Register', anonid, 'user')
and self.db.security.hasPermission('Email Access', anonid)):
create = 0
from_address = from_list[0][1]
registration_info = ""
if self.db.security.hasPermission('Web Access', author) and \
- self.db.security.hasPermission('Create', anonid, 'user'):
+ self.db.security.hasPermission('Register', anonid, 'user'):
tracker_web = self.instance.config.TRACKER_WEB
registration_info = """ Please register at:
if (title and properties.has_key('title') and not
issue_props.has_key('title')):
issue_props['title'] = title
+ if (nodeid and properties.has_key('title') and not
+ config['MAILGW_SUBJECT_UPDATES_TITLE']):
+ issue_props['title'] = cl.get(nodeid,'title')
#
# handle message-id and in-reply-to
# or we will skip PGP processing
def pgp_role():
if self.instance.config.PGP_ROLES:
- return user_has_role(self.db, author,
- self.instance.config.PGP_ROLES)
+ return self.db.user.has_role(author,
+ iter_roles(self.instance.config.PGP_ROLES))
else:
return True
encrypted.""")
# now handle the body - find the message
ig = self.instance.config.MAILGW_IGNORE_ALTERNATIVES
- content, attachments = message.extract_content(ignore_alternatives = ig)
+ content, attachments = message.extract_content(ignore_alternatives=ig,
+ unpack_rfc822=self.instance.config.MAILGW_UNPACK_RFC822)
if content is None:
raise MailUsageError, _("""
Roundup requires the submission to be plain text. The message parser could
#
# handle the attachments
#
- if properties.has_key('files'):
- files = []
+ files = []
+ if attachments and properties.has_key('files'):
for (name, mime_type, data) in attachments:
if not self.db.security.hasPermission('Create', author, 'file'):
raise Unauthorized, _(
pass
else:
files.append(fileid)
- # attach the files to the issue
- if not self.db.security.hasPermission('Edit', author,
+ # allowed to attach the files to an existing node?
+ if nodeid and not self.db.security.hasPermission('Edit', author,
classname, 'files'):
raise Unauthorized, _(
'You are not permitted to add files to %(classname)s.'
Mail message was rejected by a detector.
%(error)s
""") % locals()
- # attach the message to the node
- if not self.db.security.hasPermission('Edit', author,
+ # allowed to attach the message to the existing node?
+ if nodeid and not self.db.security.hasPermission('Edit', author,
classname, 'messages'):
raise Unauthorized, _(
'You are not permitted to add messages to %(classname)s.'
if not props.has_key(prop) :
props[prop] = issue_props[prop]
- # Check permissions for each property
- for prop in props.keys():
- if not self.db.security.hasPermission('Edit', author,
- classname, prop):
- raise Unauthorized, _('You are not permitted to edit '
- 'property %(prop)s of class %(classname)s.') % locals()
-
if nodeid:
+ # Check permissions for each property
+ for prop in props.keys():
+ if not self.db.security.hasPermission('Edit', author,
+ classname, prop):
+ raise Unauthorized, _('You are not permitted to edit '
+ 'property %(prop)s of class %(classname)s.') % locals()
cl.set(nodeid, **props)
else:
+ # Check permissions for each property
+ for prop in props.keys():
+ if not self.db.security.hasPermission('Create', author,
+ classname, prop):
+ raise Unauthorized, _('You are not permitted to set '
+ 'property %(prop)s of class %(classname)s.') % locals()
nodeid = cl.create(**props)
except (TypeError, IndexError, ValueError, exceptions.Reject), message:
raise MailUsageError, _("""