"""
 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 Products.Archetypes.public import *
from Products.Archetypes.utils import OrderedDict
from Products.Archetypes.utils import shasattr

from Products.ATContentTypes.content.schemata import finalizeATCTSchema
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.PageTemplates.ZopePageTemplate import ZopePageTemplate

from Products.TALESField import TALESString, TALESLines
from Products.TemplateFields import ZPTField as ZPTField

from Products.PloneFormGen.content.actionAdapter import FormActionAdapter, FormAdapterSchema
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 types import StringTypes, StringType, UnicodeType 

from DateTime import DateTime

from Products.PloneFormGen import PloneFormGenMessageFactory as _
from Products.PloneFormGen import HAS_PLONE25, HAS_PLONE30

try:
    # 3.0+
    from zope.contenttype import guess_content_type
except ImportError:
    try:
        # 2.5
        from zope.app.content_types import guess_content_type
    except ImportError:
        # 2.1
        from OFS.content_types import guess_content_type

if HAS_PLONE25:
  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 = "Recipient's full name",
            description = """
                The full name of the recipient of the mailed form.
                """,
            description_msgid = "help_formmailer_recipient_fullname",
            label_msgid = "label_formmailer_recipient_fullname",
            i18n_domain = "ploneformgen",
            ),
        ),
    StringField('recipient_email',
        searchable=0,
        required=0,
        default_method='getDefaultRecipient',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        validators=('isEmail',),
        widget=StringWidget(
            label = "Recipient's e-mail address",
            description = 'The recipients e-mail address.',
            description_msgid = "help_formmailer_recipient_email",
            label_msgid = "label_formmailer_recipient_email",
            i18n_domain = "ploneformgen",
            ),
        ),
    StringField('to_field',
        schemata='addressing',
        searchable=0,
        required=0,
        default='#NONE#',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        vocabulary='selectFieldsDisplayList',
        widget=SelectionWidget(
            label = 'Extract Recipient From',
            label_msgid = "label_formmailer_to_extract",
            description =
                """
                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.
                Only selection fields are eligible for this use because they
                may be validated against your specified options.
                """,
            description_msgid = "help_formmailer_to_extract",
            i18n_domain = "ploneformgen",
            ),
        ),
    LinesField('cc_recipients',
        searchable=0,
        required=0,
        default_method='getDefaultCC',
        schemata='addressing',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label = 'CC Recipients',
            description = 'E-mail addresses which receive a carbon copy.',
            description_msgid = "help_formmailer_cc_recipients",
            label_msgid = "label_formmailer_cc_recipients",
            i18n_domain = "ploneformgen",
            ),
        ),
    LinesField('bcc_recipients',
        schemata='addressing',
        searchable=0,
        required=0,
        default_method='getDefaultBCC',
        write_permission=EDIT_ADDRESSING_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label = 'BCC Recipients',
            description = 'E-mail addresses which receive a blind carbon copy.',
            description_msgid = "help_formmailer_bcc_recipients",
            label_msgid = "label_formmailer_bcc_recipients",
            i18n_domain = "ploneformgen",
            ),
        ),
    StringField('replyto_field',
        schemata='addressing',
        searchable=0,
        required=0,
        vocabulary='fieldsDisplayList',
        read_permission=ModifyPortalContent,
        write_permission=EDIT_ADVANCED_PERMISSION,
        widget=SelectionWidget(
            label = 'Extract Reply-To From',
            label_msgid = "label_formmailer_replyto_extract",
            description =
                """
                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.
                """,
            description_msgid = "help_formmailer_replyto_extract",
            i18n_domain = "ploneformgen",
            ),
        ),
    StringField('msg_subject',
        schemata='message',
        searchable=0,
        required=0,
        default='Form Submission',
        read_permission=ModifyPortalContent,
        widget=StringWidget(
            description =
                """
                Subject line of message. This is used if you
                do not specify a subject field or if the field
                is empty.
                """,
            description_msgid = "help_formmailer_subject",
            label = 'Subject',
            label_msgid = "label_formmailer_subject",
            i18n_domain = "ploneformgen",
            ),
        ),
    StringField('subject_field',
        schemata='message',
        searchable=0,
        required=0,
        vocabulary='fieldsDisplayList',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=SelectionWidget(
            label = 'Extract Subject From',
            label_msgid = "label_formmailer_subject_extract",
            description =
                """
                Choose a form field from which you wish to extract
                input for the mail subject line.
                """,
            description_msgid = "help_formmailer_subject_extract",
            i18n_domain = "ploneformgen",
            ),
        ),
    TextField('body_pre',
        searchable=0,
        required=0,
        schemata='message',
        read_permission=ModifyPortalContent,
        default_content_type = 'text/plain',
        allowable_content_types = ('text/plain',),
        widget=TextAreaWidget(description = 'Text prepended to fields listed in mail-body',
            description_msgid = "help_formmailer_body_pre",
            label = 'Body (prepended)',
            label_msgid = "label_formmailer_body_pre",
            i18n_domain = "ploneformgen",
            ),
        ),
    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 = 'Text appended to fields listed in mail-body',
            description_msgid = "help_formmailer_body_post",
            label = 'Body (appended)',
            label_msgid = "label_formmailer_body_post",
            i18n_domain = "ploneformgen",
            ),
        ),

    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 = 'Text used as the footer at '
            'bottom, delimited from the body by a dashed line.',
            description_msgid = "help_formmailer_body_footer",
            label = 'Body (signature)',
            label_msgid = "label_formmailer_body_footer",
            i18n_domain = "ploneformgen",
            ),
        ),
    ZPTField('body_pt',
        schemata='template',
        write_permission=EDIT_TALES_PERMISSION,
        default_method = 'getMailBodyDefault',
        read_permission=ModifyPortalContent,
        widget=TextAreaWidget(description = '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.',
            description_msgid = "help_formmailer_body_pt",
            label = 'Mail-Body Template',
            label_msgid = "label_formmailer_body_pt",
            i18n_domain = "ploneformgen",
            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 = '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.',
            description_msgid = "help_formmailer_body_type",
            label = 'Mail-Body Type',
            label_msgid = "label_formmailer_body_type",
            i18n_domain = "ploneformgen",
            ),
        ),
    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='HTTP Headers',
            description="""
                Pick any items from the HTTP headers that
                you'd like to insert as X- headers in the
                message.
                """,
            format='checkbox',
            i18n_domain = "ploneformgen",
            label_msgid = "label_xinfo_headers_text",
            description_msgid = "help_xinfo_headers_text",
            ),
        ),
    LinesField('additional_headers',
        schemata='headers',
        searchable=0,
        required=0,
        default_method='getDefaultAddHdrs',
        write_permission=EDIT_ADVANCED_PERMISSION,
        read_permission=ModifyPortalContent,
        widget=LinesWidget(
            label = 'Additional Headers',
            description="""
                Additional e-mail-header lines.
                Only use RFC822-compliant headers.
                """,
            description_msgid = "help_formmailer_additional_headers",
            label_msgid = "label_formmailer_additional_headers",
            i18n_domain = "ploneformgen",
            ),
        ),
))

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 = """
                    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.
                    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!
                    """,
                description_msgid = "help_gpg_key_id",
                label = 'Key-Id',
                label_msgid = "label_gpg_key_id",
                i18n_domain='ploneformgen',
                ),
            ),
        ))


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="Subject Expression",
            description="""
                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,
            i18n_domain = "ploneformgen",
            label_msgid = "label_subject_override_text",
            description_msgid = "help_subject_override_text",
        ),
    ),
    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="Sender Expression",
            description="""
                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,
            i18n_domain = "ploneformgen",
            label_msgid = "label_sender_override_text",
            description_msgid = "help_sender_override_text",
        ),
    ),
    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="Recipient Expression",
            description="""
                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,
            i18n_domain = "ploneformgen",
            label_msgid = "label_recipient_override_text",
            description_msgid = "help_recipient_override_text",
        ),
    ),
))

