Code

8e6faac74cdd7af7ffb3797f021e825f66ea8a4e
[roundup.git] / roundup / scripts / roundup_mailgw.py
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.py,v 1.1 2002-01-29 19:53:08 jhermann 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     try:
68         if not user:
69             user = raw_input(_('User: '))
70         if not password:
71             password = getpass.getpass()
72     except (KeyboardInterrupt, EOFError):
73         # Ctrl C or D maybe also Ctrl Z under Windows.
74         print "\nAborted by user."
75         return 1
77     # open a connection to the server and retrieve all messages
78     try:
79         server = poplib.POP3(server)
80     except socket.error, message:
81         print "POP server error:", message
82         return 1
83     server.user(user)
84     server.pass_(password)
85     numMessages = len(server.list()[1])
86     for i in range(1, numMessages+1):
87         # retr: returns 
88         # [ pop response e.g. '+OK 459 octets',
89         #   [ array of message lines ],
90         #   number of octets ]
91         lines = server.retr(i)[1]
92         s = cStringIO.StringIO('\n'.join(lines))
93         s.seek(0)
94         handler.handle_Message(Message(s))
95         # delete the message
96         server.dele(i)
98     # quit the server to commit changes.
99     server.quit()
100     return 0
102 def usage(args, message=None):
103     if message is not None:
104         print message
105     print _('Usage: %(program)s <instance home> [source spec]')%{'program': args[0]}
106     print _('''
107 The roundup mail gateway may be called in one of two ways:
108  . with an instance home as the only argument,
109  . with both an instance home and a mail spool file, or
110  . with both an instance home and a pop server account.
112 PIPE:
113  In the first case, the mail gateway reads a single message from the
114  standard input and submits the message to the roundup.mailgw module.
116 UNIX mailbox:
117  In the second case, the gateway reads all messages from the mail spool
118  file and submits each in turn to the roundup.mailgw module. The file is
119  emptied once all messages have been successfully handled. The file is
120  specified as:
121    mailbox /path/to/mailbox
123 POP:
124  In the third case, the gateway reads all messages from the POP server
125  specified and submits each in turn to the roundup.mailgw module. The
126  server is specified as:
127     pop username:password@server
128  The username and password may be omitted:
129     pop username@server
130     pop server
131  are both valid. The username and/or password will be prompted for if
132  not supplied on the command-line.
133 ''')
134     return 1
136 def main(args):
137     '''Handle the arguments to the program and initialise environment.
138     '''
139     # figure the instance home
140     if len(args) > 1:
141         instance_home = args[1]
142     else:
143         instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
144     if not instance_home:
145         return usage(args)
147     # get the instance
148     import roundup.instance
149     instance = roundup.instance.open(instance_home)
151     # get a mail handler
152     db = instance.open('admin')
153     handler = instance.MailGW(instance, db)
155     # if there's no more arguments, read a single message from stdin
156     if len(args) == 2:
157         return do_pipe(handler)
159     # otherwise, figure what sort of mail source to handle
160     if len(args) < 4:
161         return usage(args, _('Error: not enough source specification information'))
162     source, specification = args[2:]
163     if source == 'mailbox':
164         return do_mailbox(handler, specification)
165     elif source == 'pop':
166         m = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)',
167             specification)
168         if m:
169             return do_pop(handler, m.group('server'), m.group('user'),
170                 m.group('pass'))
171         return usage(args, _('Error: pop specification not valid'))
173     return usage(args, _('Error: The source must be either "mailbox" or "pop"'))
175 # call main
176 if __name__ == '__main__':
177     sys.exit(main(sys.argv))
180 # $Log: not supported by cvs2svn $
181 # Revision 1.21  2002/01/11 07:02:29  grubert
182 # put an exception around: do_pop user and password entry to catch ctrl-c/d.
184 # Revision 1.20  2002/01/07 10:43:48  richard
185 # #500329 ] exception on server not reachable-patch
187 # Revision 1.19  2002/01/05 02:19:03  richard
188 # i18n'ification
190 # Revision 1.18  2001/12/13 00:20:01  richard
191 #  . Centralised the python version check code, bumped version to 2.1.1 (really
192 #    needs to be 2.1.2, but that isn't released yet :)
194 # Revision 1.17  2001/12/02 05:06:16  richard
195 # . We now use weakrefs in the Classes to keep the database reference, so
196 #   the close() method on the database is no longer needed.
197 #   I bumped the minimum python requirement up to 2.1 accordingly.
198 # . #487480 ] roundup-server
199 # . #487476 ] INSTALL.txt
201 # I also cleaned up the change message / post-edit stuff in the cgi client.
202 # There's now a clearly marked "TODO: append the change note" where I believe
203 # the change note should be added there. The "changes" list will obviously
204 # have to be modified to be a dict of the changes, or somesuch.
206 # More testing needed.
208 # Revision 1.16  2001/11/30 18:23:55  jhermann
209 # Cleaned up strange import (less pollution, too)
211 # Revision 1.15  2001/11/30 13:16:37  rochecompaan
212 # Fixed bug. Mail gateway was not using the extended Message class
213 # resulting in failed submissions when mails were processed from a Unix
214 # mailbox
216 # Revision 1.14  2001/11/13 21:44:44  richard
217 #  . re-open the database as the author in mail handling
219 # Revision 1.13  2001/11/09 01:05:55  richard
220 # Fixed bug #479511 ] mailgw to pop once engelbert gruber tested the POP
221 # gateway.
223 # Revision 1.12  2001/11/08 05:16:55  richard
224 # Rolled roundup-popgw into roundup-mailgw. Cleaned mailgw up significantly,
225 # tested unix mailbox some more. POP still untested.
227 # Revision 1.11  2001/11/07 05:32:58  richard
228 # More roundup-mailgw usage help.
230 # Revision 1.10  2001/11/07 05:30:11  richard
231 # Nicer usage message.
233 # Revision 1.9  2001/11/07 05:29:26  richard
234 # Modified roundup-mailgw so it can read e-mails from a local mail spool
235 # file. Truncates the spool file after parsing.
236 # Fixed a couple of small bugs introduced in roundup.mailgw when I started
237 # the popgw.
239 # Revision 1.8  2001/11/01 22:04:37  richard
240 # Started work on supporting a pop3-fetching server
241 # Fixed bugs:
242 #  . bug #477104 ] HTML tag error in roundup-server
243 #  . bug #477107 ] HTTP header problem
245 # Revision 1.7  2001/08/07 00:24:42  richard
246 # stupid typo
248 # Revision 1.6  2001/08/07 00:15:51  richard
249 # Added the copyright/license notice to (nearly) all files at request of
250 # Bizar Software.
252 # Revision 1.5  2001/08/05 07:44:25  richard
253 # Instances are now opened by a special function that generates a unique
254 # module name for the instances on import time.
256 # Revision 1.4  2001/08/03 01:28:33  richard
257 # Used the much nicer load_package, pointed out by Steve Majewski.
259 # Revision 1.3  2001/08/03 00:59:34  richard
260 # Instance import now imports the instance using imp.load_module so that
261 # we can have instance homes of "roundup" or other existing python package
262 # names.
264 # Revision 1.2  2001/07/29 07:01:39  richard
265 # Added vim command to all source so that we don't get no steenkin' tabs :)
267 # Revision 1.1  2001/07/23 03:46:48  richard
268 # moving the bin files to facilitate out-of-the-boxness
270 # Revision 1.1  2001/07/22 11:15:45  richard
271 # More Grande Splite stuff
274 # vim: set filetype=python ts=4 sw=4 et si