1# Copyright 2020 Patrick Ulbrich <zulu99@gmx.net> 2# Copyright 2016 Timo Kankare <timo.kankare@iki.fi> 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program; if not, write to the Free Software 16# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 17# MA 02110-1301, USA. 18# 19 20"""Implementation of local mailboxes, like mbox and maildir.""" 21 22import email 23import mailbox 24import logging 25import os.path 26 27from Mailnag.backends.base import MailboxBackend 28 29class MBoxBackend(MailboxBackend): 30 """Implementation of mbox mail boxes.""" 31 32 def __init__(self, name = '', path=None, **kw): 33 """Initialize mbox mailbox backend with a name and path.""" 34 self._name = name 35 self._path = path 36 self._opened = False 37 38 39 def open(self): 40 """'Open' mbox. (Actually just checks that mailbox file exists.)""" 41 if not os.path.isfile(self._path): 42 raise IOError('Mailbox {} does not exist.'.format(self._path)) 43 self._opened = True 44 45 46 def close(self): 47 """Close mbox.""" 48 self._opened = False 49 50 51 def is_open(self): 52 """Return True if mailbox is opened.""" 53 return self._opened 54 55 56 def list_messages(self): 57 """List unread messages from the mailbox. 58 Yields pairs (folder, message) where folder is always ''. 59 """ 60 mbox = mailbox.mbox(self._path, create=False) 61 folder = '' 62 try: 63 for msg in mbox: 64 if 'R' not in msg.get_flags(): 65 yield (folder, msg, {}) 66 finally: 67 mbox.close() 68 69 70 def request_folders(self): 71 """mbox does not suppoert folders.""" 72 raise NotImplementedError("mbox does not support folders") 73 74 75 def supports_mark_as_seen(self): 76 return False 77 78 79 def mark_as_seen(self, mails): 80 # TODO: local mailboxes should support this 81 raise NotImplementedError 82 83 84 def notify_next_change(self, callback=None, timeout=None): 85 raise NotImplementedError("mbox does not support notifications") 86 87 88 def cancel_notifications(self): 89 raise NotImplementedError("mbox does not support notifications") 90 91 92class MaildirBackend(MailboxBackend): 93 """Implementation of maildir mail boxes.""" 94 95 def __init__(self, name = '', path=None, folders=[], **kw): 96 """Initialize maildir mailbox backend with a name, path and folders.""" 97 self._name = name 98 self._path = path 99 self._folders = folders 100 self._opened = False 101 102 103 def open(self): 104 """'Open' mailbox. (Actually just checks that maildir directory exists.)""" 105 if not os.path.isdir(self._path): 106 raise IOError('Mailbox {} does not exist.'.format(self._path)) 107 self._opened = True 108 109 110 def close(self): 111 """Close mailbox.""" 112 self._opened = False 113 114 115 def is_open(self): 116 """Return True if mailbox is opened.""" 117 return self._opened 118 119 120 def list_messages(self): 121 """List unread messages from the mailbox. 122 Yields pairs (folder, message). 123 """ 124 folders = self._folders if len(self._folders) != 0 else [''] 125 root_maildir = mailbox.Maildir(self._path, factory=None, create=False) 126 try: 127 for folder in folders: 128 maildir = self._get_folder(root_maildir, folder) 129 for msg in maildir: 130 if 'S' not in msg.get_flags(): 131 yield folder, msg 132 finally: 133 root_maildir.close() 134 135 136 def request_folders(self): 137 """Lists folders from maildir.""" 138 maildir = mailbox.Maildir(self._path, factory=None, create=False) 139 try: 140 return [''] + maildir.list_folders() 141 finally: 142 maildir.close() 143 144 145 def notify_next_change(self, callback=None, timeout=None): 146 raise NotImplementedError("maildir does not support notifications") 147 148 149 def cancel_notifications(self): 150 raise NotImplementedError("maildir does not support notifications") 151 152 153 def _get_folder(self, maildir, folder): 154 """Returns folder instance of the given maildir.""" 155 if folder == '': 156 return maildir 157 else: 158 return maildir.get_folder(folder) 159 160