if HAS_PLONE30:
    # 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')
if not HAS_PLONE30:
    finalizeATCTSchema(formMailerAdapterSchema, folderish=True, moveDiscussion=False)


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 initializeArchetype(self, **kwargs):
        """ Translate the adapter in the current langage
        """

        FormActionAdapter.initializeArchetype(self, **kwargs)

        if HAS_PLONE25:
            self.setMsg_subject(zope.i18n.translate(_(u'pfg_formmaileradapter_msg_subject', u'Form Submission'), context=self.REQUEST))
        else:
            self.setMsg_subject(self.translate(
                                  msgid='pfg_formmaileradapter_msg_subject',
                                  domain='ploneformgen',
                                  default='Form Submission'))


    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('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)

        mime_text = MIMEText(body,
            _subtype=self.body_type or 'html', _charset=self._site_encoding())

        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()


    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():
                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 kwargs.has_key('request'):
            request = kwargs['request']
        else:
            request = self.REQUEST

        live_fields = [f for f in fields
          if not (f.isLabel() or f.isFileField())]
        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 = getattr(self, 'gpg_keyid', None)
        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.strip().encode('utf-8') for s in input.split(',')]

        filtered_input = [s for s in input if s]
        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
        """

#        if kwargs.has_key('request'):
#            request = kwargs['request']
#        else:
#            request = self.REQUEST

        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)
            if getattr(self, 'subject_field', None):
                subject = request.form.get(self.subject_field, subject)

        # Get From address
        if shasattr(self, 'senderOverride') and self.getRawSenderOverride():
            from_addr = self.getSenderOverride().strip()
        else:
            pprops = getToolByName(self, 'portal_properties')
            site_props = getToolByName(pprops, 'site_properties')
            portal = getToolByName(self, 'portal_url').getPortalObject()
            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()
            pms = getToolByName(self, 'portal_membership')
            userdest = pms.getMemberById(ownerid)
            toemail = userdest.getProperty('email', '')
            if not toemail:
                portal = getToolByName(self, 'portal_url').getPortalObject()
                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 = '%s <%s>' %(ownerid,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
        msgSubject = str(Header(self.secure_header_line(subject), self._site_encoding()))
        headerinfo['Subject'] = msgSubject

        headerinfo['MIME-Version'] = '1.0'

        # CC
        cc_recips = filter(None, self.cc_recipients)
        if cc_recips:
            addrs = ['<%s>' % addr for addr in cc_recips]
            headerinfo['Cc'] = ', '.join(addrs)

        # BCC
        bcc_recips = filter(None, self.bcc_recipients)
        if bcc_recips:
            addrs = ['<%s>' % addr for addr in bcc_recips]
            headerinfo['Bcc'] = ', '.join(addrs)

        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'


    def fieldsDisplayList(self):
        """ returns display list of fields with simple values """

        return self.fgFieldsDisplayList(
            withNone=True,
            noneValue='#NONE#',
            objTypes=(
                'FormSelectionField',
                'FormStringField',
                )
            )


    def selectFieldsDisplayList(self):
        """ returns display list of selection fields """

        return self.fgFieldsDisplayList(
            withNone=True, 
            noneValue='#NONE#',
            objTypes=(
                'FormSelectionField',
                'FormMultiSelectionField',
                )
            )


registerATCT(FormMailerAdapter, PROJECTNAME)
