1#  GNU Mailutils -- a suite of utilities for electronic mail
2#  Copyright (C) 2009-2021 Free Software Foundation, Inc.
3#
4#  This library is free software; you can redistribute it and/or
5#  modify it under the terms of the GNU Lesser General Public
6#  License as published by the Free Software Foundation; either
7#  version 3 of the License, or (at your option) any later version.
8#
9#  This library 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 GNU
12#  Lesser General Public License for more details.
13#
14#  You should have received a copy of the GNU Lesser General
15#  Public License along with this library.  If not, see
16#  <http://www.gnu.org/licenses/>.
17
18from mailutils.c_api import mailbox
19from mailutils import message
20from mailutils import folder
21from mailutils import url
22from mailutils.error import MailboxError
23
24class MailboxBase:
25    def open (self, mode=0):
26        """Open the connection.
27
28        'mode' may be a string, consisting of the characters described
29        below, giving the access mode for the mailbox.
30
31        mode   Meaning
32        --------------------------------------------------------
33        r      Open for reading.
34        w      Open for writing.
35        a      Open for appending to the end of the mailbox.
36        c      Create the mailbox if it does not exist.
37
38        """
39        if isinstance (mode, str):
40            from mailutils import stream
41            flags = 0
42            for m in mode:
43                if m == 'r':
44                    flags = flags | stream.MU_STREAM_READ
45                elif m == 'w':
46                    flags = flags | stream.MU_STREAM_WRITE
47                elif m == 'a':
48                    flags = flags | stream.MU_STREAM_APPEND
49                elif m == 'c':
50                    flags = flags | stream.MU_STREAM_CREAT
51            if flags & stream.MU_STREAM_READ and flags & stream.MU_STREAM_WRITE:
52                flags = (flags & ~(stream.MU_STREAM_READ | \
53                                   stream.MU_STREAM_WRITE)) | \
54                                   stream.MU_STREAM_RDWR
55            mode = flags
56        status = mailbox.open (self.mbox, mode)
57        if status:
58            raise MailboxError (status)
59
60    def close (self):
61        """Close the connection."""
62        status = mailbox.close (self.mbox)
63        if status:
64            raise MailboxError (status)
65
66    def flush (self, expunge=False):
67        """Flush the mailbox."""
68        status = mailbox.flush (self.mbox, expunge)
69        if status:
70            raise MailboxError (status)
71
72    def messages_count (self):
73        """Return the number of messages in mailbox."""
74        status, total = mailbox.messages_count (self.mbox)
75        if status:
76            raise MailboxError (status)
77        return total
78
79    def messages_recent (self):
80        """Return the number of recent messages in mailbox."""
81        status, recent = mailbox.messages_recent (self.mbox)
82        if status:
83            raise MailboxError (status)
84        return recent
85
86    def message_unseen (self):
87        """Return the number of first unseen message in mailbox."""
88        status, recent = mailbox.message_unseen (self.mbox)
89        if status:
90            raise MailboxError (status)
91        return unseen
92
93    def get_message (self, msgno):
94        """Retrieve message number 'msgno'."""
95        status, c_msg = mailbox.get_message (self.mbox, msgno)
96        if status:
97            raise MailboxError (status)
98        return message.Message (c_msg)
99
100    def append_message (self, msg):
101        """Append 'msg' to the mailbox."""
102        status = mailbox.append_message (self.mbox, msg.msg)
103        if status:
104            raise MailboxError (status)
105
106    def expunge (self):
107        """Remove all messages marked for deletion."""
108        status = mailbox.expunge (self.mbox)
109        if status:
110            raise MailboxError (status)
111
112    def sync (self):
113        """Synchronize the mailbox."""
114        status = mailbox.sync (self.mbox)
115        if status:
116            raise MailboxError (status)
117
118    def get_uidls (self):
119        """Get UIDL list."""
120        status, uidls = mailbox.get_uidls (self.mbox)
121        if status:
122            raise MailboxError (status)
123        return uidls
124
125    def lock (self):
126        """Lock the mailbox."""
127        status = mailbox.lock (self.mbox)
128        if status:
129            raise MailboxError (status)
130
131    def unlock (self):
132        """Unlock the mailbox."""
133        status = mailbox.unlock (self.mbox)
134        if status:
135            raise MailboxError (status)
136
137    def get_size (self):
138        """Return the mailbox size."""
139        status, size = mailbox.get_size (self.mbox)
140        if status:
141            raise MailboxError (status)
142        return size
143
144    def get_folder (self):
145        """Get the Folder object."""
146        status, fld = mailbox.get_folder (self.mbox)
147        if status:
148            raise MailboxError (status)
149        return folder.Folder (fld)
150
151    def get_url (self):
152        """Get the Url object."""
153        status, u = mailbox.get_url (self.mbox)
154        if status:
155            raise MailboxError (status)
156        return url.Url (u)
157
158    def __next__ (self):
159        if self.__count >= self.__len:
160            self.__count = 0
161            raise StopIteration
162        else:
163            self.__count += 1
164            return self.get_message (self.__count)
165
166    def __getitem__ (self, msgno):
167        return self.get_message (msgno)
168
169    def __iter__ (self):
170        self.__count = 0
171        self.__len = self.messages_count ()
172        return self
173
174    def __getattr__ (self, name):
175        if name == 'size':
176            return self.get_size ()
177        elif name == 'folder':
178            return self.get_folder ()
179        elif name == 'url':
180            return self.get_url ()
181        else:
182            raise AttributeError(name)
183
184    def __len__ (self):
185        return self.messages_count ()
186
187    def __str__ (self):
188        return '<Mailbox %s (%d)>' % (self.get_url (), self.messages_count ())
189
190class Mailbox (MailboxBase):
191    __owner = False
192    def __init__ (self, name):
193        if isinstance (name, mailbox.MailboxType):
194            self.mbox = name
195        else:
196            self.mbox = mailbox.MailboxType ()
197            self.__owner = True
198            status = mailbox.create (self.mbox, name)
199            if status:
200                raise MailboxError (status)
201
202    def __del__ (self):
203        if self.__owner:
204            mailbox.destroy (self.mbox)
205        del self.mbox
206
207class MailboxDefault (MailboxBase):
208    def __init__ (self, name=None):
209        """MailboxDefault creates a Mailbox object for the supplied
210        mailbox 'name'. Before creating, the name is expanded using
211        the rules below:
212
213        %           --> system mailbox for the real uid
214        %user       --> system mailbox for the given user
215        ~/file      --> /home/user/file
216        ~user/file  --> /home/user/file
217        +file       --> /home/user/Mail/file
218        =file       --> /home/user/Mail/file
219        """
220        self.mbox = mailbox.MailboxType ()
221        status = mailbox.create_default (self.mbox, name)
222        if status:
223            raise MailboxError (status)
224
225    def __del__ (self):
226        mailbox.destroy (self.mbox)
227        del self.mbox
228