Code

better traversal error info
[roundup.git] / roundup / scripts / roundup_mailgw.py
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.4 2002-09-10 01:07:06 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 # vim: set filetype=python ts=4 sw=4 et si