"""
 A form action adapter that e-mails input.
"""
 
__author__ = 'Steve McMahon <steve@dcn.org>'
__docformat__ = 'plaintext'
 
 
########################
# The formMailerAdapter code and schema borrow heavily
# from PloneFormMailer <http://plone.org/products/ploneformmailer>
# by Jens Klein and Reinout van Rees.
#
# Author:       Jens Klein <jens.klein@jensquadrat.com>
#
# Copyright:    (c) 2004 by jens quadrat, Klein & Partner KEG, Austria
# Licence:      GNU General Public Licence (GPL) Version 2 or later
#######################
 
from AccessControl import ClassSecurityInfo
from AccessControl import Unauthorized
 
from Products.Archetypes.public import *
from Products.Archetypes.utils import OrderedDict
from Products.Archetypes.utils import shasattr
 
from Products.ATContentTypes.content.base import registerATCT
 
from Products.CMFCore.permissions import View, ModifyPortalContent
from Products.CMFCore.utils import getToolByName
 
from Products.PloneFormGen.config import *
from Products.PloneFormGen.content.actionAdapter import FormActionAdapter, FormAdapterSchema
 
from Products.TALESField import TALESString
from Products.TemplateFields import ZPTField as ZPTField
 
from ya_gpg import gpg
 
from email import Encoders
from email.Header import Header
from email.MIMEAudio import MIMEAudio
from email.MIMEBase import MIMEBase
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.utils import formataddr
 
from types import StringTypes
 
from Products.PloneFormGen import PloneFormGenMessageFactory as _
from Products.PloneFormGen import dollarReplace
 
from zope.contenttype import guess_content_type
 
