1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 This module contains the MailboxFactory class, together with some
22 Exceptions that are thrown by MailboxFactory, and the pathgenerator
23 functions used for the default mailbox types (see documentation
24 of MailboxFactory class)
25
26
27 """
28
29 import mailbox
30 from ConfigParser import ConfigParser
31
32 from ProcImap.ImapServer import ImapServer
33 from ProcImap.ImapMailbox import ImapMailbox
34
36 """ Raised when there is a mailbox type in the config file that is
37 unknown to MailboxFactory. You may have to add it first with the
38 'set_type' method.
39 """
40 pass
41
43 """ Raised when you supplied a factory that is not a subclass of
44 mailbox.Mailbox.
45 """
46 pass
47
49 """ Raised when you supplied a pathgenerator that is not callable """
50 pass
51
53 """ Raised if there are options missing in the config file that are
54 needed in order to produce a mailbox object
55 """
56 pass
57
59 """ MailboxFactory is a factory class for Mailbox objects. You can define
60 mailboxes of different types in an INI-style config file (the file
61 has to parsable by ConfigParser.ConfigParser; the exceptions defined
62 in ConfigParser may be thrown if the config file is not well-formed.)
63 Each section in the config file describes one mailbox.
64
65 An example of a valid config file 'mailboxes.cfg' is the following:
66
67 [Standard]
68 type = IMAP
69 mailbox = INBOX
70 server = mail.physik.fu-berlin.de
71 username = goerz
72 password = secret
73 ssl = True
74 port = 933
75
76 [Sent]
77 type = IMAP
78 mailbox = Sent
79 server = mail.physik.fu-berlin.de
80 username = goerz
81 password = secret
82
83 [Backup]
84 type = mbox
85 path = /home/goerz/Mail/backup.mbox
86
87 The type of the mailbox is described by the 'type' parameters. The
88 types known by default are 'imap', 'mbox', 'maildir', 'MH', 'Babyl',
89 and 'MMDF', all of which have corresponding subclasses of
90 mailbox.Mailbox (all except ImapMailbox are defined in the standard
91 library). The type specification is not case sensitive.
92
93 The remaining parameters in a specific section depend on the type. The
94 Mailbox classes from the standard library need only a path; IMAP needs
95 type, mailbox, server, username, and password. The ssl and port
96 parameters are optional. ssl is enabled by default; the port, if
97 unspecified, is the standard port (933 for ssl, 433 otherwise).
98
99 MailboxFactory has capabilities to extend the set of known types by
100 using the set_type method.
101
102 The MailboxFactory partly supports a read-only dictionary interface.
103 """
105 """ Initialize MailboxFactory files.
106 The mailbox objects that can be generated must be described in
107 configfilename.
108 """
109 self._types = {}
110 self.set_type('mbox', mailbox.mbox, standard_pathgenerator)
111 self.set_type('maildir', mailbox.Maildir, standard_pathgenerator)
112 self.set_type('mh', mailbox.MH, standard_pathgenerator)
113 self.set_type('babyl', mailbox.Babyl, standard_pathgenerator)
114 self.set_type('mmdf', mailbox.MMDF, standard_pathgenerator)
115 self.set_type('imap', ImapMailbox, imap_pathgenerator)
116 self._configparser = ConfigParser()
117 self._configparser.read(configfilename)
118
119 - def get(self, name):
120 """ Create the Mailbox object that is described in section 'name'
121 in the config file. For example,
122 >>> mailboxes = MailboxFactory("mailboxes.cfg")
123 >>> mb = mailboxes.get('Standard')
124 mb would now be an object of type ImapMailbox if mailboxes.cfg
125 contained the data as the example in the class docstring.
126 """
127 mailboxtype = self._configparser.get(name, 'type').lower()
128 if not mailboxtype in self._types.keys():
129 raise UnknownMailboxTypeError, "Unknown type: %s" % mailboxtype
130 factory, pathgenerator = self._types[mailboxtype]
131 path = pathgenerator(dict(self._configparser.items(name)))
132 return(factory(path))
133
135 """ Shorthand for the get method.
136 For example,
137 >>> mailboxes = MailboxFactory("mailboxes.cfg")
138 >>> mb = mailboxes['Standard']
139 """
140 return self.get(name)
141
142
144 """ Return an ImapServer instance from the server data that is
145 described in section 'name'. The section must have the form of
146 an imap mailbox (as described above). A TypeError will be raised
147 if the section is not of type IMAP. The 'mailbox' key is ignored.
148
149 For example, you could create an ImapServer like this:
150
151 >>> mailboxes = MailboxFactory("mailboxes.cfg")
152 >>> server = mailboxes.get_server('StandardServer')
153 """
154 mailboxtype = self._configparser.get(name, 'type').lower()
155 if mailboxtype != 'imap':
156 raise TypeError, "You can only create a server from an IMAP mailbox"
157 factory, pathgenerator = self._types[mailboxtype]
158 path = pathgenerator(dict(self._configparser.items(name)))
159 return(path[0])
160
162 """ Return True if there is a mailbox with the given name,
163 False otherwise """
164 return (name in self._configparser.sections())
165
166
168 """ List all mailboxes defined in the config file """
169 return self._configparser.sections()
170
171 - def data(self, name):
172 """ List all the data associated with the mailbox name """
173 return self._configparser.items(name)
174
175 - def set_type(self, typename, factory, pathgenerator):
176 """ Make a new typename of Mailbox known. This allows you to
177 handle new types of Mailbox objects beyond IMAP and the
178 mailboxes of the standard library.
179
180 factory is the class that generates the Mailbox object and must
181 be a subclass of mailbox.Mailbox
182
183 pathgenerator is a callable that receives a dict of options set
184 in a section of the config file, and returns the 'path' that
185 is passed as the first argument to the factory. For the standard
186 mailboxes of the standard library, the 'path' is just a string,
187 the path of the mailbox in the filesystem. For IMAP, the path
188 is a tuple (server, name). For new types, this may be anything.
189
190 For example the constructor of this class makes the 'mbox'
191 type known as:
192 self.set_type('mbox', mailbox.mbox, standard_pathgenerator)
193
194 In combination,
195 factory(pathgenerator(dict_of_options_in_configfile_section))
196 should create a Mailbox object of the appropriate type.
197 """
198 if not issubclass(factory, mailbox.Mailbox):
199 raise FactoryIsNotMailboxTypeError
200 if not callable(pathgenerator):
201 raise PathgeneratorNotCallableError
202 self._types[str(typename).lower()] = (factory, pathgenerator)
203
204
206 """ Converts options into (server, name) tuple """
207 try:
208 name = optionsdict['mailbox']
209 serveraddress = optionsdict['server']
210 username = optionsdict['username']
211 password = optionsdict['password']
212 ssl = True
213 if optionsdict.has_key('ssl'):
214 if optionsdict['ssl'].lower() in ['0', 'false', 'no']:
215 ssl = False
216 port = None
217 if optionsdict.has_key('port'):
218 port = int(optionsdict['port'])
219 except KeyError:
220 raise MailboxOptionsNotCompleteError, \
221 "IMAP Mailbox object needs the following parameters\n " \
222 + "'mailbox', 'server', 'username', 'password'.\n" \
223 + "The 'ssl' and 'port' parameters are optional."
224 server = ImapServer(serveraddress, username, password, ssl, port)
225 return(tuple((server, name)))
226
227
229 """ Extract 'path' from options """
230 try:
231 return optionsdict['path']
232 except KeyError:
233 raise MailboxOptionsNotCompleteError, \
234 "Standard Mailbox object needs 'path' parameter"
235