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.13 2001-11-09 01:05:55 richard Exp $
21 import sys, os, re, cStringIO
22 if int(sys.version[0]) < 2:
23 print "Roundup requires Python 2.0 or newer."
24 sys.exit(1)
26 from mimetools import Message
28 def do_pipe(handler):
29 '''Read a message from standard input and pass it to the mail handler.
30 '''
31 handler.main(sys.stdin)
32 return 0
34 def do_mailbox(handler, filename):
35 '''Read a series of messages from the specified unix mailbox file and
36 pass each to the mail handler.
37 '''
38 # open the spool file and lock it
39 import fcntl, FCNTL
40 f = open(filename, 'r+')
41 fcntl.flock(f.fileno(), FCNTL.LOCK_EX)
43 # handle and clear the mailbox
44 try:
45 from mailbox import UnixMailbox
46 mailbox = UnixMailbox(f, factory=Message)
47 # grab one message
48 message = mailbox.next()
49 while message:
50 # call the instance mail handler
51 handler.handle_Message(message)
52 message = mailbox.next()
53 # nuke the file contents
54 os.ftruncate(f.fileno(), 0)
55 except:
56 import traceback
57 traceback.print_exc()
58 return 1
59 fcntl.flock(f.fileno(), FCNTL.LOCK_UN)
60 return 0
62 def do_pop(handler, server, user='', password=''):
63 '''Read a series of messages from the specified POP server.
64 '''
65 import getpass, poplib
66 if not user:
67 user = raw_input('User: ')
68 if not password:
69 password = getpass.getpass()
71 # open a connection to the server and retrieve all messages
72 server = poplib.POP3(server)
73 server.user(user)
74 server.pass_(password)
75 numMessages = len(server.list()[1])
76 for i in range(1, numMessages+1):
77 # retr: returns
78 # [ pop response e.g. '+OK 459 octets',
79 # [ array of message lines ],
80 # number of octets ]
81 lines = server.retr(i)[1]
82 s = cStringIO.StringIO('\n'.join(lines))
83 s.seek(0)
84 handler.handle_Message(Message(s))
85 # delete the message
86 server.dele(i)
88 # quit the server to commit changes.
89 server.quit()
90 return 0
92 def usage(args, message=None):
93 if message is not None:
94 print message
95 print 'Usage: %s <instance home> [source spec]'%args[0]
96 print '''
97 The roundup mail gateway may be called in one of two ways:
98 . with an instance home as the only argument,
99 . with both an instance home and a mail spool file, or
100 . with both an instance home and a pop server account.
102 PIPE:
103 In the first case, the mail gateway reads a single message from the
104 standard input and submits the message to the roundup.mailgw module.
106 UNIX mailbox:
107 In the second case, the gateway reads all messages from the mail spool
108 file and submits each in turn to the roundup.mailgw module. The file is
109 emptied once all messages have been successfully handled. The file is
110 specified as:
111 mailbox /path/to/mailbox
113 POP:
114 In the third case, the gateway reads all messages from the POP server
115 specified and submits each in turn to the roundup.mailgw module. The
116 server is specified as:
117 pop username:password@server
118 The username and password may be omitted:
119 pop username@server
120 pop server
121 are both valid. The username and/or password will be prompted for if
122 not supplied on the command-line.
123 '''
124 return 1
126 def main(args):
127 '''Handle the arguments to the program and initialise environment.
128 '''
129 # figure the instance home
130 if len(args) > 1:
131 instance_home = args[1]
132 else:
133 instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
134 if not instance_home:
135 return usage(args)
137 # get the instance
138 import roundup.instance
139 instance = roundup.instance.open(instance_home)
141 # get a mail handler
142 db = instance.open('admin')
143 handler = instance.MailGW(db)
145 # if there's no more arguments, read a single message from stdin
146 if len(args) == 2:
147 return do_pipe(handler)
149 # otherwise, figure what sort of mail source to handle
150 if len(args) < 4:
151 return usage(args, 'Error: not enough source specification information')
152 source, specification = args[2:]
153 if source == 'mailbox':
154 return do_mailbox(handler, specification)
155 elif source == 'pop':
156 m = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)',
157 specification)
158 if m:
159 return do_pop(handler, m.group('server'), m.group('user'),
160 m.group('pass'))
161 return usage(args, 'Error: pop specification not valid')
163 return usage(args, 'Error: The source must be either "mailbox" or "pop"')
165 # call main
166 if __name__ == '__main__':
167 sys.exit(main(sys.argv))
169 #
170 # $Log: not supported by cvs2svn $
171 # Revision 1.12 2001/11/08 05:16:55 richard
172 # Rolled roundup-popgw into roundup-mailgw. Cleaned mailgw up significantly,
173 # tested unix mailbox some more. POP still untested.
174 #
175 # Revision 1.11 2001/11/07 05:32:58 richard
176 # More roundup-mailgw usage help.
177 #
178 # Revision 1.10 2001/11/07 05:30:11 richard
179 # Nicer usage message.
180 #
181 # Revision 1.9 2001/11/07 05:29:26 richard
182 # Modified roundup-mailgw so it can read e-mails from a local mail spool
183 # file. Truncates the spool file after parsing.
184 # Fixed a couple of small bugs introduced in roundup.mailgw when I started
185 # the popgw.
186 #
187 # Revision 1.8 2001/11/01 22:04:37 richard
188 # Started work on supporting a pop3-fetching server
189 # Fixed bugs:
190 # . bug #477104 ] HTML tag error in roundup-server
191 # . bug #477107 ] HTTP header problem
192 #
193 # Revision 1.7 2001/08/07 00:24:42 richard
194 # stupid typo
195 #
196 # Revision 1.6 2001/08/07 00:15:51 richard
197 # Added the copyright/license notice to (nearly) all files at request of
198 # Bizar Software.
199 #
200 # Revision 1.5 2001/08/05 07:44:25 richard
201 # Instances are now opened by a special function that generates a unique
202 # module name for the instances on import time.
203 #
204 # Revision 1.4 2001/08/03 01:28:33 richard
205 # Used the much nicer load_package, pointed out by Steve Majewski.
206 #
207 # Revision 1.3 2001/08/03 00:59:34 richard
208 # Instance import now imports the instance using imp.load_module so that
209 # we can have instance homes of "roundup" or other existing python package
210 # names.
211 #
212 # Revision 1.2 2001/07/29 07:01:39 richard
213 # Added vim command to all source so that we don't get no steenkin' tabs :)
214 #
215 # Revision 1.1 2001/07/23 03:46:48 richard
216 # moving the bin files to facilitate out-of-the-boxness
217 #
218 # Revision 1.1 2001/07/22 11:15:45 richard
219 # More Grande Splite stuff
220 #
221 #
222 # vim: set filetype=python ts=4 sw=4 et si