import zope.i18n
 
 
formMailerAdapterSchema = FormAdapterSchema.copy() + Schema((
    StringField('recipient_name',
        searchable=0,
        required=0,
        default_method='getDefaultRecipientName',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=StringWidget(
            label=_(u'label_formmailer_recipient_fullname',
                      default=u"Recipient's full name"),
            description=_(u'help_formmailer_recipient_fullname', default=u"""
                The full name of the recipient of the mailed form.
                """),
            ),
        ),
    StringField('recipient_email',
        searchable=0,
        required=0,
        default_method='getDefaultRecipient',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        validators=('isEmail',),
        widget=StringWidget(
            label=_(u'label_formmailer_recipient_email',
                      default=u"Recipient's e-mail address"),
            description=_(u'help_formmailer_recipient_email',
                            default=u'The recipients e-mail address.'),
            ),
        ),
    StringField('to_field',
        schemata='addressing',
        searchable=0,
        required=0,
        default='#NONE#',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        vocabulary='fieldsDisplayList',
        widget=SelectionWidget(
            label=_(u'label_formmailer_to_extract', default=u'Extract Recipient From'),
            description=_(u'help_formmailer_to_extract',
                default=u"""
                Choose a form field from which you wish to extract
                input for the To header. If you choose anything other
                than "None", this will override the "Recipient's e-mail address"
                setting above. Be very cautious about allowing unguarded user
                input for this purpose.
                """),
            ),
        ),
    LinesField('cc_recipients',
        searchable=0,
        required=0,
        default_method='getDefaultCC',
        schemata='addressing',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label=_(u'label_formmailer_cc_recipients',
                      default=u'CC Recipients'),
            description=_(u'help_formmailer_cc_recipients',
                    default=u'E-mail addresses which receive a carbon copy.'),
            ),
        ),
    LinesField('bcc_recipients',
        schemata='addressing',
        searchable=0,
        required=0,
        default_method='getDefaultBCC',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label=_(u'label_formmailer_bcc_recipients',
                      default=u'BCC Recipients'),
            description=_(u'help_formmailer_bcc_recipients',
                default=u'E-mail addresses which receive a blind carbon copy.'),
            ),
        ),
    StringField('replyto_field',
        schemata='addressing',
        searchable=0,
        required=0,
        vocabulary='fieldsDisplayList',
        read_permission=ModifyPortalContent,
        write_permission=EDIT_ADVANCED_PERMISSION,
        widget=SelectionWidget(
            label=_(u'label_formmailer_replyto_extract',
                      default=u'Extract Reply-To From'),
            description=_(u'help_formmailer_replyto_extract',
                default=u"""
                Choose a form field from which you wish to extract
                input for the Reply-To header. NOTE: You should
                activate e-mail address verification for the designated
                field.
                """),
            ),
        ),
    StringField('msg_subject',
        schemata='message',
        searchable=0,
        required=0,
        default='Form Submission',
        read_permission=ModifyPortalContent,
        widget=StringWidget(
            description=_(u'help_formmailer_subject',
                default=u"""
                Subject line of message. This is used if you
                do not specify a subject field or if the field
                is empty.
                """),
            label=_(u'label_formmailer_subject', default=u'Subject'),
            ),
        ),
    StringField('subject_field',
        schemata='message',
        searchable=0,
        required=0,
        vocabulary='fieldsDisplayList',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=SelectionWidget(
            label=_(u'label_formmailer_subject_extract',
                      default=u'Extract Subject From'),
            description=_(u'help_formmailer_subject_extract',
            default=u"""
                Choose a form field from which you wish to extract
                input for the mail subject line.
                """),
            ),
        ),
    TextField('body_pre',
        searchable=0,
        required=0,
        schemata='message',
        accessor='getBody_pre',
        read_permission=ModifyPortalContent,
        default_content_type='text/plain',
        allowable_content_types=('text/plain',),
        widget=TextAreaWidget(description=_(u'help_formmailer_body_pre',
                      default=u'Text prepended to fields listed in mail-body'),
          label=_(u'label_formmailer_body_pre', default=u'Body (prepended)'),
            ),
        ),
    TextField('body_post',
        searchable=0,
        required=0,
        schemata='message',
        read_permission=ModifyPortalContent,
        default_content_type='text/plain',
        allowable_content_types=('text/plain',),
        widget=TextAreaWidget(description=_(u'help_formmailer_body_post',
                      default=u'Text appended to fields listed in mail-body'),
          label=_(u'label_formmailer_body_post', default=u'Body (appended)'),
            ),
        ),
 
    TextField('body_footer',
        searchable=0,
        required=0,
        schemata='message',
        read_permission=ModifyPortalContent,
        default_content_type='text/plain',
        allowable_content_types=('text/plain',),
        widget=TextAreaWidget(description=_(u'help_formmailer_body_footer',
                          default=u'Text used as the footer at '
                          u'bottom, delimited from the body by a dashed line.'),
            label=_(u'label_formmailer_body_footer',
                      default=u'Body (signature)'),
            ),
        ),
    BooleanField('showAll',
        required=0,
        searchable=0,
        schemata='message',
        default='1',
        read_permission=ModifyPortalContent,
        widget=BooleanWidget(
            label=_(u'label_mailallfields_text', default=u"Include All Fields"),
            description=_(u'help_mailallfields_text', default=u"""
                Check this to include input for all fields
                (except label and file fields). If you check
                this, the choices in the pick box below
                will be ignored.
                """),
            ),
        ),
    LinesField('showFields',
        required=0,
        searchable=0,
        schemata='message',
        vocabulary='allFieldDisplayList',
        read_permission=ModifyPortalContent,
        widget=PicklistWidget(
            label=_(u'label_mailfields_text', default=u"Show Responses"),
            description=_(u'help_mailfields_text', default=u"""
                Pick the fields whose inputs you'd like to include in
                the e-mail.
                """),
            ),
        ),
    BooleanField('includeEmpties',
        required=0,
        searchable=0,
        schemata='message',
        default='1',
        read_permission=ModifyPortalContent,
        widget=BooleanWidget(
            label=_(u'label_mailEmpties_text', default=u"Include Empties"),
            description=_(u'help_mailEmpties_text', default=u"""
                Check this to include titles
                for fields that received no input. Uncheck
                to leave fields with no input out of the e-mail.
                """),
            ),
        ),
    ZPTField('body_pt',
        schemata='template',
        write_permission=EDIT_TALES_PERMISSION,
        default_method='getMailBodyDefault',
        read_permission=ModifyPortalContent,
        widget=TextAreaWidget(description=_(u'help_formmailer_body_pt',
            default=u"""This is a Zope Page Template
            used for rendering of the mail-body. You don\'t need to modify
            it, but if you know TAL (Zope\'s Template Attribute Language)
            you have the full power to customize your outgoing mails."""),
            label=_(u'label_formmailer_body_pt', default=u'Mail-Body Template'),
            rows=20,
            visible={'edit': 'visible', 'view': 'invisible'},
            ),
        validators=('zptvalidator',),
        ),
    StringField('body_type',
        schemata='template',
        default_method='getMailBodyTypeDefault',
        vocabulary=MIME_LIST,
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=SelectionWidget(description=_(u'help_formmailer_body_type',
            default=u"""Set the mime-type of the mail-body.
            Change this setting only if you know exactly what you are doing.
            Leave it blank for default behaviour."""),
            label = _(u'label_formmailer_body_type', default=u'Mail Format'),
            ),
        ),
    LinesField('xinfo_headers',
        searchable=0,
        required=0,
        schemata='headers',
        default_method='getDefaultXInfo',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        vocabulary=DisplayList((
            ('HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_FOR',),
            ('REMOTE_ADDR', 'REMOTE_ADDR',),
            ('PATH_INFO', 'PATH_INFO'),
            ('HTTP_USER_AGENT', 'HTTP_USER_AGENT',),
            ('HTTP_REFERER', 'HTTP_REFERER'),
            ), ),
        widget=MultiSelectionWidget(
            label=_(u'label_xinfo_headers_text', default=u'HTTP Headers'),
            description=_(u'help_xinfo_headers_text', default=u"""
                Pick any items from the HTTP headers that
                you'd like to insert as X- headers in the
                message.
                """),
            format='checkbox',
            ),
        ),
    LinesField('additional_headers',
        schemata='headers',
        searchable=0,
        required=0,
        default_method='getDefaultAddHdrs',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label=_(u'label_formmailer_additional_headers', default=u'Additional Headers'),
            description=_(u'help_formmailer_additional_headers', default=u"""
                Additional e-mail-header lines.
                Only use RFC822-compliant headers.
                """),
            ),
        ),
))
 
