comparison src/plugins/plugin_misc_maildir.py @ 254:9fc32d1d9046

Plugin IMAP, plugin MAILDIR: added IMAP's UID management, mailbox data persistence
author Goffi <goffi@goffi.org>
date Mon, 17 Jan 2011 04:23:31 +0100
parents f45ffbf211e9
children 55b750017b71
comparison
equal deleted inserted replaced
253:f45ffbf211e9 254:9fc32d1d9046
60 info(_("Plugin Maildir initialization")) 60 info(_("Plugin Maildir initialization"))
61 self.host = host 61 self.host = host
62 62
63 self.__observed={} 63 self.__observed={}
64 self.__mailboxes={} 64 self.__mailboxes={}
65 self.data=host.memory.getPrivate("MAILDIR_data") or {"INBOX":{"cur_idx":0}}
66 #a value in the dictionnary for a mailbox is a dictionnary with the following value
67 # - cur_idx: value of the current unique integer increment (UID)
68 # - message_id (as returned by MaildirMailbox): a tuple of (UID, [flag1, flag2, ...])
65 69
66 #the trigger 70 #the trigger
67 host.trigger.add("MessageReceived", self.MessageReceivedTrigger) 71 host.trigger.add("MessageReceived", self.MessageReceivedTrigger)
72
73 def __destroy__(self):
74 debug('Destroying MaildirBox')
75 self.host.memory.setPrivate('MAILDIR_data',self.data)
68 76
69 def accessMessageBox(self, boxname, observer=None): 77 def accessMessageBox(self, boxname, observer=None):
70 """Create and return a MailboxUser instance 78 """Create and return a MailboxUser instance
71 @param boxname: name of the box 79 @param boxname: name of the box
72 @param observer: method to call when a NewMessage arrive""" 80 @param observer: method to call when a NewMessage arrive"""
93 @param boxname: name of the box to check 101 @param boxname: name of the box to check
94 @return: MailboxUser instance or None""" 102 @return: MailboxUser instance or None"""
95 if self.__mailboxes.has_key(boxname): 103 if self.__mailboxes.has_key(boxname):
96 return self.__mailboxes[boxname] 104 return self.__mailboxes[boxname]
97 105
98 106 def getUid(self, boxname, message_id):
107 """Return an unique integer, always ascending, for a message
108 This is mainly needed for the IMAP protocol
109 @param boxname: name of the box where the message is
110 @param message_id: unique id of the message as given by MaildirMailbox
111 @return: Integer UID"""
112 try:
113 box_data = self.data[boxname] #the boxname MUST exist in the data
114 except KeyError:
115 err_msg=_("Boxname doesn't exist in internal data")
116 error(_("INTERNAL ERROR: ") + err_msg)
117 raise MaildirError(err_msg)
118 if box_data.has_key(message_id):
119 ret = box_data[message_id][0]
120 else:
121 box_data['cur_idx']+=1
122 box_data[message_id]=(box_data['cur_idx'],[])
123 ret = box_data[message_id]
124 self.host.memory.setPrivate('MAILDIR_data',self.data)
125 return ret
126
127
128 def getNextUid(self, boxname):
129 """Return next unique integer that will generated
130 This is mainly needed for the IMAP protocol
131 @param boxname: name of the box where the message is
132 @return: Integer UID"""
133 try:
134 box_data = self.data[boxname] #the boxname MUST exist in the data
135 except KeyError:
136 err_msg=_("Boxname doesn't exist in internal data")
137 error(_("INTERNAL ERROR: ") + err_msg)
138 raise MaildirError(err_msg)
139 return box_data['cur_idx']+1
140
141 def getNextExistingUid(self, boxname, uid):
142 """Give the next uid of existing message
143 @param boxname: name of the box where the message is
144 @param uid: uid to start from
145 @return: uid or None if the is no more message"""
146 try:
147 box_data = self.data[boxname] #the boxname MUST exist in the data
148 except KeyError:
149 err_msg=_("Boxname doesn't exist in internal data")
150 error(_("INTERNAL ERROR: ") + err_msg)
151 raise MaildirError(err_msg)
152 idx=uid+1
153 while self.getIdfromUid(boxname, idx) == None: #TODO: this is highly inefficient because getIdfromUid is inefficient, fix this
154 idx+=1
155 if idx>box_data['cur_idx']:
156 return None
157 return idx
158
159 def getMaxUid(self, boxname):
160 """Give the max existing uid
161 @param boxname: name of the box where the message is
162 @return: uid"""
163 try:
164 box_data = self.data[boxname] #the boxname MUST exist in the data
165 except KeyError:
166 err_msg=_("Boxname doesn't exist in internal data")
167 error(_("INTERNAL ERROR: ") + err_msg)
168 raise MaildirError(err_msg)
169 return box_data['cur_idx']
170
171
172 def getIdfromUid(self, boxname, message_uid):
173 """Return the message unique id from it's integer UID
174 @param boxname: name of the box where the message is
175 @param message_uid: unique integer identifier
176 @return: unique id of the message as given by MaildirMailbox or None if not found"""
177 try:
178 box_data = self.data[boxname] #the boxname MUST exist in the data
179 except KeyError:
180 err_msg=_("Boxname doesn't exist in internal data")
181 error(_("INTERNAL ERROR: ") + err_msg)
182 raise MaildirError(err_msg)
183
184 for message_id in box_data.keys(): #TODO: this is highly inefficient on big mailbox, must be replaced in the future
185 if message_id == 'cur_idx':
186 continue
187 if box_data[message_id][0] == message_uid:
188 return message_id
189 return None
190
191 def cleanTable(self, boxname, existant_id):
192 """Remove mails which no longuer exist from the table
193 @param boxname: name of the box to clean
194 @param existant_id: list of id which actually exist"""
195 try:
196 box_data = self.data[boxname] #the boxname MUST exist in the data
197 except KeyError:
198 err_msg=_("Boxname doesn't exist in internal data")
199 error(_("INTERNAL ERROR: ") + err_msg)
200 raise MaildirError(err_msg)
201 to_remove=[]
202 for key in box_data:
203 if key not in existant_id and key!="cur_idx":
204 to_remove.append(key)
205 for key in to_remove:
206 del box_data[key]
207
99 208
100 def MessageReceivedTrigger(self, message): 209 def MessageReceivedTrigger(self, message):
101 """This trigger catch normal message and put the in the Maildir box. 210 """This trigger catch normal message and put the in the Maildir box.
102 If the message is not of "normal" type, do nothing 211 If the message is not of "normal" type, do nothing
103 @param message: message xmlstrem 212 @param message: message xmlstrem
185 self.maildir=_maildir 294 self.maildir=_maildir
186 mailbox_path = os.path.expanduser(os.path.join(self.maildir.host.get_const('local_dir'), MAILDIR_PATH)) 295 mailbox_path = os.path.expanduser(os.path.join(self.maildir.host.get_const('local_dir'), MAILDIR_PATH))
187 self.mailbox_path=mailbox_path 296 self.mailbox_path=mailbox_path
188 self.mailbox = maildir.MaildirMailbox(mailbox_path) 297 self.mailbox = maildir.MaildirMailbox(mailbox_path)
189 self.observer=observer 298 self.observer=observer
299 self.__uid_table_update()
300
301
190 if observer: 302 if observer:
191 debug("adding observer for %s" % name) 303 debug("adding observer for %s" % name)
192 self.maildir.addObserver(observer, name, "NEW_MESSAGE") 304 self.maildir.addObserver(observer, name, "NEW_MESSAGE")
305
306 def __uid_table_update(self):
307 existant_id=[]
308 for mess_idx in range (self.getMessageCount()):
309 #we update the uid table
310 existant_id.append(self.getId(mess_idx))
311 self.getUid(mess_idx)
312 self.maildir.cleanTable(self.name, existant_id)
313
193 314
194 def __destroy__(self): 315 def __destroy__(self):
195 if observer: 316 if observer:
196 debug("removing observer for %s" % self.name) 317 debug("removing observer for %s" % self.name)
197 self._maildir.removeObserver(observer, self.name, "NEW_MESSAGE") 318 self._maildir.removeObserver(observer, self.name, "NEW_MESSAGE")
202 @param message: XMPP XML message""" 323 @param message: XMPP XML message"""
203 self.mailbox.appendMessage(self.xmppMessage2mail(message)).addCallback(self.emitSignal, "NEW_MESSAGE") 324 self.mailbox.appendMessage(self.xmppMessage2mail(message)).addCallback(self.emitSignal, "NEW_MESSAGE")
204 325
205 def emitSignal(self, ignore, signal): 326 def emitSignal(self, ignore, signal):
206 """Emit the signal to the observers""" 327 """Emit the signal to the observers"""
207 print ('self: %s, mailbox: %s, count: %i' % (self, self.mailbox, self.getMessageCount())) 328 self.getUid(self.getMessageCount()-1) #we make an uid for the last message added
208 self.maildir.emitSignal(self.name, signal) 329 self.maildir.emitSignal(self.name, signal)
209 330
210 def getId(self, mess_idx): 331 def getId(self, mess_idx):
211 """Return the Unique ID of the message 332 """Return the Unique ID of the message
212 @mess_idx: message index""" 333 @mess_idx: message index"""
213 return self.mailbox.getUidl(mess_idx) 334 return self.mailbox.getUidl(mess_idx)
214 335
336 def getUid(self, mess_idx):
337 """Return a unique interger id for the message, always ascending"""
338 mess_id=self.getId(mess_idx)
339 return self.maildir.getUid(self.name,mess_id)
340
341 def getNextUid(self):
342 return self.maildir.getNextUid(self.name)
343
344 def getNextExistingUid(self, uid):
345 return self.maildir.getNextExistingUid(self.name, uid)
346
347 def getMaxUid(self):
348 return self.maildir.getMaxUid(self.name)
349
215 def getMessageCount(self): 350 def getMessageCount(self):
216 """Return number of mails present in this box""" 351 """Return number of mails present in this box"""
217 print "count: %i" % len(self.mailbox.listMessages()) 352 return len(self.mailbox.list)
218 return len(self.mailbox.listMessages()) 353
219 354 def getMessageIdx(self, mess_idx):
355 """Return the full message
356 @mess_idx: message index"""
357 return self.mailbox.getMessage(mess_idx)
358
220 def getMessage(self, mess_idx): 359 def getMessage(self, mess_idx):
221 """Return the full message 360 """Return the full message
222 @mess_idx: message index""" 361 @mess_idx: message index"""
223 return self.mailbox.getMessage(mess_idx) 362 return self.mailbox.getMessage(mess_idx)
363
364 def getMessageUid(self, mess_uid):
365 """Return the full message
366 @mess_idx: message unique identifier"""
367 for mess_idx in range (self.getMessageCount()):
368 if self.getUid(mess_idx) == mess_uid:
369 return self.mailbox.getMessage(mess_idx)
370 raise IndexError