def from_email_message(cls, message, local_id=None):
        '''
        Convert an :class:`email.message.Message` or compatible message
        object into a CERP XML :class:`eulxml.xmlmap.cerp.Message`. If an
        id is specified, it will be stored in the Message <LocalId>.

        :param message: `email.message.Message` object
        :param id: optional message id to be set as `local_id`

        :returns: :class:`eulxml.xmlmap.cerp.Message` instance populated
    	    with message information

        '''
        result = cls()
        if local_id is not None:
            result.local_id = id

        message_id = message.get('Message-Id')
        if message_id:
            result.message_id_supplied = True
            result.message_id = message_id

        result.mime_version = message.get('MIME-Version')

        dates = message.get_all('Date', [])
        result.orig_date_list.extend([parse_mail_date(d) for d in dates])

        result.from_list.extend(message.get_all('From', []))
        result.sender_list.extend(message.get_all('From', []))
        try:
            result.to_list.extend(message.get_all('To', []))
        except UnicodeError:
            print(repr(message['To']))
            raise
        result.cc_list.extend(message.get_all('Cc', []))
        result.bcc_list.extend(message.get_all('Bcc', []))
        result.in_reply_to_list.extend(message.get_all('In-Reply-To', []))
        result.references_list.extend(message.get_all('References', []))
        result.subject_list.extend(message.get_all('Subject', []))
        result.comments_list.extend(message.get_all('Comments', []))
        result.keywords_list.extend(message.get_all('Keywords', []))

        headers = [ Header(name=key, value=val) for key, val in message.items() ]
        result.headers.extend(headers)

        # FIXME: skip multipart messages for now
        if not message.is_multipart():
            result.create_single_body()

            # FIXME: this is a small subset of the actual elements CERP allows.
            # we should add the rest of them, too.

            # message.get_content_type() always returns something. only
            # put it in the CERP if a Content-Type was explicitly specified.
            if message['Content-Type']:
                result.single_body.content_type_list.append(message.get_content_type())
            if message.get_content_charset():
                result.single_body.charset_list.append(message.get_content_charset())
            if message.get_filename():
                result.single_body.content_name_list.append(message.get_filename())

            # FIXME: attaching the body_content only makes sense for text
            # content types. we'll eventually need a better solution for
            # non-text messages
            result.single_body.create_body_content()
            payload = message.get_payload(decode=False)

            # if not unicode, attempt to convert
            if isinstance(payload, six.binary_type):
                charset = message.get_charset()
                # decode according to the specified character set, if any
                if charset is not None:
                    charset_decoder = codecs.getdecoder(str(charset))
                    payload, length = charset_decoder(payload)

                # otherwise, just try to convert
                else:
                    payload = u(payload)

            # remove any control characters not allowed in XML
            control_char_map = dict.fromkeys(range(32))
            for i in [9, 10, 13]: # preserve horizontal tab, line feed, carriage return
                del control_char_map[i]

            payload = u(payload).translate(control_char_map)

            result.single_body.body_content.content = payload

        else:
            # TODO: handle multipart
            logger.warn('CERP conversion does not yet handle multipart')

        # assume we've normalized newlines:
        result.eol = EOLMAP[os.linesep]

        return result