summaryrefslogtreecommitdiff
path: root/import_emails.py
blob: dee1e016abc8da4b3e32f4a645cc1f6d6910bf0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/python

from bin import django_setup, add_logging_arguments

import email
import logging
import imaplib

django_setup()  # must be called to get sys.path and django settings in place

from django.db import IntegrityError
from django.conf import settings
from django.utils.module_loading import import_string

from patchwork import parser
from patchwork.models import Patch, State

import patch_matcher

log = logging.getLogger('import_emails')


def find_old_revisions(patch):
    log.debug('looking for old versions of patch %d', patch.id)
    it = patch_matcher.get_patches_matching(
        patch.project, [patch.submitter], patch.name, patch.diff)
    for p in it:
        # skip ourself
        if p.id != patch.id:
            yield p


def process_message(msg):
    msg = email.message_from_string(msg)
    log.info('subject: %s', msg.get('Subject'))
    p = None
    try:
        p = parser.parse_mail(msg)
    except IntegrityError as e:
        log.info(e)
    if p:
        log.info('saved mail: %d', p.id)
        for patch in Patch.objects.filter(msgid=p.msgid):
            for old in find_old_revisions(patch):
                log.info('marking patch %d as superseded by %d',
                         old.id, patch.id)
                old.state = State.objects.get(name='Superseded')
                old.save()
    return p


def move_message(mail, uid, folder):
    # put in proper folder and move out of inbox
    status, _ = mail.uid('copy', uid, folder)
    assert status == 'OK'
    status, _ = mail.uid('store', uid, '+FLAGS', r'(\Deleted)')
    assert status == 'OK'


def process_inbox(mail, max_messages=0):
    status, _ = mail.select('inbox')
    assert status == 'OK'
    status, uids = mail.uid('search', None, 'ALL')
    assert status == 'OK'
    uids = uids[0].split()
    if max_messages > 0:
        uids = uids[:max_messages]
    log.info('processing %d messages', len(uids))
    added = processed = 0
    for uid in uids:
        try:
            log.info('processing message: %s', uid)
            status, data = mail.uid('fetch', uid, '(RFC822)')
            assert status == 'OK'
            if process_message(data[0][1]):
                added += 1
            move_message(mail, uid, 'processed')
            processed += 1
        except Exception:
            log.exception('Unknown error for %s, moving to retry folder', uid)
            move_message(mail, uid, 'retry')
            break
    log.info('processed %d messages, added %d', processed, added)


def get_monkey_patcher():
    p = getattr(settings, 'PARSEMAIL_MONKEY_PATCHER', None)
    if p:
        return import_string(p)


if __name__ == '__main__':
    import argparse

    arg_parser = argparse.ArgumentParser(
        description='Check configured inbox for new patches to import')
    arg_parser.add_argument('--num-emails', type=int, default=80,
                            help='''Maximum number of emails to analyze.
                                 default=%(default)d''')
    add_logging_arguments(arg_parser)
    args = arg_parser.parse_args()
    parser.logger = log

    mail = imaplib.IMAP4_SSL(settings.IMAP_SERVER)
    status, _ = mail.login(settings.IMAP_USER, settings.IMAP_PASS)
    assert status == 'OK'

    monkey_patcher = get_monkey_patcher()
    if monkey_patcher:
        with monkey_patcher(parser):
            process_inbox(mail, args.num_emails)
    else:
        process_inbox(mail, args.num_emails)