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