if gpg is not None:
    formMailerAdapterSchema = formMailerAdapterSchema + Schema((
        StringField('gpg_keyid',
            schemata='encryption',
            accessor='getGPGKeyId',
            mutator='setGPGKeyId',
            write_permission=USE_ENCRYPTION_PERMISSION,
            read_permission=ModifyPortalContent,
            widget=StringWidget(
                description=_(u'help_gpg_key_id', default=u"""
                    Give your key-id, e-mail address or
                    whatever works to match a public key from current keyring.
                    It will be used to encrypt the message body (not attachments).
                    Contact the site administrator if you need to
                    install a new public key.
                    Note that you will probably wish to change your message
                    template to plain text if you're using encryption.
                    TEST THIS FEATURE BEFORE GOING PUBLIC!
                    """),
                label=_(u'label_gpg_key_id', default=u'Key-Id'),
                ),
            ),
        ))
 
 
formMailerAdapterSchema = formMailerAdapterSchema + Schema((
    TALESString('subjectOverride',
        schemata='overrides',
        searchable=0,
        required=0,
        validators=('talesvalidator',),
        default='',
        write_permission=EDIT_TALES_PERMISSION,
        read_permission=ModifyPortalContent,
        isMetadata=True,  # just to hide from base view
        widget=StringWidget(label=_(u'label_subject_override_text', default=u"Subject Expression"),
            description=_(u'help_subject_override_text', default=u"""
                A TALES expression that will be evaluated to override any value
                otherwise entered for the e-mail subject header.
                Leave empty if unneeded. Your expression should evaluate as a string.
                PLEASE NOTE: errors in the evaluation of this expression will cause
                an error on form display.
            """),
            size=70,
        ),
    ),
    TALESString('senderOverride',
        schemata='overrides',
        searchable=0,
        required=0,
        validators=('talesvalidator',),
        default='',
        write_permission=EDIT_TALES_PERMISSION,
        read_permission=ModifyPortalContent,
        isMetadata=True,  # just to hide from base view
        widget=StringWidget(label=_(u'label_sender_override_text',
                                    default=u"Sender Expression"),
            description=_(u'help_sender_override_text', default=u"""
                A TALES expression that will be evaluated to override the "From" header.
                Leave empty if unneeded. Your expression should evaluate as a string.
                PLEASE NOTE: errors in the evaluation of this expression will cause
                an error on form display.
            """),
            size=70,
        ),
    ),
    TALESString('recipientOverride',
        schemata='overrides',
        searchable=0,
        required=0,
        validators=('talesvalidator',),
        default='',
        write_permission=EDIT_TALES_PERMISSION,
        read_permission=ModifyPortalContent,
        isMetadata=True,  # just to hide from base view
        widget=StringWidget(label=_(u'label_recipient_override_text', default=u"Recipient Expression"),
            description=_(u'help_recipient_override_text', default=u"""
                A TALES expression that will be evaluated to override any value
                otherwise entered for the recipient e-mail address. You are strongly
                cautioned against using unvalidated data from the request for this purpose.
                Leave empty if unneeded. Your expression should evaluate as a string.
                PLEASE NOTE: errors in the evaluation of this expression will cause
                an error on form display.
            """),
            size=70,
        ),
    ),
    TALESString('ccOverride',
        schemata='overrides',
        searchable=0,
        required=0,
        validators=('talesvalidator',),
        default='',
        write_permission=EDIT_TALES_PERMISSION,
        read_permission=ModifyPortalContent,
        isMetadata=True, # just to hide from base view
        widget=StringWidget(label=_(u'label_cc_override_text', default=u"CC Expression"),
            description=_(u'help_cc_override_text', default=u"""
                A TALES expression that will be evaluated to override any value
                otherwise entered for the CC list. You are strongly
                cautioned against using unvalidated data from the request for this purpose.
                Leave empty if unneeded. Your expression should evaluate as a sequence of strings.
                PLEASE NOTE: errors in the evaluation of this expression will cause
                an error on form display.
            """),
            size=70,
        ),
    ),
    TALESString('bccOverride',
        schemata='overrides',
        searchable=0,
        required=0,
        validators=('talesvalidator',),
        default='',
        write_permission=EDIT_TALES_PERMISSION,
        read_permission=ModifyPortalContent,
        isMetadata=True,  # just to hide from base view
        widget=StringWidget(label=_(u'label_bcc_override_text', default=u"BCC Expression"),
            description=_(u'help_bcc_override_text', default=u"""
                A TALES expression that will be evaluated to override any value
                otherwise entered for the BCC list. You are strongly
                cautioned against using unvalidated data from the request for this purpose.
                Leave empty if unneeded. Your expression should evaluate as a sequence of strings.
                PLEASE NOTE: errors in the evaluation of this expression will cause
                an error on form display.
            """),
            size=70,
        ),
    ),
))
 
