1
2
3
4
5
6
7 import os
8 from twisted.python import log
9 from twisted.application import service, internet
10 from twisted.internet import reactor
11 dnotify = None
12 try:
13 import dnotify
14 except:
15
16 log.msg("unable to import dnotify, so Maildir will use polling instead")
17
20
22 """I watch a maildir for new messages. I should be placed as the service
23 child of some MultiService instance. When running, I use the linux
24 dirwatcher API (if available) or poll for new files in the 'new'
25 subdirectory of my maildir path. When I discover a new message, I invoke
26 my .messageReceived() method with the short filename of the new message,
27 so the full name of the new file can be obtained with
28 os.path.join(maildir, 'new', filename). messageReceived() should be
29 overridden by a subclass to do something useful. I will not move or
30 delete the file on my own: the subclass's messageReceived() should
31 probably do that.
32 """
33 pollinterval = 10
34
36 """Create the Maildir watcher. BASEDIR is the maildir directory (the
37 one which contains new/ and tmp/)
38 """
39 service.MultiService.__init__(self)
40 self.basedir = basedir
41 self.files = []
42 self.dnotify = None
43
50
52 service.MultiService.startService(self)
53 self.newdir = os.path.join(self.basedir, "new")
54 if not os.path.isdir(self.basedir) or not os.path.isdir(self.newdir):
55 raise NoSuchMaildir("invalid maildir '%s'" % self.basedir)
56 try:
57 if dnotify:
58
59
60 self.dnotify = dnotify.DNotify(self.newdir,
61 self.dnotify_callback,
62 [dnotify.DNotify.DN_CREATE])
63 except (IOError, OverflowError):
64
65
66
67 log.msg("DNotify failed, falling back to polling")
68 if not self.dnotify:
69 t = internet.TimerService(self.pollinterval, self.poll)
70 t.setServiceParent(self)
71 self.poll()
72
74 log.msg("dnotify noticed something, now polling")
75
76
77
78
79
80
81
82
83
84
85 reactor.callLater(0.1, self.poll)
86
87
89 if self.dnotify:
90 self.dnotify.remove()
91 self.dnotify = None
92 return service.MultiService.stopService(self)
93
95 assert self.basedir
96
97 for f in self.files:
98 if not os.path.isfile(os.path.join(self.newdir, f)):
99 self.files.remove(f)
100 newfiles = []
101 for f in os.listdir(self.newdir):
102 if not f in self.files:
103 newfiles.append(f)
104 self.files.extend(newfiles)
105
106
107 for n in newfiles:
108
109 self.messageReceived(n)
110
112 """Called when a new file is noticed. Will call
113 self.parent.messageReceived() with a path relative to maildir/new.
114 Should probably be overridden in subclasses."""
115 self.parent.messageReceived(filename)
116