summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-04-07 19:12:33 +0300
committerLars Wirzenius <liw@liw.fi>2017-04-07 19:12:33 +0300
commit77fc9ad2250bc5de1a28427aa0c2b2213b6fc75b (patch)
treeb7eb8f66da8e3ee88bd9ec458c6f20b9c88fd75c
parent9424547e0483041cb474ee7bc438d9d5f98f33f1 (diff)
downloaddistix-77fc9ad2250bc5de1a28427aa0c2b2213b6fc75b.tar.gz
Add import-imap subcommand
No tests, too hard to mock an IMAP server.
-rw-r--r--distixlib/plugins/import_mail_plugin.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/distixlib/plugins/import_mail_plugin.py b/distixlib/plugins/import_mail_plugin.py
index 9e29503..f58c1b0 100644
--- a/distixlib/plugins/import_mail_plugin.py
+++ b/distixlib/plugins/import_mail_plugin.py
@@ -18,8 +18,10 @@
import contextlib
import email
+import imaplib
import mailbox
import os
+import re
import cliapp
import ttystatus
@@ -54,6 +56,27 @@ class ImportMailPlugin(cliapp.Plugin):
'import-maildir', self.import_maildir,
arg_synopsis='REPO MAILDIR')
+ self.app.add_subcommand(
+ 'import-imap', self.import_imap,
+ arg_synopsis='[KEY=VALUE]')
+
+ self.app.settings.string(
+ ['imap-server'],
+ 'which IMAP server to import from',
+ metavar='ADDR')
+
+ self.app.settings.string(
+ ['imap-username'],
+ 'username on IMAP server')
+
+ self.app.settings.string(
+ ['imap-password-cmd'],
+ 'shell command to run to get password on IMAP server')
+
+ self.app.settings.string(
+ ['local-repos'],
+ 'where local repos should be when importing from IMAP')
+
def import_mail(self, args):
repo_dirname, mail_filename, keyvalue = self._parse_command_line(args)
key, value = self._parse_keyvalue(keyvalue)
@@ -76,6 +99,58 @@ class ImportMailPlugin(cliapp.Plugin):
return mailbox.Maildir(filename, factory=None)
self._import_folder(args, maildir_factory)
+ def import_imap(self, args):
+ if args:
+ key, value = self._parse_keyvalue(args[0])
+ else:
+ key, value = None, None
+
+ collection = _RepoCollection(self.app.settings['local-repos'])
+
+ imap_server = self.app.settings['imap-server']
+ username = self.app.settings['imap-username']
+ password_cmd = self.app.settings['imap-password-cmd']
+ password = cliapp.runcmd(['sh', '-c', password_cmd]).strip()
+
+ cp = self.app.settings.as_cp()
+ repo_rules = _RepoRules()
+ repo_rules.add_rules(cp)
+
+ imap = imaplib.IMAP4_SSL(imap_server)
+ imap.login(username, password)
+ imap.select('INBOX')
+
+ _, data = imap.search(None, 'ALL')
+ for msgnum in data[0].split():
+ _, data = imap.fetch(msgnum, '(RFC822)')
+ _, text = data[0]
+ repo_url = repo_rules.find_url(text)
+ if repo_url:
+ msg = email.message_from_string(text)
+ self._import_msg_into_repo(
+ collection, repo_url, msg, key, value)
+ imap.store(msgnum, '+FLAGS', '(\\Deleted)')
+
+ imap.expunge()
+ imap.close()
+ imap.logout()
+
+ def _import_msg_into_repo(self, collection, repo_url, msg, key, value):
+ if collection.is_local(repo_url):
+ collection.pull(repo_url)
+ else:
+ collection.clone(repo_url)
+ localdir = collection.localdir(repo_url)
+
+ context = _ImportContext()
+ context.set_repo(distixlib.Repository(localdir))
+ context.repo.require_clean_working_tree()
+ context.all_ticket_ids = context.store.get_ticket_ids()
+
+ filenames = self._import_msg_to_ticket_store(context, msg, key, value)
+ if filenames:
+ context.repo.commit_changes(filenames, self.commit_msg)
+
def _import_folder(self, args, folder_factory):
repo_dirname, folder_filename, keyvalue = self._parse_command_line(
args)
@@ -276,3 +351,54 @@ class _QuietProgressReporter(object):
def __exit__(self, *args):
pass
+
+
+class _RepoCollection(object):
+
+ def __init__(self, locals_dir):
+ self._locals_dir = locals_dir
+
+ def is_local(self, repo_url):
+ localdir = self.localdir(repo_url)
+ return os.path.exists(localdir)
+
+ def clone(self, repo_url):
+ localdir = self.localdir(repo_url)
+ cliapp.runcmd(['git', 'clone', repo_url, localdir])
+ cliapp.runcmd(
+ ['git', 'branch', '-u', 'origin', 'master'],
+ cwd=localdir)
+
+ def pull(self, repo_url):
+ localdir = self.localdir(repo_url)
+ cliapp.runcmd(['git', 'pull', '--rebase'], cwd=localdir)
+
+ def localdir(self, url):
+ dirname = self._dir_for_url(url)
+ return os.path.join(self._locals_dir, dirname)
+
+ def _dir_for_url(self, repo_url):
+ s = repo_url
+ s = '_'.join(s.split('/'))
+ return s
+
+
+class _RepoRules(object):
+
+ def __init__(self):
+ self._rules = []
+
+ def add_rules(self, cp):
+ for section in cp.sections():
+ if section.startswith('distix:'):
+ url = section[len('distix:'):].strip()
+ pattern = cp.get(section, 'pattern')
+ self._rules.append((url, pattern))
+
+ def find_url(self, msg_text):
+ msg_text = ''.join(msg_text.split('\r'))
+ for url, pattern in self._rules:
+ m = re.search(pattern, msg_text, re.M | re.I)
+ if m is not None:
+ return url
+ return None