'This is a simple mailbox polling script for the Messaging Module.
It is normally called from the scheduler.
@ToDo: If there is a need to collect from non-compliant mailers
then suggest using the robust Fetchmail to collect & store
in a more compliant mailer!
@ToDo: If delete_from_server is false, we don\'t want to download the
same messages repeatedly.  Perhaps record time of fetch runs
(or use info from the scheduler_run table), compare w/ message
timestamp, as a filter. That may not be completely accurate,
so could check msg_email for messages close to the last
fetch time. Or just advise people to have a dedicated account
to which email is sent, that does not also need to be read
by humans. Or don\'t delete the fetched mail until the next run.'

@staticmethod
def poll_email(channel_id):

    db = current.db
    s3db = current.s3db
    table = s3db.msg_email_channel
    query = (table.channel_id == channel_id)
    channel = db(query).select(table.username, table.password, table.server, table.protocol, table.use_ssl, table.port, table.delete_from_server, limitby=(0, 1)).first()
    if (not channel):
        return ('No    Such    Email    Channel:    %s' % channel_id)
    import email
    import socket
    from dateutil import parser
    date_parse = parser.parse
    username = channel.username
    password = channel.password
    host = channel.server
    protocol = channel.protocol
    ssl = channel.use_ssl
    port = int(channel.port)
    delete = channel.delete_from_server
    mtable = db.msg_email
    minsert = mtable.insert
    stable = db.msg_channel_status
    sinsert = stable.insert
    atable = s3db.msg_attachment
    ainsert = atable.insert
    dtable = db.doc_document
    dinsert = dtable.insert
    store = dtable.file.store
    update_super = s3db.update_super
    parser = s3db.msg_parser_enabled(channel_id)
    if parser:
        ptable = db.msg_parsing_status
        pinsert = ptable.insert
    def parse_email(message):
        '\n                                                                Helper    to    parse    the    mail\n                                                '
        msg = email.message_from_string(message)
        sender = msg['from']
        subject = msg.get('subject', '')
        date_sent = msg.get('date', None)
        raw = msg.as_string()
        attachments = []
        body = ''
        for part in msg.walk():
            if (part.get_content_maintype() == 'multipart'):
                continue
            filename = part.get_filename()
            if (not filename):
                if (not body):
                    body = part.get_payload(decode=True)
                continue
            attachments.append((filename, part.get_payload(decode=True)))
        data = dict(channel_id=channel_id, from_address=sender, subject=subject[:78], body=body, raw=raw, inbound=True)
        if date_sent:
            data['date'] = date_parse(date_sent)
        _id = minsert(**data)
        record = dict(id=_id)
        update_super(mtable, record)
        message_id = record['message_id']
        for a in attachments:
            filename = s3_unicode(a[0][:92]).encode('ascii', 'ignore')
            fp = StringIO()
            fp.write(a[1])
            fp.seek(0)
            newfilename = store(fp, filename)
            fp.close()
            document_id = dinsert(name=filename, file=newfilename)
            update_super(dtable, dict(id=document_id))
            ainsert(message_id=message_id, document_id=document_id)
        if parser:
            pinsert(message_id=message_id, channel_id=channel_id)
    dellist = []
    if (protocol == 'pop3'):
        import poplib
        try:
            if ssl:
                p = poplib.POP3_SSL(host, port)
            else:
                p = poplib.POP3(host, port)
        except socket.error as e:
            error = ('Cannot    connect:    %s' % e)
            current.log.error(error)
            sinsert(channel_id=channel_id, status=error)
            return error
        except poplib.error_proto as e:
            current.log.error(('Email    poll    failed:    %s' % e))
            return
        try:
            p.apop(username, password)
        except poplib.error_proto:
            try:
                p.user(username)
                p.pass_(password)
            except poplib.error_proto as e:
                error = ('Login    failed:    %s' % e)
                current.log.error(error)
                sinsert(channel_id=channel_id, status=error)
                return error
        mblist = p.list()[1]
        for item in mblist:
            (number, octets) = item.split('    ')
            lines = p.retr(number)[1]
            parse_email('\n'.join(lines))
            if delete:
                dellist.append(number)
        for number in dellist:
            p.dele(number)
        p.quit()
    elif (protocol == 'imap'):
        import imaplib
        try:
            if ssl:
                M = imaplib.IMAP4_SSL(host, port)
            else:
                M = imaplib.IMAP4(host, port)
        except socket.error as e:
            error = ('Cannot    connect:    %s' % e)
            current.log.error(error)
            sinsert(channel_id=channel_id, status=error)
            return error
        try:
            M.login(username, password)
        except M.error as e:
            error = ('Login    failed:    %s' % e)
            current.log.error(error)
            sinsert(channel_id=channel_id, status=error)
            db.commit()
            return error
        M.select()
        (typ, data) = M.search(None, 'ALL')
        mblist = data[0].split()
        for number in mblist:
            (typ, msg_data) = M.fetch(number, '(RFC822)')
            for response_part in msg_data:
                if isinstance(response_part, tuple):
                    parse_email(response_part[1])
                    if delete:
                        dellist.append(number)
        for number in dellist:
            (typ, response) = M.store(number, '+FLAGS', '(\\Deleted)')
        M.close()
        M.logout()
