1 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
2 # This module is free software, and you may redistribute it and/or modify
3 # under the same terms as Python, so long as this copyright message and
4 # disclaimer are retained in their original form.
5 #
6 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
7 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
8 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
9 # POSSIBILITY OF SUCH DAMAGE.
10 #
11 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
12 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
14 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
15 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
16 #
17 # $Id: roundup_mailgw.py,v 1.3 2002-03-14 23:59:24 richard Exp $
19 # python version check
20 from roundup import version_check
22 import sys, os, re, cStringIO
24 from roundup.mailgw import Message
25 from roundup.i18n import _
27 def do_pipe(handler):
28 '''Read a message from standard input and pass it to the mail handler.
29 '''
30 handler.main(sys.stdin)
31 return 0
33 def do_mailbox(handler, filename):
34 '''Read a series of messages from the specified unix mailbox file and
35 pass each to the mail handler.
36 '''
37 # open the spool file and lock it
38 import fcntl, FCNTL
39 f = open(filename, 'r+')
40 fcntl.flock(f.fileno(), FCNTL.LOCK_EX)
42 # handle and clear the mailbox
43 try:
44 from mailbox import UnixMailbox
45 mailbox = UnixMailbox(f, factory=Message)
46 # grab one message
47 message = mailbox.next()
48 while message:
49 # call the instance mail handler
50 handler.handle_Message(message)
51 message = mailbox.next()
52 # nuke the file contents
53 os.ftruncate(f.fileno(), 0)
54 except:
55 import traceback
56 traceback.print_exc()
57 return 1
58 fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
59 return 0
61 def do_pop(handler, server, user='', password=''):
62 '''Read a series of messages from the specified POP server.
63 '''
64 import getpass, poplib, socket
65 try:
66 if not user:
67 user = raw_input(_('User: '))
68 if not password:
69 password = getpass.getpass()
70 except (KeyboardInterrupt, EOFError):
71 # Ctrl C or D maybe also Ctrl Z under Windows.
72 print "\nAborted by user."
73 return 1
75 # open a connection to the server and retrieve all messages
76 try:
77 server = poplib.POP3(server)
78 except socket.error, message:
79 print "POP server error:", message
80 return 1
81 server.user(user)
82 server.pass_(password)
83 numMessages = len(server.list()[1])
84 for i in range(1, numMessages+1):
85 # retr: returns
86 # [ pop response e.g. '+OK 459 octets',
87 # [ array of message lines ],
88 # number of octets ]
89 lines = server.retr(i)[1]
90 s = cStringIO.StringIO('\n'.join(lines))
91 s.seek(0)
92 handler.handle_Message(Message(s))
93 # delete the message
94 server.dele(i)
96 # quit the server to commit changes.
97 server.quit()
98 return 0
100 def usage(args, message=None):
101 if message is not None:
102 print message
103 print _('Usage: %(program)s <instance home> [source spec]')%{'program': args[0]}
104 print _('''
105 The roundup mail gateway may be called in one of two ways:
106 . with an instance home as the only argument,
107 . with both an instance home and a mail spool file, or
108 . with both an instance home and a pop server account.
110 PIPE:
111 In the first case, the mail gateway reads a single message from the
112 standard input and submits the message to the roundup.mailgw module.
114 UNIX mailbox:
115 In the second case, the gateway reads all messages from the mail spool
116 file and submits each in turn to the roundup.mailgw module. The file is
117 emptied once all messages have been successfully handled. The file is
118 specified as:
119 mailbox /path/to/mailbox
121 POP:
122 In the third case, the gateway reads all messages from the POP server
123 specified and submits each in turn to the roundup.mailgw module. The
124 server is specified as:
125 pop username:password@server
126 The username and password may be omitted:
127 pop username@server
128 pop server
129 are both valid. The username and/or password will be prompted for if
130 not supplied on the command-line.
131 ''')
132 return 1
134 def main(args):
135 '''Handle the arguments to the program and initialise environment.
136 '''
137 # figure the instance home
138 if len(args) > 1:
139 instance_home = args[1]
140 else:
141 instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
142 if not instance_home:
143 return usage(args)
145 # get the instance
146 import roundup.instance
147 instance = roundup.instance.open(instance_home)
149 # get a mail handler
150 db = instance.open('admin')
151 handler = instance.MailGW(instance, db)
153 # if there's no more arguments, read a single message from stdin
154 if len(args) == 2:
155 return do_pipe(handler)
157 # otherwise, figure what sort of mail source to handle
158 if len(args) < 4:
159 return usage(args, _('Error: not enough source specification information'))
160 source, specification = args[2:]
161 if source == 'mailbox':
162 return do_mailbox(handler, specification)
163 elif source == 'pop':
164 m = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)',
165 specification)
166 if m:
167 return do_pop(handler, m.group('server'), m.group('user'),
168 m.group('pass'))
169 return usage(args, _('Error: pop specification not valid'))
171 return usage(args, _('Error: The source must be either "mailbox" or "pop"'))
173 def run():
174 sys.exit(main(sys.argv))
176 # call main
177 if __name__ == '__main__':
178 run()
180 #
181 # $Log: not supported by cvs2svn $
182 # Revision 1.2 2002/01/29 20:07:15 jhermann
183 # Conversion to generated script stubs
184 #
185 # Revision 1.1 2002/01/29 19:53:08 jhermann
186 # Moved scripts from top-level dir to roundup.scripts subpackage
187 #
188 # Revision 1.21 2002/01/11 07:02:29 grubert
189 # put an exception around: do_pop user and password entry to catch ctrl-c/d.
190 #
191 # Revision 1.20 2002/01/07 10:43:48 richard
192 # #500329 ] exception on server not reachable-patch
193 #
194 # Revision 1.19 2002/01/05 02:19:03 richard
195 # i18n'ification
196 #
197 # Revision 1.18 2001/12/13 00:20:01 richard
198 # . Centralised the python version check code, bumped version to 2.1.1 (really
199 # needs to be 2.1.2, but that isn't released yet :)
200 #
201 # Revision 1.17 2001/12/02 05:06:16 richard
202 # . We now use weakrefs in the Classes to keep the database reference, so
203 # the close() method on the database is no longer needed.
204 # I bumped the minimum python requirement up to 2.1 accordingly.
205 # . #487480 ] roundup-server
206 # . #487476 ] INSTALL.txt
207 #
208 # I also cleaned up the change message / post-edit stuff in the cgi client.
209 # There's now a clearly marked "TODO: append the change note" where I believe
210 # the change note should be added there. The "changes" list will obviously
211 # have to be modified to be a dict of the changes, or somesuch.
212 #
213 # More testing needed.
214 #
215 # Revision 1.16 2001/11/30 18:23:55 jhermann
216 # Cleaned up strange import (less pollution, too)
217 #
218 # Revision 1.15 2001/11/30 13:16:37 rochecompaan
219 # Fixed bug. Mail gateway was not using the extended Message class
220 # resulting in failed submissions when mails were processed from a Unix
221 # mailbox
222 #
223 # Revision 1.14 2001/11/13 21:44:44 richard
224 # . re-open the database as the author in mail handling
225 #
226 # Revision 1.13 2001/11/09 01:05:55 richard
227 # Fixed bug #479511 ] mailgw to pop once engelbert gruber tested the POP
228 # gateway.
229 #
230 # Revision 1.12 2001/11/08 05:16:55 richard
231 # Rolled roundup-popgw into roundup-mailgw. Cleaned mailgw up significantly,
232 # tested unix mailbox some more. POP still untested.
233 #
234 # Revision 1.11 2001/11/07 05:32:58 richard
235 # More roundup-mailgw usage help.
236 #
237 # Revision 1.10 2001/11/07 05:30:11 richard
238 # Nicer usage message.
239 #
240 # Revision 1.9 2001/11/07 05:29:26 richard
241 # Modified roundup-mailgw so it can read e-mails from a local mail spool
242 # file. Truncates the spool file after parsing.
243 # Fixed a couple of small bugs introduced in roundup.mailgw when I started
244 # the popgw.
245 #
246 # Revision 1.8 2001/11/01 22:04:37 richard
247 # Started work on supporting a pop3-fetching server
248 # Fixed bugs:
249 # . bug #477104 ] HTML tag error in roundup-server
250 # . bug #477107 ] HTTP header problem
251 #
252 # Revision 1.7 2001/08/07 00:24:42 richard
253 # stupid typo
254 #
255 # Revision 1.6 2001/08/07 00:15:51 richard
256 # Added the copyright/license notice to (nearly) all files at request of
257 # Bizar Software.
258 #
259 # Revision 1.5 2001/08/05 07:44:25 richard
260 # Instances are now opened by a special function that generates a unique
261 # module name for the instances on import time.
262 #
263 # Revision 1.4 2001/08/03 01:28:33 richard
264 # Used the much nicer load_package, pointed out by Steve Majewski.
265 #
266 # Revision 1.3 2001/08/03 00:59:34 richard
267 # Instance import now imports the instance using imp.load_module so that
268 # we can have instance homes of "roundup" or other existing python package
269 # names.
270 #
271 # Revision 1.2 2001/07/29 07:01:39 richard
272 # Added vim command to all source so that we don't get no steenkin' tabs :)
273 #
274 # Revision 1.1 2001/07/23 03:46:48 richard
275 # moving the bin files to facilitate out-of-the-boxness
276 #
277 # Revision 1.1 2001/07/22 11:15:45 richard
278 # More Grande Splite stuff
279 #
280 #
281 # vim: set filetype=python ts=4 sw=4 et si