Code

Bugs fixed:
[roundup.git] / roundup-mailgw
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.18 2001-12-13 00:20:01 richard Exp $
21 # python version check
22 from roundup import version_check
24 import sys, os, re, cStringIO
26 from roundup.mailgw 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(instance, 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))
170 # $Log: not supported by cvs2svn $
171 # Revision 1.17  2001/12/02 05:06:16  richard
172 # . We now use weakrefs in the Classes to keep the database reference, so
173 #   the close() method on the database is no longer needed.
174 #   I bumped the minimum python requirement up to 2.1 accordingly.
175 # . #487480 ] roundup-server
176 # . #487476 ] INSTALL.txt
178 # I also cleaned up the change message / post-edit stuff in the cgi client.
179 # There's now a clearly marked "TODO: append the change note" where I believe
180 # the change note should be added there. The "changes" list will obviously
181 # have to be modified to be a dict of the changes, or somesuch.
183 # More testing needed.
185 # Revision 1.16  2001/11/30 18:23:55  jhermann
186 # Cleaned up strange import (less pollution, too)
188 # Revision 1.15  2001/11/30 13:16:37  rochecompaan
189 # Fixed bug. Mail gateway was not using the extended Message class
190 # resulting in failed submissions when mails were processed from a Unix
191 # mailbox
193 # Revision 1.14  2001/11/13 21:44:44  richard
194 #  . re-open the database as the author in mail handling
196 # Revision 1.13  2001/11/09 01:05:55  richard
197 # Fixed bug #479511 ] mailgw to pop once engelbert gruber tested the POP
198 # gateway.
200 # Revision 1.12  2001/11/08 05:16:55  richard
201 # Rolled roundup-popgw into roundup-mailgw. Cleaned mailgw up significantly,
202 # tested unix mailbox some more. POP still untested.
204 # Revision 1.11  2001/11/07 05:32:58  richard
205 # More roundup-mailgw usage help.
207 # Revision 1.10  2001/11/07 05:30:11  richard
208 # Nicer usage message.
210 # Revision 1.9  2001/11/07 05:29:26  richard
211 # Modified roundup-mailgw so it can read e-mails from a local mail spool
212 # file. Truncates the spool file after parsing.
213 # Fixed a couple of small bugs introduced in roundup.mailgw when I started
214 # the popgw.
216 # Revision 1.8  2001/11/01 22:04:37  richard
217 # Started work on supporting a pop3-fetching server
218 # Fixed bugs:
219 #  . bug #477104 ] HTML tag error in roundup-server
220 #  . bug #477107 ] HTTP header problem
222 # Revision 1.7  2001/08/07 00:24:42  richard
223 # stupid typo
225 # Revision 1.6  2001/08/07 00:15:51  richard
226 # Added the copyright/license notice to (nearly) all files at request of
227 # Bizar Software.
229 # Revision 1.5  2001/08/05 07:44:25  richard
230 # Instances are now opened by a special function that generates a unique
231 # module name for the instances on import time.
233 # Revision 1.4  2001/08/03 01:28:33  richard
234 # Used the much nicer load_package, pointed out by Steve Majewski.
236 # Revision 1.3  2001/08/03 00:59:34  richard
237 # Instance import now imports the instance using imp.load_module so that
238 # we can have instance homes of "roundup" or other existing python package
239 # names.
241 # Revision 1.2  2001/07/29 07:01:39  richard
242 # Added vim command to all source so that we don't get no steenkin' tabs :)
244 # Revision 1.1  2001/07/23 03:46:48  richard
245 # moving the bin files to facilitate out-of-the-boxness
247 # Revision 1.1  2001/07/22 11:15:45  richard
248 # More Grande Splite stuff
251 # vim: set filetype=python ts=4 sw=4 et si