# move headers schema items to template schema to keep the schema
# count <= 6
formMailerAdapterSchema['xinfo_headers'].schemata = 'template'
formMailerAdapterSchema['additional_headers'].schemata = 'template'
 
formMailerAdapterSchema.moveField('execCondition', pos='bottom')
 
NEED_PROTECTION = (
    'body_pre',
    'body_post',
    'body_footer',
    'body_pt',
    'subjectOverride',
    'senderOverride',
    'recipientOverride',
    'ccOverride',
    'bccOverride',
    )
 
class FormMailerAdapter(FormActionAdapter):
    """ A form action adapter that will e-mail form input. """
 
    schema = formMailerAdapterSchema
    portal_type = meta_type = 'FormMailerAdapter'
    archetype_name = 'Mailer Adapter'
    content_icon = 'mailaction.gif'
 
    security = ClassSecurityInfo()
 
    def __bobo_traverse__(self, REQUEST, name):
        # prevent traversal to attributes we want to protect
        if name in NEED_PROTECTION:
            raise AttributeError
        return super(FormMailerAdapter, self).__bobo_traverse__(REQUEST, name)
 
    def initializeArchetype(self, **kwargs):
        """ Translate the adapter in the current langage
        """
 
        FormActionAdapter.initializeArchetype(self, **kwargs)
 
        self.setMsg_subject(zope.i18n.translate(_(u'pfg_formmaileradapter_msg_subject', u'Form Submission'), context=self.REQUEST))
 
    security.declarePrivate('onSuccess')
 
    def onSuccess(self, fields, REQUEST=None):
        """
        e-mails data.
        """
 
        self.send_form(fields, REQUEST)
 
    security.declarePrivate('getMailBodyDefault')
 
    def getMailBodyDefault(self):
        """ Get default mail body from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailTemplateBody()
 
    security.declarePrivate('getMailBodyTypeDefault')
 
    def getMailBodyTypeDefault(self):
        """ Get default mail body type from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailBodyType()
 
    security.declarePrivate('getDefaultRecipient')
 
    def getDefaultRecipient(self):
        """ Get default mail recipient from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailRecipient()
 
    security.declarePrivate('getDefaultRecipientName')
 
    def getDefaultRecipientName(self):
        """ Get default mail recipient from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailRecipientName()
 
    security.declarePrivate('getDefaultCC')
 
    def getDefaultCC(self):
        """ Get default mail cc from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailCC()
 
    security.declarePrivate('getDefaultBCC')
 
    def getDefaultBCC(self):
        """ Get default mail bcc from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailBCC()
 
    security.declarePrivate('getDefaultXInfo')
 
    def getDefaultXInfo(self):
        """ Get default mail xinfo hdrs from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailXInfo()
 
    security.declarePrivate('getDefaultAddHdrs')
 
    def getDefaultAddHdrs(self):
        """ Get default mail add headers from our tool """
 
        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getDefaultMailAddHdrs()
 
    security.declarePrivate('setBody_pt')
 
    def setBody_pt(self, value, **kw):
        # """ set body template with BBB for accessors """
 
        if shasattr(value, 'replace'):
            template = value
            for s, t in [('body_pre', 'getBody_pre'),
                         ('body_post', 'getBody_post'),
                         ('body_footer', 'getBody_footer')]:
                template = template.replace('here/%s' % s, 'here/%s' % t)
            myField = self.getField('body_pt')
            myField.set(self, template)
 
    security.declarePrivate('_dreplace')
 
    def _dreplace(self, s):
        request = getattr(self, 'REQUEST', {})
        return dollarReplace.DollarVarReplacer(getattr(request, 'form', {})).sub(s)
 
    security.declarePublic('getBody_pre')
 
    def getBody_pre(self):
        """ get expanded mail body prefix """
        return self._dreplace(self.getRawBody_pre())
 
    security.declarePublic('getBody_post')
 
    def getBody_post(self):
        """ get expanded mail body postfix """
        return self._dreplace(self.getRawBody_post())
 
    security.declarePublic('getBody_footer')
 
    def getBody_footer(self):
        """ get expanded mail body footer """
        return self._dreplace(self.getRawBody_footer())
 
    security.declarePrivate('get_mail_text')
 
    def get_mail_text(self, fields, request, **kwargs):
        """Get header and body of e-mail as text (string)
        """
 
        (headerinfo, additional_headers,
         body) = self.get_header_body_tuple(fields, request, **kwargs)
 
        if not isinstance(body, unicode):
            body = unicode(body, self._site_encoding())
        portal = getToolByName(self, 'portal_url').getPortalObject()
        email_charset = portal.getProperty('email_charset', 'utf-8')
        # always use text/plain for encrypted bodies
        subtype = getattr(self, 'gpg_keyid', False) and 'plain' or self.body_type or 'html'
        mime_text = MIMEText(body.encode(email_charset, 'replace'),
                _subtype=subtype, _charset=email_charset)
 
        attachments = self.get_attachments(fields, request)
 
        if attachments:
            outer = MIMEMultipart()
            outer.attach(mime_text)
        else:
            outer = mime_text
 
        # write header
        for key, value in headerinfo.items():
            outer[key] = value
 
        # write additional header
        for a in additional_headers:
            key, value = a.split(':', 1)
            outer.add_header(key, value.strip())
 
        for attachment in attachments:
            filename = attachment[0]
            ctype = attachment[1]
            # encoding = attachment[2]
            content = attachment[3]
 
            if ctype is None:
                ctype = 'application/octet-stream'
 
            maintype, subtype = ctype.split('/', 1)
 
            if maintype == 'text':
                msg = MIMEText(content, _subtype=subtype)
            elif maintype == 'image':
                msg = MIMEImage(content, _subtype=subtype)
            elif maintype == 'audio':
                msg = MIMEAudio(content, _subtype=subtype)
            else:
                msg = MIMEBase(maintype, subtype)
                msg.set_payload(content)
                # Encode the payload using Base64
                Encoders.encode_base64(msg)
 
            # Set the filename parameter
            msg.add_header('Content-Disposition', 'attachment', filename=filename)
            outer.attach(msg)
 
        return outer.as_string()
 
    security.declarePrivate('get_mail_text')
 
    def get_attachments(self, fields, request):
        """Return all attachments uploaded in form.
        """
 
        from ZPublisher.HTTPRequest import FileUpload
 
        attachments = []
 
        for field in fields:
            if field.isFileField() and (getattr(self, 'showAll', True) \
                or field.fgField.getName() in getattr(self, 'showFields', ())):
                file = request.form.get('%s_file' % field.__name__, None)
                if file and isinstance(file, FileUpload) and file.filename != '':
                    file.seek(0)  # rewind
                    data = file.read()
                    filename = file.filename
                    mimetype, enc = guess_content_type(filename, data, None)
                    attachments.append((filename, mimetype, enc, data))
        return attachments
 
    security.declarePrivate('get_mail_body')
 
    def get_mail_body(self, fields, **kwargs):
        """Returns the mail-body with footer.
        """
 
        if 'request' in kwargs:
            request = kwargs['request']
        else:
            request = self.REQUEST
 
        all_fields = [f for f in fields
            if not (f.isLabel() or f.isFileField()) and not (getattr(self, 'showAll', True) and f.getServerSide())]
 
        # which fields should we show?
        if getattr(self, 'showAll', True):
            live_fields = all_fields
        else:
            live_fields = \
                [f for f in all_fields
                   if f.fgField.getName() in getattr(self, 'showFields', ())]
 
        if not getattr(self, 'includeEmpties', True):
            all_fields = live_fields
            live_fields = []
            for f in all_fields:
                value = f.htmlValue(request)
                if value and value != 'No Input':
                    live_fields.append(f)
 
        bare_fields = [f.fgField for f in live_fields]
        bodyfield = self.getField('body_pt')
 
        # pass both the bare_fields (fgFields only) and full fields.
        # bare_fields for compatability with older templates,
        # full fields to enable access to htmlValue
        body = bodyfield.get(self, fields=bare_fields, wrappedFields=live_fields, **kwargs)
 
        if isinstance(body, unicode):
            body = body.encode(self.getCharset())
 
        keyid = self.getGPGKeyId()
        encryption = gpg and keyid
 
        if encryption:
            bodygpg = gpg.encrypt(body, keyid)
            if bodygpg.strip():
                body = bodygpg
 
        return body
 
    security.declarePrivate('secure_header_line')
 
    def secure_header_line(self, line):
        nlpos = line.find('\x0a')
        if nlpos >= 0:
            line = line[:nlpos]
        nlpos = line.find('\x0d')
        if nlpos >= 0:
            line = line[:nlpos]
        return line
 
    security.declarePrivate('_destFormat')
 
    def _destFormat(self, input):
        """ Format destination (To) input.
            Input may be a string or sequence of strings;
            returns a well-formatted address field
        """
 
        if type(input) in StringTypes:
            input = [s for s in input.split(',')]
        input = [s for s in input if s]
        filtered_input = [s.strip().encode('utf-8') for s in input]
 
        if filtered_input:
            return "<%s>" % '>, <'.join(filtered_input)
        else:
            return ''
 
    security.declarePrivate('get_header_body_tuple')
 
    def get_header_body_tuple(self, fields, request,
                              from_addr=None, to_addr=None,
                              subject=None, **kwargs):
        """Return header and body of e-mail as an 3-tuple:
        (header, additional_header, body)
 
        header is a dictionary, additional header is a list, body is a StringIO
 
        Keyword arguments:
        request -- (optional) alternate request object to use
        """
 
        pprops = getToolByName(self, 'portal_properties')
        site_props = getToolByName(pprops, 'site_properties')
        portal = getToolByName(self, 'portal_url').getPortalObject()
        pms = getToolByName(self, 'portal_membership')
        utils = getToolByName(self, 'plone_utils')
 
        body = self.get_mail_body(fields, **kwargs)
 
        # fields = self.fgFields()
 
        # get Reply-To
        reply_addr = None
        if shasattr(self, 'replyto_field'):
            reply_addr = request.form.get(self.replyto_field, None)
 
        # get subject header
        nosubject = '(no subject)'
        if shasattr(self, 'subjectOverride') and self.getRawSubjectOverride():
            # subject has a TALES override
            subject = self.getSubjectOverride().strip()
        else:
            subject = getattr(self, 'msg_subject', nosubject)
            subjectField = request.form.get(self.subject_field, None)
            if subjectField is not None:
                subject = subjectField
            else:
                # we only do subject expansion if there's no field chosen
                subject = self._dreplace(subject)
 
        # Get From address
        if shasattr(self, 'senderOverride') and self.getRawSenderOverride():
            from_addr = self.getSenderOverride().strip()
        else:
            from_addr = from_addr or site_props.getProperty('email_from_address') or \
                        portal.getProperty('email_from_address')
 
        # Get To address and full name
        if shasattr(self, 'recipientOverride') and self.getRawRecipientOverride():
            recip_email = self.getRecipientOverride()
        else:
            recip_email = None
            if shasattr(self, 'to_field'):
                recip_email = request.form.get(self.to_field, None)
            if not recip_email:
                recip_email = self.recipient_email
        recip_email = self._destFormat(recip_email)
 
        recip_name = self.recipient_name.encode('utf-8')
 
        # if no to_addr and no recip_email specified, use owner adress if possible.
        # if not, fall back to portal email_from_address.
        # if still no destination, raise an assertion exception.
        if not recip_email and not to_addr:
            ownerinfo = self.getOwner()
            ownerid = ownerinfo.getId()
            fullname = ownerid
            userdest = pms.getMemberById(ownerid)
            if userdest is not None:
                fullname = userdest.getProperty('fullname', ownerid)
            toemail = ''
            if userdest is not None:
                toemail = userdest.getProperty('email', '')
            if not toemail:
                toemail = portal.getProperty('email_from_address')
            assert toemail, """
                    Unable to mail form input because no recipient address has been specified.
                    Please check the recipient settings of the PloneFormGen "Mailer" within the
                    current form folder.
                """
            to = formataddr((fullname, toemail))
        else:
            to = to_addr or '%s %s' % (recip_name, recip_email)
 
        headerinfo = OrderedDict()
 
        headerinfo['To'] = self.secure_header_line(to)
        headerinfo['From'] = self.secure_header_line(from_addr)
        if reply_addr:
            headerinfo['Reply-To'] = self.secure_header_line(reply_addr)
 
        # transform subject into mail header encoded string
        email_charset = portal.getProperty('email_charset', 'utf-8')
 
        if not isinstance(subject, unicode):
            site_charset = utils.getSiteEncoding()
            subject = unicode(subject, site_charset, 'replace')
 
        msgSubject = self.secure_header_line(subject).encode(email_charset, 'replace')
        msgSubject = str(Header(msgSubject, email_charset))
        headerinfo['Subject'] = msgSubject
 
        # CC
        cc_recips = filter(None, self.cc_recipients)
        if shasattr(self, 'ccOverride') and self.getRawCcOverride():
            cc_recips = self.getCcOverride()
        if cc_recips:
            headerinfo['Cc'] = self._destFormat(cc_recips)
 
        # BCC
        bcc_recips = filter(None, self.bcc_recipients)
        if shasattr(self, 'bccOverride') and self.getRawBccOverride():
            bcc_recips = self.getBccOverride()
        if bcc_recips:
            headerinfo['Bcc'] = self._destFormat(bcc_recips)
 
        for key in getattr(self, 'xinfo_headers', []):
            headerinfo['X-%s' % key] = self.secure_header_line(request.get(key, 'MISSING'))
 
        # return 3-Tuple
        return (headerinfo, self.additional_headers, body)
 
    def send_form(self, fields, request, **kwargs):
        """Send the form.
        """
 
        mailtext = self.get_mail_text(fields, request, **kwargs)
        host = self.MailHost
        host.send(mailtext)
 
    # translation and encodings
    def _site_encoding(self):
        site_props = self.portal_properties.site_properties
        return site_props.default_charset or 'UTF-8'
 
    security.declareProtected(View, 'allFieldDisplayList')
 
    def allFieldDisplayList(self):
        """ returns a DisplayList of all fields """
 
        return self.fgFieldsDisplayList()
 
    def fieldsDisplayList(self):
        """ returns display list of fields with simple values """
 
        return self.fgFieldsDisplayList(
            withNone=True,
            noneValue='#NONE#',
            objTypes=(
                'FormSelectionField',
                'FormStringField',
                )
            )
 
    security.declareProtected(ModifyPortalContent, 'setShowFields')
 
    def setShowFields(self, value, **kw):
        """ Reorder form input to match field order """
        # This wouldn't be desirable if the PickWidget
        # retained order.
 
        self.showFields = []
        for field in self.fgFields(excludeServerSide=False):
            id = field.getName()
            if id in value:
                self.showFields.append(id)
 
 
registerATCT(FormMailerAdapter, PROJECTNAME)